Merge pull request #1281 from maraoz/refactor/settings
Refactor settings
This commit is contained in:
commit
d358330d1d
40 changed files with 697 additions and 655 deletions
41
config.js
41
config.js
|
|
@ -6,11 +6,6 @@ var defaultConfig = {
|
||||||
forceNetwork: false,
|
forceNetwork: false,
|
||||||
logLevel: 'info',
|
logLevel: 'info',
|
||||||
|
|
||||||
// DEFAULT unit: Bit
|
|
||||||
unitName: 'bits',
|
|
||||||
unitToSatoshi: 100,
|
|
||||||
alternativeName: 'US Dollar',
|
|
||||||
alternativeIsoCode: 'USD',
|
|
||||||
|
|
||||||
// wallet limits
|
// wallet limits
|
||||||
limits: {
|
limits: {
|
||||||
|
|
@ -20,9 +15,12 @@ var defaultConfig = {
|
||||||
|
|
||||||
// network layer config
|
// network layer config
|
||||||
network: {
|
network: {
|
||||||
host: 'test-insight.bitpay.com',
|
testnet: {
|
||||||
port: 443,
|
url: 'https://test-insight.bitpay.com:443'
|
||||||
schema: 'https'
|
},
|
||||||
|
livenet: {
|
||||||
|
url: 'https://insight.bitpay.com:443'
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// wallet default config
|
// wallet default config
|
||||||
|
|
@ -30,25 +28,15 @@ var defaultConfig = {
|
||||||
requiredCopayers: 2,
|
requiredCopayers: 2,
|
||||||
totalCopayers: 3,
|
totalCopayers: 3,
|
||||||
spendUnconfirmed: true,
|
spendUnconfirmed: true,
|
||||||
verbose: 1,
|
|
||||||
// will duplicate itself after each try
|
|
||||||
reconnectDelay: 5000,
|
reconnectDelay: 5000,
|
||||||
idleDurationMin: 4
|
idleDurationMin: 4,
|
||||||
},
|
settings: {
|
||||||
|
unitName: 'bits',
|
||||||
// blockchain service API config
|
unitToSatoshi: 100,
|
||||||
blockchain: {
|
unitDecimals: 2,
|
||||||
schema: 'https',
|
alternativeName: 'US Dollar',
|
||||||
host: 'test-insight.bitpay.com',
|
alternativeIsoCode: 'USD',
|
||||||
port: 443,
|
}
|
||||||
retryDelay: 1000,
|
|
||||||
},
|
|
||||||
// socket service API config
|
|
||||||
socket: {
|
|
||||||
schema: 'https',
|
|
||||||
host: 'test-insight.bitpay.com',
|
|
||||||
port: 443,
|
|
||||||
reconnectDelay: 1000,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// local encryption/security config
|
// local encryption/security config
|
||||||
|
|
@ -62,7 +50,6 @@ var defaultConfig = {
|
||||||
updateFrequencySeconds: 60 * 60
|
updateFrequencySeconds: 60 * 60
|
||||||
},
|
},
|
||||||
|
|
||||||
verbose: 1,
|
|
||||||
};
|
};
|
||||||
if (typeof module !== 'undefined')
|
if (typeof module !== 'undefined')
|
||||||
module.exports = defaultConfig;
|
module.exports = defaultConfig;
|
||||||
|
|
@ -17,8 +17,8 @@
|
||||||
<i class="fi-loop icon-rotate m15r"></i>
|
<i class="fi-loop icon-rotate m15r"></i>
|
||||||
<span translate> Network Error. Attempting to reconnect...</span>
|
<span translate> Network Error. Attempting to reconnect...</span>
|
||||||
</span>
|
</span>
|
||||||
<nav class="tab-bar" ng-class="{'hide-tab-bar' : !$root.wallet ||
|
<nav class="tab-bar" ng-if="$root.wallet &&
|
||||||
!$root.wallet.isReady() || $root.wallet.isLocked}">
|
$root.wallet.isReady() && !$root.wallet.isLocked">
|
||||||
<section class="left-small">
|
<section class="left-small">
|
||||||
<a class="left-off-canvas-toggle menu-icon" ><span></span></a>
|
<a class="left-off-canvas-toggle menu-icon" ><span></span></a>
|
||||||
</section>
|
</section>
|
||||||
|
|
@ -28,7 +28,7 @@
|
||||||
|
|
||||||
<section class="middle tab-bar-section">
|
<section class="middle tab-bar-section">
|
||||||
<h1 class="right">
|
<h1 class="right">
|
||||||
{{totalBalance || 0 |noFractionNumber}} {{$root.unitName}}
|
{{totalBalance || 0 |noFractionNumber}} {{$root.wallet.settings.unitName}}
|
||||||
</h1>
|
</h1>
|
||||||
<h1 class="title ellipsis">
|
<h1 class="title ellipsis">
|
||||||
{{$root.wallet.getName()}}
|
{{$root.wallet.getName()}}
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ angular.module('copayApp.controllers').controller('CreateController',
|
||||||
$scope.walletPassword = $rootScope.walletPassword;
|
$scope.walletPassword = $rootScope.walletPassword;
|
||||||
$scope.isMobile = !!window.cordova;
|
$scope.isMobile = !!window.cordova;
|
||||||
$scope.hideAdv = true;
|
$scope.hideAdv = true;
|
||||||
|
$scope.networkName = config.networkName;
|
||||||
|
|
||||||
// ng-repeat defined number of times instead of repeating over array?
|
// ng-repeat defined number of times instead of repeating over array?
|
||||||
$scope.getNumber = function(num) {
|
$scope.getNumber = function(num) {
|
||||||
|
|
@ -83,6 +84,7 @@ angular.module('copayApp.controllers').controller('CreateController',
|
||||||
nickname: $scope.myNickname,
|
nickname: $scope.myNickname,
|
||||||
passphrase: passphrase,
|
passphrase: passphrase,
|
||||||
privateKeyHex: $scope.private,
|
privateKeyHex: $scope.private,
|
||||||
|
networkName: $scope.networkName,
|
||||||
};
|
};
|
||||||
var w = walletFactory.create(opts);
|
var w = walletFactory.create(opts);
|
||||||
controllerUtils.startNetwork(w, $scope);
|
controllerUtils.startNetwork(w, $scope);
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,6 @@ angular.module('copayApp.controllers').controller('JoinController',
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.loading = true;
|
$scope.loading = true;
|
||||||
walletFactory.network.on('badSecret', function() {});
|
|
||||||
|
|
||||||
Passphrase.getBase64Async($scope.joinPassword, function(passphrase) {
|
Passphrase.getBase64Async($scope.joinPassword, function(passphrase) {
|
||||||
walletFactory.joinCreateSession($scope.connectionId, $scope.nickname, passphrase, $scope.private, function(err, w) {
|
walletFactory.joinCreateSession($scope.connectionId, $scope.nickname, passphrase, $scope.private, function(err, w) {
|
||||||
|
|
@ -129,7 +128,7 @@ angular.module('copayApp.controllers').controller('JoinController',
|
||||||
else if (err === 'walletFull')
|
else if (err === 'walletFull')
|
||||||
notification.error('The wallet is full');
|
notification.error('The wallet is full');
|
||||||
else if (err === 'badNetwork')
|
else if (err === 'badNetwork')
|
||||||
notification.error('Network Error', 'The wallet your are trying to join uses a different Bitcoin Network. Check your settings.');
|
notification.error('Network Error', 'Wallet network configuration missmatch');
|
||||||
else if (err === 'badSecret')
|
else if (err === 'badSecret')
|
||||||
notification.error('Bad secret', 'The secret string you entered is invalid');
|
notification.error('Bad secret', 'The secret string you entered is invalid');
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -1,45 +1,97 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
angular.module('copayApp.controllers').controller('MoreController',
|
angular.module('copayApp.controllers').controller('MoreController',
|
||||||
function($scope, $rootScope, $location, backupService, walletFactory, controllerUtils, notification) {
|
function($scope, $rootScope, $location, backupService, walletFactory, controllerUtils, notification, rateService) {
|
||||||
var w = $rootScope.wallet;
|
var w = $rootScope.wallet;
|
||||||
|
|
||||||
$scope.hideAdv=true;
|
$scope.unitOpts = [{
|
||||||
$scope.hidePriv=true;
|
name: 'Satoshis (100,000,000 satoshis = 1BTC)',
|
||||||
|
shortName: 'SAT',
|
||||||
|
value: 1,
|
||||||
|
decimals: 0
|
||||||
|
}, {
|
||||||
|
name: 'bits (1,000,000 bits = 1BTC)',
|
||||||
|
shortName: 'bits',
|
||||||
|
value: 100,
|
||||||
|
decimals: 2
|
||||||
|
}, {
|
||||||
|
name: 'mBTC (1,000 mBTC = 1BTC)',
|
||||||
|
shortName: 'mBTC',
|
||||||
|
value: 100000,
|
||||||
|
decimals: 5
|
||||||
|
}, {
|
||||||
|
name: 'BTC',
|
||||||
|
shortName: 'BTC',
|
||||||
|
value: 100000000,
|
||||||
|
decimals: 8
|
||||||
|
}];
|
||||||
|
$scope.selectedAlternative = {
|
||||||
|
name: w.settings.alternativeName,
|
||||||
|
isoCode: w.settings.alternativeIsoCode
|
||||||
|
};
|
||||||
|
$scope.alternativeOpts = rateService.isAvailable ?
|
||||||
|
rateService.listAlternatives() : [$scope.selectedAlternative];
|
||||||
|
|
||||||
|
rateService.whenAvailable(function() {
|
||||||
|
$scope.alternativeOpts = rateService.listAlternatives();
|
||||||
|
for (var ii in $scope.alternativeOpts) {
|
||||||
|
if (w.settings.alternativeIsoCode === $scope.alternativeOpts[ii].isoCode) {
|
||||||
|
$scope.selectedAlternative = $scope.alternativeOpts[ii];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
for (var ii in $scope.unitOpts) {
|
||||||
|
if (w.settings.unitName === $scope.unitOpts[ii].shortName) {
|
||||||
|
$scope.selectedUnit = $scope.unitOpts[ii];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$scope.save = function() {
|
||||||
|
w.changeSettings({
|
||||||
|
unitName: $scope.selectedUnit.shortName,
|
||||||
|
unitToSatoshi: $scope.selectedUnit.value,
|
||||||
|
unitDecimals: $scope.selectedUnit.decimals,
|
||||||
|
alternativeName: $scope.selectedAlternative.name,
|
||||||
|
alternativeIsoCode: $scope.selectedAlternative.isoCode,
|
||||||
|
});
|
||||||
|
controllerUtils.updateBalance();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
$scope.hideAdv = true;
|
||||||
|
$scope.hidePriv = true;
|
||||||
if (w)
|
if (w)
|
||||||
$scope.priv = w.privateKey.toObj().extendedPrivateKeyString;
|
$scope.priv = w.privateKey.toObj().extendedPrivateKeyString;
|
||||||
|
|
||||||
$scope.downloadBackup = function() {
|
$scope.downloadBackup = function() {
|
||||||
var w = $rootScope.wallet;
|
|
||||||
backupService.download(w);
|
backupService.download(w);
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.deleteWallet = function() {
|
$scope.deleteWallet = function() {
|
||||||
var w = $rootScope.wallet;
|
|
||||||
walletFactory.delete(w.id, function() {
|
walletFactory.delete(w.id, function() {
|
||||||
controllerUtils.logout();
|
controllerUtils.logout();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.purge = function(deleteAll) {
|
$scope.purge = function(deleteAll) {
|
||||||
var w = $rootScope.wallet;
|
|
||||||
var removed = w.purgeTxProposals(deleteAll);
|
var removed = w.purgeTxProposals(deleteAll);
|
||||||
if (removed){
|
if (removed) {
|
||||||
controllerUtils.updateBalance();
|
controllerUtils.updateBalance();
|
||||||
}
|
}
|
||||||
notification.info('Tx Proposals Purged', removed + ' transaction proposal purged');
|
notification.info('Tx Proposals Purged', removed + ' transaction proposal purged');
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.updateIndexes = function() {
|
$scope.updateIndexes = function() {
|
||||||
var w = $rootScope.wallet;
|
notification.info('Scaning for transactions', 'Using derived addresses from your wallet');
|
||||||
notification.info('Scaning for transactions','Using derived addresses from your wallet');
|
|
||||||
w.updateIndexes(function(err) {
|
w.updateIndexes(function(err) {
|
||||||
notification.info('Scan Ended', 'Updating balance');
|
notification.info('Scan Ended', 'Updating balance');
|
||||||
if (err) {
|
if (err) {
|
||||||
notification.error('Error', 'Error updating indexes: ' + err);
|
notification.error('Error', 'Error updating indexes: ' + err);
|
||||||
}
|
}
|
||||||
controllerUtils.updateAddressList();
|
controllerUtils.updateAddressList();
|
||||||
controllerUtils.updateBalance(function(){
|
controllerUtils.updateBalance(function() {
|
||||||
notification.info('Finished', 'The balance is updated using the derived addresses');
|
notification.info('Finished', 'The balance is updated using the derived addresses');
|
||||||
w.sendIndexes();
|
w.sendIndexes();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,22 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
var bitcore = require('bitcore');
|
var bitcore = require('bitcore');
|
||||||
|
var preconditions = require('preconditions').singleton();
|
||||||
|
|
||||||
angular.module('copayApp.controllers').controller('SendController',
|
angular.module('copayApp.controllers').controller('SendController',
|
||||||
function($scope, $rootScope, $window, $timeout, $anchorScroll, $modal, isMobile, notification, controllerUtils, rateService) {
|
function($scope, $rootScope, $window, $timeout, $anchorScroll, $modal, isMobile, notification, controllerUtils, rateService) {
|
||||||
|
var w = $rootScope.wallet;
|
||||||
|
preconditions.checkState(w);
|
||||||
|
preconditions.checkState(w.settings.unitToSatoshi);
|
||||||
|
|
||||||
$scope.title = 'Send';
|
$scope.title = 'Send';
|
||||||
$scope.loading = false;
|
$scope.loading = false;
|
||||||
var satToUnit = 1 / config.unitToSatoshi;
|
var satToUnit = 1 / w.settings.unitToSatoshi;
|
||||||
$scope.defaultFee = bitcore.TransactionBuilder.FEE_PER_1000B_SAT * satToUnit;
|
$scope.defaultFee = bitcore.TransactionBuilder.FEE_PER_1000B_SAT * satToUnit;
|
||||||
$scope.unitToBtc = config.unitToSatoshi / bitcore.util.COIN;
|
$scope.unitToBtc = w.settings.unitToSatoshi / bitcore.util.COIN;
|
||||||
$scope.unitToSatoshi = config.unitToSatoshi;
|
$scope.unitToSatoshi = w.settings.unitToSatoshi;
|
||||||
|
|
||||||
$scope.alternativeName = config.alternativeName;
|
$scope.alternativeName = w.settings.alternativeName;
|
||||||
$scope.alternativeIsoCode = config.alternativeIsoCode;
|
$scope.alternativeIsoCode = w.settings.alternativeIsoCode;
|
||||||
|
|
||||||
$scope.isRateAvailable = false;
|
$scope.isRateAvailable = false;
|
||||||
$scope.rateService = rateService;
|
$scope.rateService = rateService;
|
||||||
|
|
@ -36,7 +41,7 @@ angular.module('copayApp.controllers').controller('SendController',
|
||||||
this._alternative = newValue;
|
this._alternative = newValue;
|
||||||
if (typeof(newValue) === 'number' && $scope.isRateAvailable) {
|
if (typeof(newValue) === 'number' && $scope.isRateAvailable) {
|
||||||
this._amount = parseFloat(
|
this._amount = parseFloat(
|
||||||
(rateService.fromFiat(newValue, config.alternativeIsoCode) * satToUnit).toFixed(config.unitDecimals), 10);
|
(rateService.fromFiat(newValue, w.settings.alternativeIsoCode) * satToUnit).toFixed(w.settings.unitDecimals), 10);
|
||||||
} else {
|
} else {
|
||||||
this._amount = 0;
|
this._amount = 0;
|
||||||
}
|
}
|
||||||
|
|
@ -53,7 +58,7 @@ angular.module('copayApp.controllers').controller('SendController',
|
||||||
this._amount = newValue;
|
this._amount = newValue;
|
||||||
if (typeof(newValue) === 'number' && $scope.isRateAvailable) {
|
if (typeof(newValue) === 'number' && $scope.isRateAvailable) {
|
||||||
this._alternative = parseFloat(
|
this._alternative = parseFloat(
|
||||||
(rateService.toFiat(newValue * config.unitToSatoshi, config.alternativeIsoCode)).toFixed(2), 10);
|
(rateService.toFiat(newValue * w.settings.unitToSatoshi, w.settings.alternativeIsoCode)).toFixed(2), 10);
|
||||||
} else {
|
} else {
|
||||||
this._alternative = 0;
|
this._alternative = 0;
|
||||||
}
|
}
|
||||||
|
|
@ -75,7 +80,6 @@ angular.module('copayApp.controllers').controller('SendController',
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.showAddressBook = function() {
|
$scope.showAddressBook = function() {
|
||||||
var w = $rootScope.wallet;
|
|
||||||
var flag;
|
var flag;
|
||||||
if (w) {
|
if (w) {
|
||||||
for (var k in w.addressBook) {
|
for (var k in w.addressBook) {
|
||||||
|
|
@ -91,7 +95,7 @@ angular.module('copayApp.controllers').controller('SendController',
|
||||||
if ($rootScope.pendingPayment) {
|
if ($rootScope.pendingPayment) {
|
||||||
var pp = $rootScope.pendingPayment;
|
var pp = $rootScope.pendingPayment;
|
||||||
$scope.address = pp.address + '';
|
$scope.address = pp.address + '';
|
||||||
var amount = pp.data.amount / config.unitToSatoshi * 100000000;
|
var amount = pp.data.amount / w.settings.unitToSatoshi * 100000000;
|
||||||
$scope.amount = amount;
|
$scope.amount = amount;
|
||||||
$scope.commentText = pp.data.message;
|
$scope.commentText = pp.data.message;
|
||||||
}
|
}
|
||||||
|
|
@ -113,11 +117,9 @@ angular.module('copayApp.controllers').controller('SendController',
|
||||||
$scope.loading = true;
|
$scope.loading = true;
|
||||||
|
|
||||||
var address = form.address.$modelValue;
|
var address = form.address.$modelValue;
|
||||||
var amount = parseInt((form.amount.$modelValue * config.unitToSatoshi).toFixed(0));
|
var amount = parseInt((form.amount.$modelValue * w.settings.unitToSatoshi).toFixed(0));
|
||||||
var commentText = form.comment.$modelValue;
|
var commentText = form.comment.$modelValue;
|
||||||
|
|
||||||
var w = $rootScope.wallet;
|
|
||||||
|
|
||||||
function done(err, ntxid, merchantData) {
|
function done(err, ntxid, merchantData) {
|
||||||
if (err) {
|
if (err) {
|
||||||
var message = 'The transaction' + (w.isShared() ? ' proposal' : '') + ' could not be created';
|
var message = 'The transaction' + (w.isShared() ? ' proposal' : '') + ' could not be created';
|
||||||
|
|
@ -344,7 +346,6 @@ angular.module('copayApp.controllers').controller('SendController',
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.toggleAddressBookEntry = function(key) {
|
$scope.toggleAddressBookEntry = function(key) {
|
||||||
var w = $rootScope.wallet;
|
|
||||||
w.toggleAddressBookEntry(key);
|
w.toggleAddressBookEntry(key);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -379,7 +380,6 @@ angular.module('copayApp.controllers').controller('SendController',
|
||||||
});
|
});
|
||||||
|
|
||||||
modalInstance.result.then(function(entry) {
|
modalInstance.result.then(function(entry) {
|
||||||
var w = $rootScope.wallet;
|
|
||||||
|
|
||||||
$timeout(function() {
|
$timeout(function() {
|
||||||
$scope.loading = false;
|
$scope.loading = false;
|
||||||
|
|
@ -403,7 +403,7 @@ angular.module('copayApp.controllers').controller('SendController',
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.getAvailableAmount = function() {
|
$scope.getAvailableAmount = function() {
|
||||||
var amount = ((($rootScope.availableBalance * config.unitToSatoshi).toFixed(0) - bitcore.TransactionBuilder.FEE_PER_1000B_SAT) / config.unitToSatoshi);
|
var amount = ((($rootScope.availableBalance * w.settings.unitToSatoshi).toFixed(0) - bitcore.TransactionBuilder.FEE_PER_1000B_SAT) / w.settings.unitToSatoshi);
|
||||||
return amount > 0 ? amount : 0;
|
return amount > 0 ? amount : 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -416,7 +416,6 @@ angular.module('copayApp.controllers').controller('SendController',
|
||||||
$scope.send = function(ntxid, cb) {
|
$scope.send = function(ntxid, cb) {
|
||||||
$scope.loading = true;
|
$scope.loading = true;
|
||||||
$rootScope.txAlertCount = 0;
|
$rootScope.txAlertCount = 0;
|
||||||
var w = $rootScope.wallet;
|
|
||||||
w.sendTx(ntxid, function(txid, merchantData) {
|
w.sendTx(ntxid, function(txid, merchantData) {
|
||||||
if (!txid) {
|
if (!txid) {
|
||||||
notification.error('Error', 'There was an error sending the transaction');
|
notification.error('Error', 'There was an error sending the transaction');
|
||||||
|
|
@ -441,7 +440,6 @@ angular.module('copayApp.controllers').controller('SendController',
|
||||||
|
|
||||||
$scope.sign = function(ntxid) {
|
$scope.sign = function(ntxid) {
|
||||||
$scope.loading = true;
|
$scope.loading = true;
|
||||||
var w = $rootScope.wallet;
|
|
||||||
w.sign(ntxid, function(ret) {
|
w.sign(ntxid, function(ret) {
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
notification.error('Error', 'There was an error signing the transaction');
|
notification.error('Error', 'There was an error signing the transaction');
|
||||||
|
|
@ -461,7 +459,6 @@ angular.module('copayApp.controllers').controller('SendController',
|
||||||
$scope.reject = function(ntxid) {
|
$scope.reject = function(ntxid) {
|
||||||
$scope.loading = true;
|
$scope.loading = true;
|
||||||
$rootScope.txAlertCount = 0;
|
$rootScope.txAlertCount = 0;
|
||||||
var w = $rootScope.wallet;
|
|
||||||
w.reject(ntxid);
|
w.reject(ntxid);
|
||||||
notification.warning('Transaction rejected', 'You rejected the transaction successfully');
|
notification.warning('Transaction rejected', 'You rejected the transaction successfully');
|
||||||
$scope.loading = false;
|
$scope.loading = false;
|
||||||
|
|
@ -497,7 +494,7 @@ angular.module('copayApp.controllers').controller('SendController',
|
||||||
// Payment Protocol URI (BIP-72)
|
// Payment Protocol URI (BIP-72)
|
||||||
scope.wallet.fetchPaymentTx(uri.merchant, function(err, merchantData) {
|
scope.wallet.fetchPaymentTx(uri.merchant, function(err, merchantData) {
|
||||||
var balance = $rootScope.availableBalance;
|
var balance = $rootScope.availableBalance;
|
||||||
var available = +(balance * config.unitToSatoshi).toFixed(0);
|
var available = +(balance * w.settings.unitToSatoshi).toFixed(0);
|
||||||
|
|
||||||
if (merchantData && available < +merchantData.total) {
|
if (merchantData && available < +merchantData.total) {
|
||||||
err = new Error('No unspent outputs available.');
|
err = new Error('No unspent outputs available.');
|
||||||
|
|
@ -508,7 +505,7 @@ angular.module('copayApp.controllers').controller('SendController',
|
||||||
scope.sendForm.address.$isValid = false;
|
scope.sendForm.address.$isValid = false;
|
||||||
|
|
||||||
if (err.amount) {
|
if (err.amount) {
|
||||||
scope.sendForm.amount.$setViewValue(+err.amount / config.unitToSatoshi);
|
scope.sendForm.amount.$setViewValue(+err.amount / w.settings.unitToSatoshi);
|
||||||
scope.sendForm.amount.$render();
|
scope.sendForm.amount.$render();
|
||||||
scope.sendForm.amount.$isValid = false;
|
scope.sendForm.amount.$isValid = false;
|
||||||
scope.notEnoughAmount = true;
|
scope.notEnoughAmount = true;
|
||||||
|
|
@ -538,7 +535,7 @@ angular.module('copayApp.controllers').controller('SendController',
|
||||||
var url = merchantData.request_url;
|
var url = merchantData.request_url;
|
||||||
var domain = /^(?:https?)?:\/\/([^\/:]+).*$/.exec(url)[1];
|
var domain = /^(?:https?)?:\/\/([^\/:]+).*$/.exec(url)[1];
|
||||||
|
|
||||||
merchantData.unitTotal = (+merchantData.total / config.unitToSatoshi) + '';
|
merchantData.unitTotal = (+merchantData.total / w.settings.unitToSatoshi) + '';
|
||||||
merchantData.expiration = new Date(
|
merchantData.expiration = new Date(
|
||||||
merchantData.pr.pd.expires * 1000).toISOString();
|
merchantData.pr.pd.expires * 1000).toISOString();
|
||||||
merchantData.domain = domain;
|
merchantData.domain = domain;
|
||||||
|
|
@ -587,7 +584,9 @@ angular.module('copayApp.controllers').controller('SendController',
|
||||||
}
|
}
|
||||||
|
|
||||||
notification.info('Payment Request',
|
notification.info('Payment Request',
|
||||||
'Server is requesting ' + merchantData.unitTotal + ' ' + config.unitName + '.' + ' Message: ' + merchantData.pr.pd.memo);
|
'Server is requesting ' + merchantData.unitTotal +
|
||||||
|
' ' + w.settings.unitName +
|
||||||
|
'.' + ' Message: ' + merchantData.pr.pd.memo);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,12 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
angular.module('copayApp.controllers').controller('SettingsController', function($scope, $rootScope, $window, $location, controllerUtils, rateService) {
|
angular.module('copayApp.controllers').controller('SettingsController', function($scope, $rootScope, $window, $location, controllerUtils) {
|
||||||
|
|
||||||
controllerUtils.redirIfLogged();
|
controllerUtils.redirIfLogged();
|
||||||
$scope.title = 'Settings';
|
$scope.title = 'Settings';
|
||||||
$scope.networkName = config.networkName;
|
|
||||||
$scope.insightHost = config.blockchain.host;
|
|
||||||
$scope.insightPort = config.blockchain.port;
|
|
||||||
$scope.insightSecure = config.blockchain.schema === 'https';
|
|
||||||
$scope.forceNetwork = config.forceNetwork;
|
|
||||||
$scope.defaultLanguage = config.defaultLanguage || 'en';
|
$scope.defaultLanguage = config.defaultLanguage || 'en';
|
||||||
|
$scope.insightLivenet = config.network.livenet.url;
|
||||||
|
$scope.insightTestnet = config.network.testnet.url;
|
||||||
|
|
||||||
$scope.availableLanguages = [{
|
$scope.availableLanguages = [{
|
||||||
name: 'English',
|
name: 'English',
|
||||||
|
|
@ -26,86 +23,18 @@ angular.module('copayApp.controllers').controller('SettingsController', function
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.unitOpts = [{
|
|
||||||
name: 'Satoshis (100,000,000 satoshis = 1BTC)',
|
|
||||||
shortName: 'SAT',
|
|
||||||
value: 1,
|
|
||||||
decimals: 0
|
|
||||||
}, {
|
|
||||||
name: 'bits (1,000,000 bits = 1BTC)',
|
|
||||||
shortName: 'bits',
|
|
||||||
value: 100,
|
|
||||||
decimals: 2
|
|
||||||
}, {
|
|
||||||
name: 'mBTC (1,000 mBTC = 1BTC)',
|
|
||||||
shortName: 'mBTC',
|
|
||||||
value: 100000,
|
|
||||||
decimals: 5
|
|
||||||
}, {
|
|
||||||
name: 'BTC',
|
|
||||||
shortName: 'BTC',
|
|
||||||
value: 100000000,
|
|
||||||
decimals: 8
|
|
||||||
}];
|
|
||||||
|
|
||||||
$scope.selectedAlternative = {
|
|
||||||
name: config.alternativeName,
|
|
||||||
isoCode: config.alternativeIsoCode
|
|
||||||
};
|
|
||||||
$scope.alternativeOpts = rateService.isAvailable ?
|
|
||||||
rateService.listAlternatives() : [$scope.selectedAlternative];
|
|
||||||
|
|
||||||
rateService.whenAvailable(function() {
|
|
||||||
$scope.alternativeOpts = rateService.listAlternatives();
|
|
||||||
for (var ii in $scope.alternativeOpts) {
|
|
||||||
if (config.alternativeIsoCode === $scope.alternativeOpts[ii].isoCode) {
|
|
||||||
$scope.selectedAlternative = $scope.alternativeOpts[ii];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
for (var ii in $scope.unitOpts) {
|
|
||||||
if (config.unitName === $scope.unitOpts[ii].shortName) {
|
|
||||||
$scope.selectedUnit = $scope.unitOpts[ii];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.changeNetwork = function() {
|
|
||||||
$scope.insightHost = $scope.networkName !== 'testnet' ? 'test-insight.bitpay.com' : 'insight.bitpay.com';
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
$scope.changeInsightSSL = function() {
|
|
||||||
$scope.insightPort = $scope.insightSecure ? 80 : 443;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
$scope.save = function() {
|
$scope.save = function() {
|
||||||
var network = config.network;
|
var insightSettings = {
|
||||||
network.host = $scope.insightHost;
|
livenet: {
|
||||||
network.port = $scope.insightPort;
|
url: $scope.insightLivenet,
|
||||||
network.schema = $scope.insightSecure ? 'https' : 'http';
|
},
|
||||||
|
testnet: {
|
||||||
|
url: $scope.insightTestnet,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
localStorage.setItem('config', JSON.stringify({
|
localStorage.setItem('config', JSON.stringify({
|
||||||
networkName: $scope.networkName,
|
network: insightSettings,
|
||||||
blockchain: {
|
|
||||||
host: $scope.insightHost,
|
|
||||||
port: $scope.insightPort,
|
|
||||||
schema: $scope.insightSecure ? 'https' : 'http',
|
|
||||||
},
|
|
||||||
socket: {
|
|
||||||
host: $scope.insightHost,
|
|
||||||
port: $scope.insightPort,
|
|
||||||
schema: $scope.insightSecure ? 'https' : 'http',
|
|
||||||
},
|
|
||||||
network: network,
|
|
||||||
unitName: $scope.selectedUnit.shortName,
|
|
||||||
unitToSatoshi: $scope.selectedUnit.value,
|
|
||||||
unitDecimals: $scope.selectedUnit.decimals,
|
|
||||||
alternativeName: $scope.selectedAlternative.name,
|
|
||||||
alternativeIsoCode: $scope.selectedAlternative.isoCode,
|
|
||||||
|
|
||||||
version: copay.version,
|
version: copay.version,
|
||||||
defaultLanguage: $scope.selectedLanguage.isoCode
|
defaultLanguage: $scope.selectedLanguage.isoCode
|
||||||
}));
|
}));
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ var bitcore = require('bitcore');
|
||||||
angular.module('copayApp.controllers').controller('TransactionsController',
|
angular.module('copayApp.controllers').controller('TransactionsController',
|
||||||
function($scope, $rootScope, $timeout, controllerUtils, notification) {
|
function($scope, $rootScope, $timeout, controllerUtils, notification) {
|
||||||
|
|
||||||
|
var w = $rootScope.wallet;
|
||||||
|
|
||||||
$scope.title = 'Transactions';
|
$scope.title = 'Transactions';
|
||||||
$scope.loading = false;
|
$scope.loading = false;
|
||||||
$scope.lastShowed = false;
|
$scope.lastShowed = false;
|
||||||
|
|
@ -12,7 +14,7 @@ angular.module('copayApp.controllers').controller('TransactionsController',
|
||||||
$scope.txpItemsPerPage = 4;
|
$scope.txpItemsPerPage = 4;
|
||||||
$scope.blockchain_txs = [];
|
$scope.blockchain_txs = [];
|
||||||
|
|
||||||
var satToUnit = 1 / config.unitToSatoshi;
|
var satToUnit = 1 / w.settings.unitToSatoshi;
|
||||||
|
|
||||||
$scope.update = function() {
|
$scope.update = function() {
|
||||||
$scope.loading = true;
|
$scope.loading = true;
|
||||||
|
|
@ -139,7 +141,7 @@ angular.module('copayApp.controllers').controller('TransactionsController',
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.getShortNetworkName = function() {
|
$scope.getShortNetworkName = function() {
|
||||||
return config.networkName.substring(0, 4);
|
return w.getNetworkName().substring(0, 4);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Autoload transactions on 1-of-1
|
// Autoload transactions on 1-of-1
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,11 @@
|
||||||
angular.module('copayApp.controllers').controller('VersionController',
|
angular.module('copayApp.controllers').controller('VersionController',
|
||||||
function($scope, $rootScope, $http, notification) {
|
function($scope, $rootScope, $http, notification) {
|
||||||
|
|
||||||
|
var w = $rootScope.wallet;
|
||||||
|
|
||||||
$scope.version = copay.version;
|
$scope.version = copay.version;
|
||||||
$scope.commitHash = copay.commitHash;
|
$scope.commitHash = copay.commitHash;
|
||||||
$scope.networkName = config.networkName;
|
$scope.networkName = w ? w.getNetworkName() : '';
|
||||||
$scope.defaultLanguage = config.defaultLanguage;
|
$scope.defaultLanguage = config.defaultLanguage;
|
||||||
if (_.isUndefined($rootScope.checkVersion))
|
if (_.isUndefined($rootScope.checkVersion))
|
||||||
$rootScope.checkVersion = true;
|
$rootScope.checkVersion = true;
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
var bitcore = require('bitcore');
|
||||||
|
var Address = bitcore.Address;
|
||||||
|
var bignum = bitcore.Bignum;
|
||||||
|
var preconditions = require('preconditions').singleton();
|
||||||
|
|
||||||
angular.module('copayApp.directives')
|
angular.module('copayApp.directives')
|
||||||
.directive('validAddress', ['$rootScope', function($rootScope) {
|
.directive('validAddress', ['$rootScope', function($rootScope) {
|
||||||
var bitcore = require('bitcore');
|
|
||||||
var Address = bitcore.Address;
|
|
||||||
var bignum = bitcore.Bignum;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
require: 'ngModel',
|
require: 'ngModel',
|
||||||
|
|
@ -28,14 +30,14 @@ angular.module('copayApp.directives')
|
||||||
// Bip21 uri
|
// Bip21 uri
|
||||||
if (/^bitcoin:/.test(value)) {
|
if (/^bitcoin:/.test(value)) {
|
||||||
var uri = new bitcore.BIP21(value);
|
var uri = new bitcore.BIP21(value);
|
||||||
var hasAddress = uri.address && uri.isValid() && uri.address.network().name === config.networkName;
|
var hasAddress = uri.address && uri.isValid() && uri.address.network().name === $rootScope.wallet.getNetworkName();
|
||||||
ctrl.$setValidity('validAddress', uri.data.merchant || hasAddress);
|
ctrl.$setValidity('validAddress', uri.data.merchant || hasAddress);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Regular Address
|
// Regular Address
|
||||||
var a = new Address(value);
|
var a = new Address(value);
|
||||||
ctrl.$setValidity('validAddress', a.isValid() && a.network().name === config.networkName);
|
ctrl.$setValidity('validAddress', a.isValid() && a.network().name === $rootScope.wallet.getNetworkName());
|
||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -46,14 +48,17 @@ angular.module('copayApp.directives')
|
||||||
}])
|
}])
|
||||||
.directive('enoughAmount', ['$rootScope',
|
.directive('enoughAmount', ['$rootScope',
|
||||||
function($rootScope) {
|
function($rootScope) {
|
||||||
var bitcore = require('bitcore');
|
var w = $rootScope.wallet;
|
||||||
|
preconditions.checkState(w);
|
||||||
|
preconditions.checkState(w.settings.unitToSatoshi);
|
||||||
|
|
||||||
var feeSat = Number(bitcore.TransactionBuilder.FEE_PER_1000B_SAT);
|
var feeSat = Number(bitcore.TransactionBuilder.FEE_PER_1000B_SAT);
|
||||||
return {
|
return {
|
||||||
require: 'ngModel',
|
require: 'ngModel',
|
||||||
link: function(scope, element, attrs, ctrl) {
|
link: function(scope, element, attrs, ctrl) {
|
||||||
var val = function(value) {
|
var val = function(value) {
|
||||||
var availableBalanceNum = Number(($rootScope.availableBalance * config.unitToSatoshi).toFixed(0));
|
var availableBalanceNum = Number(($rootScope.availableBalance * w.settings.unitToSatoshi).toFixed(0));
|
||||||
var vNum = Number((value * config.unitToSatoshi).toFixed(0));
|
var vNum = Number((value * w.settings.unitToSatoshi).toFixed(0));
|
||||||
|
|
||||||
if (typeof vNum == "number" && vNum > 0) {
|
if (typeof vNum == "number" && vNum > 0) {
|
||||||
vNum = vNum + feeSat;
|
vNum = vNum + feeSat;
|
||||||
|
|
@ -270,7 +275,7 @@ angular.module('copayApp.directives')
|
||||||
|
|
||||||
client.on('datarequested', function(client) {
|
client.on('datarequested', function(client) {
|
||||||
client.setText(scope.clipCopy);
|
client.setText(scope.clipCopy);
|
||||||
} );
|
});
|
||||||
|
|
||||||
client.on('complete', function(client, args) {
|
client.on('complete', function(client, args) {
|
||||||
elm.removeClass('btn-copy').addClass('btn-copied').html('Copied!');
|
elm.removeClass('btn-copy').addClass('btn-copied').html('Copied!');
|
||||||
|
|
|
||||||
|
|
@ -44,43 +44,44 @@ angular.module('copayApp.filters', [])
|
||||||
return addrs;
|
return addrs;
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.filter('noFractionNumber',
|
.filter('noFractionNumber', ['$filter', '$locale', '$rootScope',
|
||||||
[ '$filter', '$locale',
|
function(filter, locale, $rootScope) {
|
||||||
function(filter, locale) {
|
var numberFilter = filter('number');
|
||||||
var numberFilter = filter('number');
|
var formats = locale.NUMBER_FORMATS;
|
||||||
var formats = locale.NUMBER_FORMATS;
|
return function(amount, n) {
|
||||||
return function(amount, n) {
|
if (typeof(n) === 'undefined' && !$rootScope.wallet) return amount;
|
||||||
var fractionSize = (typeof(n) != 'undefined') ? n : config.unitToSatoshi.toString().length - 1;
|
|
||||||
var value = numberFilter(amount, fractionSize);
|
var fractionSize = (typeof(n) !== 'undefined') ?
|
||||||
var sep = value.indexOf(formats.DECIMAL_SEP);
|
n : $rootScope.wallet.settings.unitToSatoshi.toString().length - 1;
|
||||||
var group = value.indexOf(formats.GROUP_SEP);
|
var value = numberFilter(amount, fractionSize);
|
||||||
if(amount >= 0) {
|
var sep = value.indexOf(formats.DECIMAL_SEP);
|
||||||
if (group > 0) {
|
var group = value.indexOf(formats.GROUP_SEP);
|
||||||
if (sep < 0) {
|
if (amount >= 0) {
|
||||||
|
if (group > 0) {
|
||||||
|
if (sep < 0) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
var intValue = value.substring(0, sep);
|
||||||
|
var floatValue = parseFloat(value.substring(sep));
|
||||||
|
if (floatValue === 0) {
|
||||||
|
floatValue = '';
|
||||||
|
} else {
|
||||||
|
if (floatValue % 1 === 0) {
|
||||||
|
floatValue = floatValue.toFixed(0);
|
||||||
|
}
|
||||||
|
floatValue = floatValue.toString().substring(1);
|
||||||
|
}
|
||||||
|
var finalValue = intValue + floatValue;
|
||||||
|
return finalValue;
|
||||||
|
} else {
|
||||||
|
value = parseFloat(value);
|
||||||
|
if (value % 1 === 0) {
|
||||||
|
value = value.toFixed(0);
|
||||||
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
var intValue = value.substring(0, sep);
|
|
||||||
var floatValue = parseFloat(value.substring(sep));
|
|
||||||
if (floatValue === 0) {
|
|
||||||
floatValue = '';
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if(floatValue % 1 === 0) {
|
|
||||||
floatValue = floatValue.toFixed(0);
|
|
||||||
}
|
|
||||||
floatValue = floatValue.toString().substring(1);
|
|
||||||
}
|
|
||||||
var finalValue = intValue + floatValue;
|
|
||||||
return finalValue;
|
|
||||||
}
|
}
|
||||||
else {
|
return 0;
|
||||||
value = parseFloat(value);
|
};
|
||||||
if(value % 1 === 0) {
|
}
|
||||||
value = value.toFixed(0);
|
]);
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
};
|
|
||||||
} ]);
|
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,7 @@ var preconditions = require('preconditions').singleton();
|
||||||
subscribing to transactions on adressess and blocks.
|
subscribing to transactions on adressess and blocks.
|
||||||
|
|
||||||
Opts:
|
Opts:
|
||||||
- host
|
- url
|
||||||
- port
|
|
||||||
- schema
|
|
||||||
- reconnection (optional)
|
- reconnection (optional)
|
||||||
- reconnectionDelay (optional)
|
- reconnectionDelay (optional)
|
||||||
|
|
||||||
|
|
@ -29,22 +27,22 @@ var preconditions = require('preconditions').singleton();
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var Insight = function(opts) {
|
var Insight = function(opts) {
|
||||||
|
preconditions.checkArgument(opts)
|
||||||
|
.shouldBeObject(opts)
|
||||||
|
.checkArgument(opts.url)
|
||||||
|
|
||||||
this.status = this.STATUS.DISCONNECTED;
|
this.status = this.STATUS.DISCONNECTED;
|
||||||
this.subscribed = {};
|
this.subscribed = {};
|
||||||
this.listeningBlocks = false;
|
this.listeningBlocks = false;
|
||||||
|
|
||||||
preconditions.checkArgument(opts).shouldBeObject(opts)
|
this.url = opts.url;
|
||||||
.checkArgument(opts.host)
|
|
||||||
.checkArgument(opts.port)
|
|
||||||
.checkArgument(opts.schema);
|
|
||||||
|
|
||||||
this.url = opts.schema + '://' + opts.host + ':' + opts.port;
|
|
||||||
this.opts = {
|
this.opts = {
|
||||||
'reconnection': opts.reconnection || true,
|
'reconnection': opts.reconnection || true,
|
||||||
'reconnectionDelay': opts.reconnectionDelay || 1000,
|
'reconnectionDelay': opts.reconnectionDelay || 1000,
|
||||||
'secure': opts.schema === 'https'
|
'secure': opts.url.indexOf('https') === 0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.socket = this.getSocket();
|
||||||
}
|
}
|
||||||
|
|
||||||
util.inherits(Insight, EventEmitter);
|
util.inherits(Insight, EventEmitter);
|
||||||
|
|
@ -105,7 +103,7 @@ Insight.prototype._setMainHandlers = function(url, opts) {
|
||||||
|
|
||||||
|
|
||||||
/** @private */
|
/** @private */
|
||||||
Insight.prototype.getSocket = function(url, opts) {
|
Insight.prototype.getSocket = function() {
|
||||||
|
|
||||||
if (!this.socket) {
|
if (!this.socket) {
|
||||||
this.socket = this._getSocketIO(this.url, this.opts);
|
this.socket = this._getSocketIO(this.url, this.opts);
|
||||||
|
|
@ -148,7 +146,6 @@ Insight.prototype.subscribe = function(addresses) {
|
||||||
return function(txid) {
|
return function(txid) {
|
||||||
// verify the address is still subscribed
|
// verify the address is still subscribed
|
||||||
if (!self.subscribed[address]) return;
|
if (!self.subscribed[address]) return;
|
||||||
|
|
||||||
log.debug('insight tx event');
|
log.debug('insight tx event');
|
||||||
|
|
||||||
self.emit('tx', {
|
self.emit('tx', {
|
||||||
|
|
|
||||||
|
|
@ -73,9 +73,9 @@ function Wallet(opts) {
|
||||||
this.id = opts.id || Wallet.getRandomId();
|
this.id = opts.id || Wallet.getRandomId();
|
||||||
this.secretNumber = opts.secretNumber || Wallet.getRandomNumber();
|
this.secretNumber = opts.secretNumber || Wallet.getRandomNumber();
|
||||||
this.lock = new WalletLock(this.storage, this.id, opts.lockTimeOutMin);
|
this.lock = new WalletLock(this.storage, this.id, opts.lockTimeOutMin);
|
||||||
|
this.settings = opts.settings || copayConfig.wallet.settings;
|
||||||
this.name = opts.name;
|
this.name = opts.name;
|
||||||
|
|
||||||
this.verbose = opts.verbose;
|
|
||||||
this.publicKeyRing.walletId = this.id;
|
this.publicKeyRing.walletId = this.id;
|
||||||
this.txProposals.walletId = this.id;
|
this.txProposals.walletId = this.id;
|
||||||
this.network.maxPeers = this.totalCopayers;
|
this.network.maxPeers = this.totalCopayers;
|
||||||
|
|
@ -112,6 +112,21 @@ Wallet.builderOpts = {
|
||||||
feeSat: undefined,
|
feeSat: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc static list with persisted properties of a wallet.
|
||||||
|
* These are the properties that get stored/read from localstorage
|
||||||
|
*/
|
||||||
|
Wallet.PERSISTED_PROPERTIES = [
|
||||||
|
'opts',
|
||||||
|
'settings',
|
||||||
|
'publicKeyRing',
|
||||||
|
'txProposals',
|
||||||
|
'privateKey',
|
||||||
|
'addressBook',
|
||||||
|
'backupOffered',
|
||||||
|
'lastTimestamp',
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc Retrieve a random id for the wallet
|
* @desc Retrieve a random id for the wallet
|
||||||
* @TODO: Discuss changing to a UUID
|
* @TODO: Discuss changing to a UUID
|
||||||
|
|
@ -161,6 +176,22 @@ Wallet.prototype._onIndexes = function(senderId, data) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc
|
||||||
|
* Changes wallet settings. The settings format is:
|
||||||
|
*
|
||||||
|
* var settings = {
|
||||||
|
* unitName: 'bits',
|
||||||
|
* unitToSatoshi: 100,
|
||||||
|
* alternativeName: 'US Dollar',
|
||||||
|
* alternativeIsoCode: 'USD',
|
||||||
|
* };
|
||||||
|
*/
|
||||||
|
Wallet.prototype.changeSettings = function(settings) {
|
||||||
|
this.settings = settings;
|
||||||
|
this.store();
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc
|
* @desc
|
||||||
* Handles a 'PUBLICKEYRING' message from <tt>senderId</tt>.
|
* Handles a 'PUBLICKEYRING' message from <tt>senderId</tt>.
|
||||||
|
|
@ -570,6 +601,7 @@ Wallet.prototype._optsToObj = function() {
|
||||||
totalCopayers: this.totalCopayers,
|
totalCopayers: this.totalCopayers,
|
||||||
name: this.name,
|
name: this.name,
|
||||||
version: this.version,
|
version: this.version,
|
||||||
|
networkName: this.getNetworkName(),
|
||||||
};
|
};
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
|
|
@ -615,7 +647,11 @@ Wallet.prototype.getSecretNumber = function() {
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
Wallet.prototype.getSecret = function() {
|
Wallet.prototype.getSecret = function() {
|
||||||
var buf = new Buffer(this.getMyCopayerId() + this.getSecretNumber(), 'hex');
|
var buf = new Buffer(
|
||||||
|
this.getMyCopayerId() +
|
||||||
|
this.getSecretNumber() +
|
||||||
|
(this.getNetworkName() === 'livenet' ? '00' : '01'),
|
||||||
|
'hex');
|
||||||
var str = Base58Check.encode(buf);
|
var str = Base58Check.encode(buf);
|
||||||
return str;
|
return str;
|
||||||
};
|
};
|
||||||
|
|
@ -630,9 +666,11 @@ Wallet.decodeSecret = function(secretB) {
|
||||||
var secret = Base58Check.decode(secretB);
|
var secret = Base58Check.decode(secretB);
|
||||||
var pubKeyBuf = secret.slice(0, 33);
|
var pubKeyBuf = secret.slice(0, 33);
|
||||||
var secretNumber = secret.slice(33, 38);
|
var secretNumber = secret.slice(33, 38);
|
||||||
|
var networkName = secret.slice(38, 39).toString('hex') === '00' ? 'livenet' : 'testnet';
|
||||||
return {
|
return {
|
||||||
pubKey: pubKeyBuf.toString('hex'),
|
pubKey: pubKeyBuf.toString('hex'),
|
||||||
secretNumber: secretNumber.toString('hex')
|
secretNumber: secretNumber.toString('hex'),
|
||||||
|
networkName: networkName,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -780,9 +818,7 @@ Wallet.prototype.keepAlive = function() {
|
||||||
*/
|
*/
|
||||||
Wallet.prototype.store = function() {
|
Wallet.prototype.store = function() {
|
||||||
this.keepAlive();
|
this.keepAlive();
|
||||||
|
this.storage.setFromObj(this.id, this.toObj());
|
||||||
var wallet = this.toObj();
|
|
||||||
this.storage.setFromObj(this.id, wallet);
|
|
||||||
log.debug('Wallet stored');
|
log.debug('Wallet stored');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -793,13 +829,11 @@ Wallet.prototype.store = function() {
|
||||||
Wallet.prototype.toObj = function() {
|
Wallet.prototype.toObj = function() {
|
||||||
var optsObj = this._optsToObj();
|
var optsObj = this._optsToObj();
|
||||||
|
|
||||||
var networkNonce = this.network.getHexNonce();
|
|
||||||
var networkNonces = this.network.getHexNonces();
|
|
||||||
|
|
||||||
var walletObj = {
|
var walletObj = {
|
||||||
opts: optsObj,
|
opts: optsObj,
|
||||||
networkNonce: networkNonce, //yours
|
settings: this.settings,
|
||||||
networkNonces: networkNonces, //copayers
|
networkNonce: this.network.getHexNonce(), //yours
|
||||||
|
networkNonces: this.network.getHexNonces(), //copayers
|
||||||
publicKeyRing: this.publicKeyRing.toObj(),
|
publicKeyRing: this.publicKeyRing.toObj(),
|
||||||
txProposals: this.txProposals.toObj(),
|
txProposals: this.txProposals.toObj(),
|
||||||
privateKey: this.privateKey ? this.privateKey.toObj() : undefined,
|
privateKey: this.privateKey ? this.privateKey.toObj() : undefined,
|
||||||
|
|
@ -831,6 +865,7 @@ Wallet.fromObj = function(o, storage, network, blockchain) {
|
||||||
var opts = JSON.parse(JSON.stringify(o.opts));
|
var opts = JSON.parse(JSON.stringify(o.opts));
|
||||||
|
|
||||||
opts.addressBook = o.addressBook;
|
opts.addressBook = o.addressBook;
|
||||||
|
opts.settings = o.settings;
|
||||||
|
|
||||||
if (o.privateKey) {
|
if (o.privateKey) {
|
||||||
opts.privateKey = PrivateKey.fromObj(o.privateKey);
|
opts.privateKey = PrivateKey.fromObj(o.privateKey);
|
||||||
|
|
@ -896,7 +931,6 @@ Wallet.prototype.send = function(recipients, obj) {
|
||||||
Wallet.prototype.sendAllTxProposals = function(recipients, sinceTs) {
|
Wallet.prototype.sendAllTxProposals = function(recipients, sinceTs) {
|
||||||
var ntxids = sinceTs ? this.txProposals.getNtxidsSince(sinceTs) : this.txProposals.getNtxids();
|
var ntxids = sinceTs ? this.txProposals.getNtxidsSince(sinceTs) : this.txProposals.getNtxids();
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
_.each(ntxids, function(ntxid, key) {
|
_.each(ntxids, function(ntxid, key) {
|
||||||
self.sendTxProposal(ntxid, recipients);
|
self.sendTxProposal(ntxid, recipients);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ var log = require('../../log');
|
||||||
var Async = module.exports.Async = require('../network/Async');
|
var Async = module.exports.Async = require('../network/Async');
|
||||||
var Insight = module.exports.Insight = require('../blockchain/Insight');
|
var Insight = module.exports.Insight = require('../blockchain/Insight');
|
||||||
var StorageLocalEncrypted = module.exports.StorageLocalEncrypted = require('../storage/LocalEncrypted');
|
var StorageLocalEncrypted = module.exports.StorageLocalEncrypted = require('../storage/LocalEncrypted');
|
||||||
|
var preconditions = require('preconditions').singleton();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc
|
* @desc
|
||||||
|
|
@ -23,10 +24,8 @@ var StorageLocalEncrypted = module.exports.StorageLocalEncrypted = require('../s
|
||||||
* @param {Storage} config.Storage - the class to instantiate to store the wallet (StorageLocalEncrypted by default)
|
* @param {Storage} config.Storage - the class to instantiate to store the wallet (StorageLocalEncrypted by default)
|
||||||
* @param {Object} config.storage - the configuration to be sent to the Storage constructor
|
* @param {Object} config.storage - the configuration to be sent to the Storage constructor
|
||||||
* @param {Network} config.Network - the class to instantiate to make network requests to copayers (the Async module by default)
|
* @param {Network} config.Network - the class to instantiate to make network requests to copayers (the Async module by default)
|
||||||
* @param {Object} config.network - the configuration to be sent to the Network constructor
|
* @param {Object} config.network - the configurations to be sent to the Network and Blockchain constructors
|
||||||
* @param {Blockchain} config.Blockchain - the class to instantiate to get information about the blockchain (Insight by default)
|
* @param {Blockchain} config.Blockchain - the class to instantiate to get information about the blockchain (Insight by default)
|
||||||
* @param {Object} config.blockchain - the configuration to be sent to the Blockchain constructor
|
|
||||||
* @param {string} config.networkName - the name of the bitcoin network to use ('testnet' or 'livenet')
|
|
||||||
* @TODO: Investigate what parameters go inside this object
|
* @TODO: Investigate what parameters go inside this object
|
||||||
* @param {Object} config.wallet - default configuration for the wallet
|
* @param {Object} config.wallet - default configuration for the wallet
|
||||||
* @TODO: put `version` inside of the config object
|
* @TODO: put `version` inside of the config object
|
||||||
|
|
@ -41,10 +40,15 @@ function WalletFactory(config, version) {
|
||||||
this.Blockchain = config.Blockchain || Insight;
|
this.Blockchain = config.Blockchain || Insight;
|
||||||
|
|
||||||
this.storage = new this.Storage(config.storage);
|
this.storage = new this.Storage(config.storage);
|
||||||
this.network = new this.Network(config.network);
|
this.networks = {
|
||||||
this.blockchain = new this.Blockchain(config.blockchain);
|
'livenet': new this.Network(config.network.livenet),
|
||||||
|
'testnet': new this.Network(config.network.testnet),
|
||||||
|
};
|
||||||
|
this.blockchains = {
|
||||||
|
'livenet': new this.Blockchain(config.network.livenet),
|
||||||
|
'testnet': new this.Blockchain(config.network.testnet),
|
||||||
|
};
|
||||||
|
|
||||||
this.networkName = config.networkName;
|
|
||||||
this.walletDefaults = config.wallet;
|
this.walletDefaults = config.wallet;
|
||||||
this.version = version;
|
this.version = version;
|
||||||
};
|
};
|
||||||
|
|
@ -71,33 +75,41 @@ WalletFactory.prototype._checkRead = function(walletId) {
|
||||||
return !!ret;
|
return !!ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc obtain network name from serialized wallet
|
||||||
|
* @param {Object} wallet object
|
||||||
|
* @return {string} network name
|
||||||
|
*/
|
||||||
|
WalletFactory.prototype.obtainNetworkName = function(obj) {
|
||||||
|
return obj.networkName ||
|
||||||
|
obj.opts.networkName ||
|
||||||
|
obj.publicKeyRing.networkName ||
|
||||||
|
obj.privateKey.networkName;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc Deserialize an object to a Wallet
|
* @desc Deserialize an object to a Wallet
|
||||||
* @param {Object} obj
|
* @param {Object} wallet object
|
||||||
* @param {string[]} skipFields - fields to skip when importing
|
* @param {string[]} skipFields - fields to skip when importing
|
||||||
* @return {Wallet}
|
* @return {Wallet}
|
||||||
*/
|
*/
|
||||||
WalletFactory.prototype.fromObj = function(obj, skipFields) {
|
WalletFactory.prototype.fromObj = function(obj, skipFields) {
|
||||||
|
var networkName = this.obtainNetworkName(obj);
|
||||||
|
preconditions.checkState(networkName);
|
||||||
|
|
||||||
// not stored options
|
|
||||||
obj.opts.reconnectDelay = this.walletDefaults.reconnectDelay;
|
obj.opts.reconnectDelay = this.walletDefaults.reconnectDelay;
|
||||||
|
|
||||||
// this is only used if private key or public key ring is skipped
|
|
||||||
obj.opts.networkName = this.networkName;
|
|
||||||
|
|
||||||
skipFields = skipFields || [];
|
skipFields = skipFields || [];
|
||||||
skipFields.forEach(function(k){
|
skipFields.forEach(function(k) {
|
||||||
if (obj[k]) {
|
if (obj[k]) {
|
||||||
delete obj[k];
|
delete obj[k];
|
||||||
} else
|
} else
|
||||||
throw new Error('unknown field:' + k);
|
throw new Error('unknown field:' + k);
|
||||||
});
|
});
|
||||||
|
|
||||||
var w = Wallet.fromObj(obj, this.storage, this.network, this.blockchain);
|
var w = Wallet.fromObj(obj, this.storage, this.networks[networkName], this.blockchains[networkName]);
|
||||||
if (!w) return false;
|
if (!w) return false;
|
||||||
w.verbose = this.verbose;
|
|
||||||
this._checkVersion(w.version);
|
this._checkVersion(w.version);
|
||||||
this._checkNetwork(w.getNetworkName());
|
|
||||||
return w;
|
return w;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -147,13 +159,9 @@ WalletFactory.prototype.read = function(walletId, skipFields) {
|
||||||
var s = this.storage;
|
var s = this.storage;
|
||||||
|
|
||||||
obj.id = walletId;
|
obj.id = walletId;
|
||||||
obj.opts = s.get(walletId, 'opts');
|
_.each(Wallet.PERSISTED_PROPERTIES, function(value) {
|
||||||
obj.publicKeyRing = s.get(walletId, 'publicKeyRing');
|
obj[value] = s.get(walletId, value);
|
||||||
obj.txProposals = s.get(walletId, 'txProposals');
|
});
|
||||||
obj.privateKey = s.get(walletId, 'privateKey');
|
|
||||||
obj.addressBook = s.get(walletId, 'addressBook');
|
|
||||||
obj.backupOffered = s.get(walletId, 'backupOffered');
|
|
||||||
obj.lastTimestamp = s.get(walletId, 'lastTimestamp');
|
|
||||||
|
|
||||||
var w = this.fromObj(obj, skipFields);
|
var w = this.fromObj(obj, skipFields);
|
||||||
return w;
|
return w;
|
||||||
|
|
@ -179,14 +187,17 @@ WalletFactory.prototype.read = function(walletId, skipFields) {
|
||||||
* @return {Wallet}
|
* @return {Wallet}
|
||||||
*/
|
*/
|
||||||
WalletFactory.prototype.create = function(opts) {
|
WalletFactory.prototype.create = function(opts) {
|
||||||
|
|
||||||
opts = opts || {};
|
opts = opts || {};
|
||||||
|
opts.networkName = opts.networkName || 'testnet';
|
||||||
|
|
||||||
log.debug('### CREATING NEW WALLET.' + (opts.id ? ' USING ID: ' + opts.id : ' NEW ID') + (opts.privateKey ? ' USING PrivateKey: ' + opts.privateKey.getId() : ' NEW PrivateKey'));
|
log.debug('### CREATING NEW WALLET.' + (opts.id ? ' USING ID: ' + opts.id : ' NEW ID') + (opts.privateKey ? ' USING PrivateKey: ' + opts.privateKey.getId() : ' NEW PrivateKey'));
|
||||||
|
|
||||||
var privOpts = {
|
var privOpts = {
|
||||||
networkName: this.networkName,
|
networkName: opts.networkName,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (opts.privateKeyHex && opts.privateKeyHex.length>1) {
|
if (opts.privateKeyHex && opts.privateKeyHex.length > 1) {
|
||||||
privOpts.extendedPrivateKeyString = opts.privateKeyHex;
|
privOpts.extendedPrivateKeyString = opts.privateKeyHex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -197,7 +208,7 @@ WalletFactory.prototype.create = function(opts) {
|
||||||
opts.lockTimeoutMin = this.walletDefaults.idleDurationMin;
|
opts.lockTimeoutMin = this.walletDefaults.idleDurationMin;
|
||||||
|
|
||||||
opts.publicKeyRing = opts.publicKeyRing || new PublicKeyRing({
|
opts.publicKeyRing = opts.publicKeyRing || new PublicKeyRing({
|
||||||
networkName: this.networkName,
|
networkName: opts.networkName,
|
||||||
requiredCopayers: requiredCopayers,
|
requiredCopayers: requiredCopayers,
|
||||||
totalCopayers: totalCopayers,
|
totalCopayers: totalCopayers,
|
||||||
});
|
});
|
||||||
|
|
@ -208,16 +219,15 @@ WalletFactory.prototype.create = function(opts) {
|
||||||
log.debug('\t### PublicKeyRing Initialized');
|
log.debug('\t### PublicKeyRing Initialized');
|
||||||
|
|
||||||
opts.txProposals = opts.txProposals || new TxProposals({
|
opts.txProposals = opts.txProposals || new TxProposals({
|
||||||
networkName: this.networkName,
|
networkName: opts.networkName,
|
||||||
});
|
});
|
||||||
log.debug('\t### TxProposals Initialized');
|
log.debug('\t### TxProposals Initialized');
|
||||||
|
|
||||||
this.storage._setPassphrase(opts.passphrase);
|
this.storage._setPassphrase(opts.passphrase);
|
||||||
|
|
||||||
opts.storage = this.storage;
|
opts.storage = this.storage;
|
||||||
opts.network = this.network;
|
opts.network = this.networks[opts.networkName];
|
||||||
opts.blockchain = this.blockchain;
|
opts.blockchain = this.blockchains[opts.networkName];
|
||||||
opts.verbose = this.verbose;
|
|
||||||
|
|
||||||
opts.spendUnconfirmed = opts.spendUnconfirmed || this.walletDefaults.spendUnconfirmed;
|
opts.spendUnconfirmed = opts.spendUnconfirmed || this.walletDefaults.spendUnconfirmed;
|
||||||
opts.reconnectDelay = opts.reconnectDelay || this.walletDefaults.reconnectDelay;
|
opts.reconnectDelay = opts.reconnectDelay || this.walletDefaults.reconnectDelay;
|
||||||
|
|
@ -245,20 +255,9 @@ WalletFactory.prototype._checkVersion = function(inVersion) {
|
||||||
//We only check for major version differences
|
//We only check for major version differences
|
||||||
if (thisV0 < inV0) {
|
if (thisV0 < inV0) {
|
||||||
throw new Error('Major difference in software versions' +
|
throw new Error('Major difference in software versions' +
|
||||||
'. Received:' + inVersion +
|
'. Received:' + inVersion +
|
||||||
'. Current version:' + this.version +
|
'. Current version:' + this.version +
|
||||||
'. Aborting.');
|
'. Aborting.');
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @desc Throw an error if the network name is different to {@link WalletFactory#networkName}
|
|
||||||
* @param {string} inNetworkName - the network name to check
|
|
||||||
* @throws {Error}
|
|
||||||
*/
|
|
||||||
WalletFactory.prototype._checkNetwork = function(inNetworkName) {
|
|
||||||
if (this.networkName !== inNetworkName) {
|
|
||||||
throw new Error('This Wallet is configured for ' + inNetworkName + ' while currently Copay is configured for: ' + this.networkName + '. Check your settings.');
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -271,8 +270,9 @@ WalletFactory.prototype._checkNetwork = function(inNetworkName) {
|
||||||
WalletFactory.prototype.open = function(walletId, passphrase) {
|
WalletFactory.prototype.open = function(walletId, passphrase) {
|
||||||
this.storage._setPassphrase(passphrase);
|
this.storage._setPassphrase(passphrase);
|
||||||
var w = this.read(walletId);
|
var w = this.read(walletId);
|
||||||
if (w)
|
if (w) {
|
||||||
w.store();
|
w.store();
|
||||||
|
}
|
||||||
|
|
||||||
this.storage.setLastOpened(walletId);
|
this.storage.setLastOpened(walletId);
|
||||||
return w;
|
return w;
|
||||||
|
|
@ -338,14 +338,16 @@ WalletFactory.prototype.decodeSecret = function(secret) {
|
||||||
*/
|
*/
|
||||||
WalletFactory.prototype.joinCreateSession = function(secret, nickname, passphrase, privateHex, cb) {
|
WalletFactory.prototype.joinCreateSession = function(secret, nickname, passphrase, privateHex, cb) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var s = self.decodeSecret(secret);
|
var decodedSecret = this.decodeSecret(secret);
|
||||||
if (!s) return cb('badSecret');
|
if (!decodedSecret || !decodedSecret.networkName || !decodedSecret.pubKey) {
|
||||||
|
return cb('badSecret');
|
||||||
|
}
|
||||||
|
|
||||||
var privOpts = {
|
var privOpts = {
|
||||||
networkName: this.networkName,
|
networkName: decodedSecret.networkName,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (privateHex && privateHex.length>1) {
|
if (privateHex && privateHex.length > 1) {
|
||||||
privOpts.extendedPrivateKeyString = privateHex;
|
privOpts.extendedPrivateKeyString = privateHex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -356,25 +358,27 @@ WalletFactory.prototype.joinCreateSession = function(secret, nickname, passphras
|
||||||
copayerId: privateKey.getId(),
|
copayerId: privateKey.getId(),
|
||||||
privkey: privateKey.getIdPriv(),
|
privkey: privateKey.getIdPriv(),
|
||||||
key: privateKey.getIdKey(),
|
key: privateKey.getIdKey(),
|
||||||
secretNumber : s.secretNumber,
|
secretNumber: decodedSecret.secretNumber,
|
||||||
};
|
};
|
||||||
self.network.cleanUp();
|
|
||||||
|
var joinNetwork = this.networks[decodedSecret.networkName];
|
||||||
|
joinNetwork.cleanUp();
|
||||||
|
|
||||||
// This is a hack to reconize if the connection was rejected or the peer wasn't there.
|
// This is a hack to reconize if the connection was rejected or the peer wasn't there.
|
||||||
var connectedOnce = false;
|
var connectedOnce = false;
|
||||||
self.network.on('connected', function(sender, data) {
|
joinNetwork.on('connected', function(sender, data) {
|
||||||
connectedOnce = true;
|
connectedOnce = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
self.network.on('serverError', function() {
|
joinNetwork.on('serverError', function() {
|
||||||
return cb('joinError');
|
return cb('joinError');
|
||||||
});
|
});
|
||||||
|
|
||||||
self.network.start(opts, function() {
|
joinNetwork.start(opts, function() {
|
||||||
self.network.greet(s.pubKey,opts.secretNumber);
|
joinNetwork.greet(decodedSecret.pubKey, opts.secretNumber);
|
||||||
self.network.on('data', function(sender, data) {
|
joinNetwork.on('data', function(sender, data) {
|
||||||
if (data.type === 'walletId') {
|
if (data.type === 'walletId') {
|
||||||
if (data.networkName !== self.networkName) {
|
if (data.networkName !== decodedSecret.networkName) {
|
||||||
return cb('badNetwork');
|
return cb('badNetwork');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -383,8 +387,7 @@ WalletFactory.prototype.joinCreateSession = function(secret, nickname, passphras
|
||||||
data.opts.passphrase = passphrase;
|
data.opts.passphrase = passphrase;
|
||||||
data.opts.id = data.walletId;
|
data.opts.id = data.walletId;
|
||||||
var w = self.create(data.opts);
|
var w = self.create(data.opts);
|
||||||
w.sendWalletReady(s.pubKey);
|
w.sendWalletReady(decodedSecret.pubKey);
|
||||||
//w.seedCopayer(s.pubKey);
|
|
||||||
return cb(null, w);
|
return cb(null, w);
|
||||||
} else {
|
} else {
|
||||||
return cb('walletFull', w);
|
return cb('walletFull', w);
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,11 @@ var io = require('socket.io-client');
|
||||||
var preconditions = require('preconditions').singleton();
|
var preconditions = require('preconditions').singleton();
|
||||||
|
|
||||||
function Network(opts) {
|
function Network(opts) {
|
||||||
var self = this;
|
preconditions.checkArgument(opts);
|
||||||
|
preconditions.checkArgument(opts.url);
|
||||||
opts = opts || {};
|
opts = opts || {};
|
||||||
this.maxPeers = opts.maxPeers || 12;
|
this.maxPeers = opts.maxPeers || 12;
|
||||||
this.host = opts.host || 'localhost';
|
this.url = opts.url;
|
||||||
this.port = opts.port || 3001;
|
|
||||||
this.schema = opts.schema || 'https';
|
|
||||||
this.secretNumber = opts.secretNumber;
|
this.secretNumber = opts.secretNumber;
|
||||||
this.cleanUp();
|
this.cleanUp();
|
||||||
}
|
}
|
||||||
|
|
@ -74,12 +73,12 @@ Network.prototype.connectedCopayers = function() {
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
Network.prototype._sendHello = function(copayerId,secretNumber) {
|
Network.prototype._sendHello = function(copayerId, secretNumber) {
|
||||||
|
|
||||||
this.send(copayerId, {
|
this.send(copayerId, {
|
||||||
type: 'hello',
|
type: 'hello',
|
||||||
copayerId: this.copayerId,
|
copayerId: this.copayerId,
|
||||||
secretNumber : secretNumber
|
secretNumber: secretNumber
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -197,8 +196,7 @@ Network.prototype._onMessage = function(enc) {
|
||||||
var self = this;
|
var self = this;
|
||||||
switch (payload.type) {
|
switch (payload.type) {
|
||||||
case 'hello':
|
case 'hello':
|
||||||
if (typeof payload.secretNumber === 'undefined' || payload.secretNumber !== this.secretNumber)
|
if (typeof payload.secretNumber === 'undefined' || payload.secretNumber !== this.secretNumber) {
|
||||||
{
|
|
||||||
this._sendRejectConnection(sender);
|
this._sendRejectConnection(sender);
|
||||||
this._deletePeer(enc.pubkey, 'incorrect secret number');
|
this._deletePeer(enc.pubkey, 'incorrect secret number');
|
||||||
return;
|
return;
|
||||||
|
|
@ -274,8 +272,8 @@ Network.prototype._onError = function(err) {
|
||||||
this.criticalError = err.message;
|
this.criticalError = err.message;
|
||||||
};
|
};
|
||||||
|
|
||||||
Network.prototype.greet = function(copayerId,secretNumber) {
|
Network.prototype.greet = function(copayerId, secretNumber) {
|
||||||
this._sendHello(copayerId,secretNumber);
|
this._sendHello(copayerId, secretNumber);
|
||||||
var peerId = this.peerFromCopayer(copayerId);
|
var peerId = this.peerFromCopayer(copayerId);
|
||||||
this._addCopayerMap(peerId, copayerId);
|
this._addCopayerMap(peerId, copayerId);
|
||||||
};
|
};
|
||||||
|
|
@ -326,11 +324,10 @@ Network.prototype.start = function(opts, openCallback) {
|
||||||
};
|
};
|
||||||
|
|
||||||
Network.prototype.createSocket = function() {
|
Network.prototype.createSocket = function() {
|
||||||
var hostPort = this.schema + '://' + this.host + ':' + this.port;
|
return io.connect(this.url, {
|
||||||
return io.connect(hostPort, {
|
|
||||||
reconnection: true,
|
reconnection: true,
|
||||||
'force new connection': true,
|
'force new connection': true,
|
||||||
'secure': this.schema === 'https',
|
'secure': this.url.indexOf('https') === 0,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,6 @@ angular.module('copayApp.services')
|
||||||
$scope.loading = false;
|
$scope.loading = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
w.on('corrupt', function(peerId) {
|
w.on('corrupt', function(peerId) {
|
||||||
notification.error('Error', 'Received corrupt message from ' + peerId);
|
notification.error('Error', 'Received corrupt message from ' + peerId);
|
||||||
});
|
});
|
||||||
|
|
@ -176,7 +175,7 @@ angular.module('copayApp.services')
|
||||||
w.getBalance(function(err, balanceSat, balanceByAddrSat, safeBalanceSat) {
|
w.getBalance(function(err, balanceSat, balanceByAddrSat, safeBalanceSat) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
|
|
||||||
var satToUnit = 1 / config.unitToSatoshi;
|
var satToUnit = 1 / w.settings.unitToSatoshi;
|
||||||
var COIN = bitcore.util.COIN;
|
var COIN = bitcore.util.COIN;
|
||||||
|
|
||||||
$rootScope.totalBalance = balanceSat * satToUnit;
|
$rootScope.totalBalance = balanceSat * satToUnit;
|
||||||
|
|
@ -196,9 +195,9 @@ angular.module('copayApp.services')
|
||||||
$rootScope.updatingBalance = false;
|
$rootScope.updatingBalance = false;
|
||||||
|
|
||||||
rateService.whenAvailable(function() {
|
rateService.whenAvailable(function() {
|
||||||
$rootScope.totalBalanceAlternative = rateService.toFiat(balanceSat, config.alternativeIsoCode);
|
$rootScope.totalBalanceAlternative = rateService.toFiat(balanceSat, w.settings.alternativeIsoCode);
|
||||||
$rootScope.alternativeIsoCode = config.alternativeIsoCode;
|
$rootScope.alternativeIsoCode = w.settings.alternativeIsoCode;
|
||||||
$rootScope.lockedBalanceAlternative = rateService.toFiat(balanceSat - safeBalanceSat, config.alternativeIsoCode);
|
$rootScope.lockedBalanceAlternative = rateService.toFiat(balanceSat - safeBalanceSat, w.settings.alternativeIsoCode);
|
||||||
|
|
||||||
|
|
||||||
return cb ? cb() : null;
|
return cb ? cb() : null;
|
||||||
|
|
@ -211,7 +210,7 @@ angular.module('copayApp.services')
|
||||||
if (!w) return;
|
if (!w) return;
|
||||||
opts = opts || $rootScope.txsOpts || {};
|
opts = opts || $rootScope.txsOpts || {};
|
||||||
|
|
||||||
var satToUnit = 1 / config.unitToSatoshi;
|
var satToUnit = 1 / w.settings.unitToSatoshi;
|
||||||
var myCopayerId = w.getMyCopayerId();
|
var myCopayerId = w.getMyCopayerId();
|
||||||
var pendingForUs = 0;
|
var pendingForUs = 0;
|
||||||
var inT = w.getTxProposals().sort(function(t1, t2) {
|
var inT = w.getTxProposals().sort(function(t1, t2) {
|
||||||
|
|
@ -235,7 +234,7 @@ angular.module('copayApp.services')
|
||||||
var tx = i.builder.build();
|
var tx = i.builder.build();
|
||||||
var outs = [];
|
var outs = [];
|
||||||
tx.outs.forEach(function(o) {
|
tx.outs.forEach(function(o) {
|
||||||
var addr = bitcore.Address.fromScriptPubKey(o.getScript(), config.networkName)[0].toString();
|
var addr = bitcore.Address.fromScriptPubKey(o.getScript(), w.getNetworkName())[0].toString();
|
||||||
if (!w.addressIsOwn(addr, {
|
if (!w.addressIsOwn(addr, {
|
||||||
excludeMain: true
|
excludeMain: true
|
||||||
})) {
|
})) {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/bitpay/copay/issues"
|
"url": "https://github.com/bitpay/copay/issues"
|
||||||
},
|
},
|
||||||
"version": "0.5.0",
|
"version": "0.6.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"browser-request": "^0.3.2",
|
"browser-request": "^0.3.2",
|
||||||
"inherits": "^2.0.1",
|
"inherits": "^2.0.1",
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ FakeBlockchain.prototype.getTransaction = function(txid, cb) {
|
||||||
};
|
};
|
||||||
|
|
||||||
FakeBlockchain.prototype.getTransactions = function(addresses, cb) {
|
FakeBlockchain.prototype.getTransactions = function(addresses, cb) {
|
||||||
return cb(null, []);
|
cb(null, []);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,10 @@ if (is_browser) {
|
||||||
}
|
}
|
||||||
var Wallet = copay.Wallet;
|
var Wallet = copay.Wallet;
|
||||||
|
|
||||||
var FakePrivateKey = function () {
|
var FakePrivateKey = function() {};
|
||||||
};
|
|
||||||
|
|
||||||
FakePrivateKey.prototype.toObj = function() {
|
FakePrivateKey.prototype.toObj = function() {
|
||||||
return extendedPublicKeyString = 'privHex';
|
return extendedPublicKeyString = 'privHex';
|
||||||
};
|
};
|
||||||
|
|
||||||
var FakeWallet = function() {
|
var FakeWallet = function() {
|
||||||
|
|
@ -37,11 +36,21 @@ var FakeWallet = function() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
this.blockchain = {
|
this.blockchain = {
|
||||||
getSubscriptions: function(){ return []; },
|
getSubscriptions: function() {
|
||||||
subscribe: function(){}
|
return [];
|
||||||
|
},
|
||||||
|
subscribe: function() {},
|
||||||
|
getTransactions: function() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.privateKey = new FakePrivateKey();
|
this.privateKey = new FakePrivateKey();
|
||||||
|
this.settings = {
|
||||||
|
unitName: 'bits',
|
||||||
|
unitToSatoshi: 100,
|
||||||
|
unitDecimals: 2,
|
||||||
|
alternativeName: 'US Dollar',
|
||||||
|
alternativeIsoCode: 'USD',
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
FakeWallet.prototype.createTx = function(toAddress, amountSatStr, comment, opts, cb) {
|
FakeWallet.prototype.createTx = function(toAddress, amountSatStr, comment, opts, cb) {
|
||||||
|
|
@ -52,6 +61,9 @@ FakeWallet.prototype.createTx = function(toAddress, amountSatStr, comment, opts,
|
||||||
FakeWallet.prototype.sendTx = function(ntxid, cb) {
|
FakeWallet.prototype.sendTx = function(ntxid, cb) {
|
||||||
cb(8);
|
cb(8);
|
||||||
}
|
}
|
||||||
|
FakeWallet.prototype.getAddressesStr = function() {
|
||||||
|
return ['2Mw2YXxyMD7fhtPhHYY39X6BVWiBRaez5Zn'];
|
||||||
|
};
|
||||||
|
|
||||||
FakeWallet.prototype.set = function(balance, safeBalance, balanceByAddr) {
|
FakeWallet.prototype.set = function(balance, safeBalance, balanceByAddr) {
|
||||||
this.balance = balance;
|
this.balance = balance;
|
||||||
|
|
@ -98,8 +110,7 @@ FakeWallet.prototype.getBalance = function(cb) {
|
||||||
return cb(null, this.balance, this.balanceByAddr, this.safeBalance);
|
return cb(null, this.balance, this.balanceByAddr, this.safeBalance);
|
||||||
};
|
};
|
||||||
|
|
||||||
FakeWallet.prototype.removeTxWithSpentInputs = function (cb) {
|
FakeWallet.prototype.removeTxWithSpentInputs = function(cb) {};
|
||||||
};
|
|
||||||
|
|
||||||
FakeWallet.prototype.setEnc = function(enc) {
|
FakeWallet.prototype.setEnc = function(enc) {
|
||||||
this.enc = enc;
|
this.enc = enc;
|
||||||
|
|
@ -109,7 +120,10 @@ FakeWallet.prototype.toEncryptedObj = function() {
|
||||||
return this.enc;
|
return this.enc;
|
||||||
};
|
};
|
||||||
|
|
||||||
FakeWallet.prototype.close = function() {
|
FakeWallet.prototype.close = function() {};
|
||||||
|
|
||||||
|
FakeWallet.prototype.getNetworkName = function() {
|
||||||
|
return 'testnet';
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO a try catch was here
|
// TODO a try catch was here
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,6 @@ describe('PayPro (in Wallet) model', function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
c.networkName = walletConfig.networkName;
|
c.networkName = walletConfig.networkName;
|
||||||
c.verbose = walletConfig.verbose;
|
|
||||||
c.version = '0.0.1';
|
c.version = '0.0.1';
|
||||||
|
|
||||||
return new Wallet(c);
|
return new Wallet(c);
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,6 @@ describe('Wallet model', function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
c.networkName = walletConfig.networkName;
|
c.networkName = walletConfig.networkName;
|
||||||
c.verbose = walletConfig.verbose;
|
|
||||||
c.version = '0.0.1';
|
c.version = '0.0.1';
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -363,7 +362,18 @@ describe('Wallet model', function() {
|
||||||
var s = Wallet.decodeSecret(sb);
|
var s = Wallet.decodeSecret(sb);
|
||||||
s.pubKey.should.equal(id);
|
s.pubKey.should.equal(id);
|
||||||
s.secretNumber.should.equal(secretNumber);
|
s.secretNumber.should.equal(secretNumber);
|
||||||
|
s.networkName.should.equal(w.getNetworkName());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('#getSecret decodeSecret livenet', function() {
|
||||||
|
var w = cachedCreateW2();
|
||||||
|
var stub = sinon.stub(w, 'getNetworkName');
|
||||||
|
stub.returns('livenet');
|
||||||
|
var sb = w.getSecret();
|
||||||
|
should.exist(sb);
|
||||||
|
var s = Wallet.decodeSecret(sb);
|
||||||
|
s.networkName.should.equal('livenet');
|
||||||
|
stub.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -40,9 +40,7 @@ var UNSPENT = [{
|
||||||
}];
|
}];
|
||||||
|
|
||||||
var FAKE_OPTS = {
|
var FAKE_OPTS = {
|
||||||
host: 'something.com',
|
url: 'http://something.com:123',
|
||||||
port: 123,
|
|
||||||
schema: 'http'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('Insight model', function() {
|
describe('Insight model', function() {
|
||||||
|
|
@ -348,7 +346,7 @@ describe('Insight model', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Events', function() {
|
describe('Events', function() {
|
||||||
it('should emmit event on a new block', function(done) {
|
it('should emit event on a new block', function(done) {
|
||||||
var blockchain = new Insight(FAKE_OPTS);
|
var blockchain = new Insight(FAKE_OPTS);
|
||||||
var socket = blockchain.getSocket();
|
var socket = blockchain.getSocket();
|
||||||
blockchain.on('connect', function() {
|
blockchain.on('connect', function() {
|
||||||
|
|
@ -362,7 +360,7 @@ describe('Insight model', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should emmit event on a transaction for subscribed addresses', function(done) {
|
it('should emit event on a transaction for subscribed addresses', function(done) {
|
||||||
var blockchain = new Insight(FAKE_OPTS);
|
var blockchain = new Insight(FAKE_OPTS);
|
||||||
var socket = blockchain.getSocket();
|
var socket = blockchain.getSocket();
|
||||||
blockchain.subscribe('2NFjCBFZSsxiwWAD7CKQ3hzWFtf9DcqTucY');
|
blockchain.subscribe('2NFjCBFZSsxiwWAD7CKQ3hzWFtf9DcqTucY');
|
||||||
|
|
@ -378,7 +376,7 @@ describe('Insight model', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should\'t emmit event on a transaction for non subscribed addresses', function(done) {
|
it('should\'t emit event on a transaction for non subscribed addresses', function(done) {
|
||||||
var blockchain = new Insight(FAKE_OPTS);
|
var blockchain = new Insight(FAKE_OPTS);
|
||||||
var socket = blockchain.getSocket();
|
var socket = blockchain.getSocket();
|
||||||
blockchain.on('connect', function() {
|
blockchain.on('connect', function() {
|
||||||
|
|
@ -392,7 +390,7 @@ describe('Insight model', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should emmit event on connection', function(done) {
|
it('should emit event on connection', function(done) {
|
||||||
var blockchain = new Insight(FAKE_OPTS);
|
var blockchain = new Insight(FAKE_OPTS);
|
||||||
var socket = blockchain.getSocket();
|
var socket = blockchain.getSocket();
|
||||||
blockchain.on('connect', function() {
|
blockchain.on('connect', function() {
|
||||||
|
|
@ -400,7 +398,7 @@ describe('Insight model', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should emmit event on disconnection', function(done) {
|
it('should emit event on disconnection', function(done) {
|
||||||
var blockchain = new Insight(FAKE_OPTS);
|
var blockchain = new Insight(FAKE_OPTS);
|
||||||
var socket = blockchain.getSocket();
|
var socket = blockchain.getSocket();
|
||||||
blockchain.on('connect', function() {
|
blockchain.on('connect', function() {
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,9 @@ describe('Network / Async', function() {
|
||||||
|
|
||||||
|
|
||||||
var createN = function(pk) {
|
var createN = function(pk) {
|
||||||
var n = new Async();
|
var n = new Async({
|
||||||
|
url: 'http://insight.example.com:1234'
|
||||||
|
});
|
||||||
var fakeSocket = {};
|
var fakeSocket = {};
|
||||||
fakeSocket.emit = function() {};
|
fakeSocket.emit = function() {};
|
||||||
fakeSocket.on = function() {};
|
fakeSocket.on = function() {};
|
||||||
|
|
|
||||||
|
|
@ -34,12 +34,6 @@ describe("Unit: Controllers", function() {
|
||||||
alternativeIsoCode: 'LOL'
|
alternativeIsoCode: 'LOL'
|
||||||
};
|
};
|
||||||
|
|
||||||
it('Copay config should be binded', function() {
|
|
||||||
should.exist(config);
|
|
||||||
should.exist(config.unitToSatoshi);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
describe('More Controller', function() {
|
describe('More Controller', function() {
|
||||||
var ctrl;
|
var ctrl;
|
||||||
beforeEach(inject(function($controller, $rootScope) {
|
beforeEach(inject(function($controller, $rootScope) {
|
||||||
|
|
@ -110,6 +104,7 @@ describe("Unit: Controllers", function() {
|
||||||
var transactionsCtrl;
|
var transactionsCtrl;
|
||||||
beforeEach(inject(function($controller, $rootScope) {
|
beforeEach(inject(function($controller, $rootScope) {
|
||||||
scope = $rootScope.$new();
|
scope = $rootScope.$new();
|
||||||
|
$rootScope.wallet = new FakeWallet(walletConfig);
|
||||||
transactionsCtrl = $controller('TransactionsController', {
|
transactionsCtrl = $controller('TransactionsController', {
|
||||||
$scope: scope,
|
$scope: scope,
|
||||||
});
|
});
|
||||||
|
|
@ -131,7 +126,11 @@ describe("Unit: Controllers", function() {
|
||||||
beforeEach(module(function($provide) {
|
beforeEach(module(function($provide) {
|
||||||
$provide.value('request', {
|
$provide.value('request', {
|
||||||
'get': function(_, cb) {
|
'get': function(_, cb) {
|
||||||
cb(null, null, [{name: 'lol currency', code: 'LOL', rate: 2}]);
|
cb(null, null, [{
|
||||||
|
name: 'lol currency',
|
||||||
|
code: 'LOL',
|
||||||
|
rate: 2
|
||||||
|
}]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
@ -139,8 +138,8 @@ describe("Unit: Controllers", function() {
|
||||||
scope = $rootScope.$new();
|
scope = $rootScope.$new();
|
||||||
scope.rateService = rateService;
|
scope.rateService = rateService;
|
||||||
$rootScope.wallet = new FakeWallet(walletConfig);
|
$rootScope.wallet = new FakeWallet(walletConfig);
|
||||||
config.alternativeName = 'lol currency';
|
$rootScope.wallet.settings.alternativeName = 'lol currency';
|
||||||
config.alternativeIsoCode = 'LOL';
|
$rootScope.wallet.settings.alternativeIsoCode = 'LOL';
|
||||||
var element = angular.element(
|
var element = angular.element(
|
||||||
'<form name="form">' +
|
'<form name="form">' +
|
||||||
'<input type="text" id="newaddress" name="newaddress" ng-disabled="loading" placeholder="Address" ng-model="newaddress" valid-address required>' +
|
'<input type="text" id="newaddress" name="newaddress" ng-disabled="loading" placeholder="Address" ng-model="newaddress" valid-address required>' +
|
||||||
|
|
@ -224,35 +223,35 @@ describe("Unit: Controllers", function() {
|
||||||
sinon.assert.callCount(spy2, 0);
|
sinon.assert.callCount(spy2, 0);
|
||||||
sinon.assert.callCount(scope.loadTxs, 1);
|
sinon.assert.callCount(scope.loadTxs, 1);
|
||||||
spy.getCall(0).args[0].should.equal('mkfTyEk7tfgV611Z4ESwDDSZwhsZdbMpVy');
|
spy.getCall(0).args[0].should.equal('mkfTyEk7tfgV611Z4ESwDDSZwhsZdbMpVy');
|
||||||
spy.getCall(0).args[1].should.equal(1000 * config.unitToSatoshi);
|
spy.getCall(0).args[1].should.equal(1000 * scope.wallet.settings.unitToSatoshi);
|
||||||
(typeof spy.getCall(0).args[2]).should.equal('undefined');
|
(typeof spy.getCall(0).args[2]).should.equal('undefined');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should handle big values in 100 BTC', function() {
|
it('should handle big values in 100 BTC', function() {
|
||||||
var old = config.unitToSatoshi;
|
var old = scope.wallet.settings.unitToSatoshi;
|
||||||
config.unitToSatoshi = 100000000;;
|
scope.wallet.settings.unitToSatoshi = 100000000;;
|
||||||
sendForm.address.$setViewValue('mkfTyEk7tfgV611Z4ESwDDSZwhsZdbMpVy');
|
sendForm.address.$setViewValue('mkfTyEk7tfgV611Z4ESwDDSZwhsZdbMpVy');
|
||||||
sendForm.amount.$setViewValue(100);
|
sendForm.amount.$setViewValue(100);
|
||||||
var spy = sinon.spy(scope.wallet, 'createTx');
|
var spy = sinon.spy(scope.wallet, 'createTx');
|
||||||
scope.loadTxs = sinon.spy();
|
scope.loadTxs = sinon.spy();
|
||||||
scope.submitForm(sendForm);
|
scope.submitForm(sendForm);
|
||||||
spy.getCall(0).args[1].should.equal(100 * config.unitToSatoshi);
|
spy.getCall(0).args[1].should.equal(100 * scope.wallet.settings.unitToSatoshi);
|
||||||
config.unitToSatoshi = old;
|
scope.wallet.settings.unitToSatoshi = old;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should handle big values in 5000 BTC', function() {
|
it('should handle big values in 5000 BTC', inject(function($rootScope) {
|
||||||
var old = config.unitToSatoshi;
|
var old = $rootScope.wallet.settings.unitToSatoshi;
|
||||||
config.unitToSatoshi = 100000000;;
|
$rootScope.wallet.settings.unitToSatoshi = 100000000;;
|
||||||
sendForm.address.$setViewValue('mkfTyEk7tfgV611Z4ESwDDSZwhsZdbMpVy');
|
sendForm.address.$setViewValue('mkfTyEk7tfgV611Z4ESwDDSZwhsZdbMpVy');
|
||||||
sendForm.amount.$setViewValue(5000);
|
sendForm.amount.$setViewValue(5000);
|
||||||
var spy = sinon.spy(scope.wallet, 'createTx');
|
var spy = sinon.spy(scope.wallet, 'createTx');
|
||||||
scope.loadTxs = sinon.spy();
|
scope.loadTxs = sinon.spy();
|
||||||
scope.submitForm(sendForm);
|
scope.submitForm(sendForm);
|
||||||
spy.getCall(0).args[1].should.equal(5000 * config.unitToSatoshi);
|
spy.getCall(0).args[1].should.equal(5000 * $rootScope.wallet.settings.unitToSatoshi);
|
||||||
config.unitToSatoshi = old;
|
$rootScope.wallet.settings.unitToSatoshi = old;
|
||||||
});
|
}));
|
||||||
|
|
||||||
it('should convert bits amount to fiat', function(done) {
|
it('should convert bits amount to fiat', function(done) {
|
||||||
scope.rateService.whenAvailable(function() {
|
scope.rateService.whenAvailable(function() {
|
||||||
|
|
@ -305,15 +304,15 @@ describe("Unit: Controllers", function() {
|
||||||
beforeEach(inject(function($controller, $injector) {
|
beforeEach(inject(function($controller, $injector) {
|
||||||
$httpBackend = $injector.get('$httpBackend');
|
$httpBackend = $injector.get('$httpBackend');
|
||||||
$httpBackend.when('GET', GH)
|
$httpBackend.when('GET', GH)
|
||||||
.respond([{
|
.respond([{
|
||||||
name: "v100.1.6",
|
name: "v100.1.6",
|
||||||
zipball_url: "https://api.github.com/repos/bitpay/copay/zipball/v0.0.6",
|
zipball_url: "https://api.github.com/repos/bitpay/copay/zipball/v0.0.6",
|
||||||
tarball_url: "https://api.github.com/repos/bitpay/copay/tarball/v0.0.6",
|
tarball_url: "https://api.github.com/repos/bitpay/copay/tarball/v0.0.6",
|
||||||
commit: {
|
commit: {
|
||||||
sha: "ead7352bf2eca705de58d8b2f46650691f2bc2c7",
|
sha: "ead7352bf2eca705de58d8b2f46650691f2bc2c7",
|
||||||
url: "https://api.github.com/repos/bitpay/copay/commits/ead7352bf2eca705de58d8b2f46650691f2bc2c7"
|
url: "https://api.github.com/repos/bitpay/copay/commits/ead7352bf2eca705de58d8b2f46650691f2bc2c7"
|
||||||
}
|
}
|
||||||
}]);
|
}]);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
var rootScope;
|
var rootScope;
|
||||||
|
|
@ -358,11 +357,6 @@ describe("Unit: Controllers", function() {
|
||||||
scope.$apply();
|
scope.$apply();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return networkName', function() {
|
|
||||||
$httpBackend.flush(); // need flush
|
|
||||||
var networkName = scope.networkName;
|
|
||||||
expect(networkName).equal('testnet');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Unit: Sidebar Controller", function() {
|
describe("Unit: Sidebar Controller", function() {
|
||||||
|
|
@ -390,6 +384,7 @@ describe("Unit: Controllers", function() {
|
||||||
beforeEach(inject(function($compile, $rootScope, $controller) {
|
beforeEach(inject(function($compile, $rootScope, $controller) {
|
||||||
scope = $rootScope.$new();
|
scope = $rootScope.$new();
|
||||||
$rootScope.availableBalance = 123456;
|
$rootScope.availableBalance = 123456;
|
||||||
|
$rootScope.wallet = new FakeWallet(walletConfig);
|
||||||
|
|
||||||
var element = angular.element(
|
var element = angular.element(
|
||||||
'<form name="form">' +
|
'<form name="form">' +
|
||||||
|
|
|
||||||
|
|
@ -8,17 +8,23 @@ describe("Unit: Testing Directives", function() {
|
||||||
|
|
||||||
beforeEach(module('copayApp.directives'));
|
beforeEach(module('copayApp.directives'));
|
||||||
|
|
||||||
beforeEach(function() {
|
var walletConfig = {
|
||||||
config.unitToSatoshi = 100;
|
requiredCopayers: 3,
|
||||||
config.unitName = 'bits';
|
totalCopayers: 5,
|
||||||
});
|
spendUnconfirmed: 1,
|
||||||
|
reconnectDelay: 100,
|
||||||
|
networkName: 'testnet',
|
||||||
|
alternativeName: 'lol currency',
|
||||||
|
alternativeIsoCode: 'LOL'
|
||||||
|
};
|
||||||
|
|
||||||
describe('Check config', function() {
|
|
||||||
it('unit should be set to BITS in config.js', function() {
|
beforeEach(inject(function($rootScope) {
|
||||||
expect(config.unitToSatoshi).to.equal(100);
|
$rootScope.wallet = new FakeWallet(walletConfig);
|
||||||
expect(config.unitName).to.equal('bits');
|
var w = $rootScope.wallet;
|
||||||
});
|
w.settings.unitToSatoshi = 100;
|
||||||
});
|
w.settings.unitName = 'bits';
|
||||||
|
}));
|
||||||
|
|
||||||
describe('Validate Address', function() {
|
describe('Validate Address', function() {
|
||||||
beforeEach(inject(function($compile, $rootScope) {
|
beforeEach(inject(function($compile, $rootScope) {
|
||||||
|
|
@ -36,16 +42,16 @@ describe("Unit: Testing Directives", function() {
|
||||||
form = $scope.form;
|
form = $scope.form;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should validate with network', function() {
|
it('should validate with network', inject(function($rootScope) {
|
||||||
config.networkName = 'testnet';
|
$rootScope.wallet.getNetworkName = sinon.stub().returns('testnet');
|
||||||
form.address.$setViewValue('mkfTyEk7tfgV611Z4ESwDDSZwhsZdbMpVy');
|
form.address.$setViewValue('mkfTyEk7tfgV611Z4ESwDDSZwhsZdbMpVy');
|
||||||
expect(form.address.$invalid).to.equal(false);
|
expect(form.address.$invalid).to.equal(false);
|
||||||
});
|
}));
|
||||||
it('should not validate with other network', function() {
|
it('should not validate with other network', inject(function($rootScope) {
|
||||||
config.networkName = 'livenet';
|
$rootScope.wallet.getNetworkName = sinon.stub().returns('livenet');
|
||||||
form.address.$setViewValue('mkfTyEk7tfgV611Z4ESwDDSZwhsZdbMpVy');
|
form.address.$setViewValue('mkfTyEk7tfgV611Z4ESwDDSZwhsZdbMpVy');
|
||||||
expect(form.address.$invalid).to.equal(true);
|
expect(form.address.$invalid).to.equal(true);
|
||||||
});
|
}));
|
||||||
it('should not validate random', function() {
|
it('should not validate random', function() {
|
||||||
form.address.$setViewValue('thisisaninvalidaddress');
|
form.address.$setViewValue('thisisaninvalidaddress');
|
||||||
expect(form.address.$invalid).to.equal(true);
|
expect(form.address.$invalid).to.equal(true);
|
||||||
|
|
@ -94,9 +100,12 @@ describe("Unit: Testing Directives", function() {
|
||||||
|
|
||||||
describe('Unit: BTC', function() {
|
describe('Unit: BTC', function() {
|
||||||
beforeEach(inject(function($compile, $rootScope) {
|
beforeEach(inject(function($compile, $rootScope) {
|
||||||
config.unitToSatoshi = 100000000;
|
|
||||||
config.unitName = 'BTC';
|
|
||||||
$scope = $rootScope;
|
$scope = $rootScope;
|
||||||
|
var w = new FakeWallet(walletConfig);
|
||||||
|
w.settings.unitToSatoshi = 100000000;
|
||||||
|
w.settings.unitName = 'BTC';
|
||||||
|
$rootScope.wallet = w;
|
||||||
|
|
||||||
$rootScope.availableBalance = 0.04;
|
$rootScope.availableBalance = 0.04;
|
||||||
var element = angular.element(
|
var element = angular.element(
|
||||||
'<form name="form">' +
|
'<form name="form">' +
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,15 @@
|
||||||
describe('Unit: Testing Filters', function() {
|
describe('Unit: Testing Filters', function() {
|
||||||
|
|
||||||
beforeEach(module('copayApp.filters'));
|
beforeEach(module('copayApp.filters'));
|
||||||
|
var walletConfig = {
|
||||||
|
requiredCopayers: 3,
|
||||||
|
totalCopayers: 5,
|
||||||
|
spendUnconfirmed: 1,
|
||||||
|
reconnectDelay: 100,
|
||||||
|
networkName: 'testnet',
|
||||||
|
alternativeName: 'lol currency',
|
||||||
|
alternativeIsoCode: 'LOL'
|
||||||
|
};
|
||||||
|
|
||||||
describe('limitAddress', function() {
|
describe('limitAddress', function() {
|
||||||
|
|
||||||
|
|
@ -103,68 +112,76 @@ describe('Unit: Testing Filters', function() {
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('noFractionNumber bits', function() {
|
describe('noFractionNumber', function() {
|
||||||
beforeEach(function() {
|
describe('noFractionNumber bits', function() {
|
||||||
config.unitToSatoshi = 100;
|
beforeEach(inject(function($rootScope) {
|
||||||
config.unitName = 'bits';
|
$rootScope.wallet = new FakeWallet(walletConfig);
|
||||||
|
var w = $rootScope.wallet;
|
||||||
|
w.settings.unitToSatoshi = 100;
|
||||||
|
w.settings.unitName = 'bits';
|
||||||
|
}));
|
||||||
|
it('should format number to display correctly', inject(function($filter) {
|
||||||
|
var noFraction = $filter('noFractionNumber');
|
||||||
|
expect(noFraction(3100)).to.equal('3,100');
|
||||||
|
expect(noFraction(3100200)).to.equal('3,100,200');
|
||||||
|
expect(noFraction(3)).to.equal('3');
|
||||||
|
expect(noFraction(0.3)).to.equal(0.3);
|
||||||
|
expect(noFraction(0.30000000)).to.equal(0.3);
|
||||||
|
expect(noFraction(3200.01)).to.equal('3,200.01');
|
||||||
|
expect(noFraction(3200890.010000)).to.equal('3,200,890.01');
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
it('should format number to display correctly', inject(function($filter) {
|
|
||||||
var noFraction = $filter('noFractionNumber');
|
|
||||||
expect(noFraction(3100)).to.equal('3,100');
|
|
||||||
expect(noFraction(3100200)).to.equal('3,100,200');
|
|
||||||
expect(noFraction(3)).to.equal('3');
|
|
||||||
expect(noFraction(0.3)).to.equal(0.3);
|
|
||||||
expect(noFraction(0.30000000)).to.equal(0.3);
|
|
||||||
expect(noFraction(3200.01)).to.equal('3,200.01');
|
|
||||||
expect(noFraction(3200890.010000)).to.equal('3,200,890.01');
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('noFractionNumber BTC', function() {
|
describe('noFractionNumber BTC', function() {
|
||||||
beforeEach(function() {
|
beforeEach(inject(function($rootScope) {
|
||||||
config.unitToSatoshi = 100000000;
|
$rootScope.wallet = new FakeWallet(walletConfig);
|
||||||
config.unitName = 'BTC';
|
var w = $rootScope.wallet;
|
||||||
|
w.settings.unitToSatoshi = 100000000;
|
||||||
|
w.settings.unitName = 'BTC';
|
||||||
|
}));
|
||||||
|
it('should format number to display correctly', inject(function($filter) {
|
||||||
|
var noFraction = $filter('noFractionNumber');
|
||||||
|
expect(noFraction(0.30000000)).to.equal(0.3);
|
||||||
|
expect(noFraction(0.00302000)).to.equal(0.00302);
|
||||||
|
expect(noFraction(1.00000001)).to.equal(1.00000001);
|
||||||
|
expect(noFraction(3.10000012)).to.equal(3.10000012);
|
||||||
|
expect(noFraction(0.00100000)).to.equal(0.001);
|
||||||
|
expect(noFraction(0.00100009)).to.equal(0.00100009);
|
||||||
|
expect(noFraction(2000.00312011)).to.equal('2,000.00312011');
|
||||||
|
expect(noFraction(2000998.00312011)).to.equal('2,000,998.00312011');
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
it('should format number to display correctly', inject(function($filter) {
|
|
||||||
var noFraction = $filter('noFractionNumber');
|
|
||||||
expect(noFraction(0.30000000)).to.equal(0.3);
|
|
||||||
expect(noFraction(0.00302000)).to.equal(0.00302);
|
|
||||||
expect(noFraction(1.00000001)).to.equal(1.00000001);
|
|
||||||
expect(noFraction(3.10000012)).to.equal(3.10000012);
|
|
||||||
expect(noFraction(0.00100000)).to.equal(0.001);
|
|
||||||
expect(noFraction(0.00100009)).to.equal(0.00100009);
|
|
||||||
expect(noFraction(2000.00312011)).to.equal('2,000.00312011');
|
|
||||||
expect(noFraction(2000998.00312011)).to.equal('2,000,998.00312011');
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('noFractionNumber mBTC', function() {
|
describe('noFractionNumber mBTC', function() {
|
||||||
beforeEach(function() {
|
beforeEach(inject(function($rootScope) {
|
||||||
config.unitToSatoshi = 100000;
|
$rootScope.wallet = new FakeWallet(walletConfig);
|
||||||
config.unitName = 'mBTC';
|
var w = $rootScope.wallet;
|
||||||
|
w.settings.unitToSatoshi = 100000;
|
||||||
|
w.settings.unitName = 'mBTC';
|
||||||
|
}));
|
||||||
|
it('should format number to display correctly', inject(function($filter) {
|
||||||
|
var noFraction = $filter('noFractionNumber');
|
||||||
|
expect(noFraction(0.30000)).to.equal(0.3);
|
||||||
|
expect(noFraction(0.00302)).to.equal(0.00302);
|
||||||
|
expect(noFraction(1.00001)).to.equal(1.00001);
|
||||||
|
expect(noFraction(3.10002)).to.equal(3.10002);
|
||||||
|
expect(noFraction(0.00100000)).to.equal(0.001);
|
||||||
|
expect(noFraction(0.00100009)).to.equal(0.001);
|
||||||
|
expect(noFraction(2000.00312)).to.equal('2,000.00312');
|
||||||
|
expect(noFraction(2000998.00312)).to.equal('2,000,998.00312');
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
it('should format number to display correctly', inject(function($filter) {
|
|
||||||
var noFraction = $filter('noFractionNumber');
|
|
||||||
expect(noFraction(0.30000)).to.equal(0.3);
|
|
||||||
expect(noFraction(0.00302)).to.equal(0.00302);
|
|
||||||
expect(noFraction(1.00001)).to.equal(1.00001);
|
|
||||||
expect(noFraction(3.10002)).to.equal(3.10002);
|
|
||||||
expect(noFraction(0.00100000)).to.equal(0.001);
|
|
||||||
expect(noFraction(0.00100009)).to.equal(0.001);
|
|
||||||
expect(noFraction(2000.00312)).to.equal('2,000.00312');
|
|
||||||
expect(noFraction(2000998.00312)).to.equal('2,000,998.00312');
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('noFractionNumber:custom fractionSize', function() {
|
describe('noFractionNumber:custom fractionSize', function() {
|
||||||
it('should format number to display correctly', inject(function($filter) {
|
it('should format number to display correctly', inject(function($filter) {
|
||||||
var noFraction = $filter('noFractionNumber');
|
var noFraction = $filter('noFractionNumber');
|
||||||
expect(noFraction(0.30000, 0)).to.equal('0');
|
expect(noFraction(0.30000, 0)).to.equal('0');
|
||||||
expect(noFraction(1.00001, 0)).to.equal('1');
|
expect(noFraction(1.00001, 0)).to.equal('1');
|
||||||
expect(noFraction(3.10002, 0)).to.equal('3');
|
expect(noFraction(3.10002, 0)).to.equal('3');
|
||||||
expect(noFraction(2000.00312, 0)).to.equal('2,000');
|
expect(noFraction(2000.00312, 0)).to.equal('2,000');
|
||||||
expect(noFraction(2000998.00312, 0)).to.equal('2,000,998');
|
expect(noFraction(2000998.00312, 0)).to.equal('2,000,998');
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -4,19 +4,7 @@
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
var sinon = require('sinon');
|
var sinon = require('sinon');
|
||||||
|
var preconditions = require('preconditions').singleton();
|
||||||
beforeEach(function() {
|
|
||||||
config.unitToSatoshi = 100;
|
|
||||||
config.unitName = 'bits';
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Check config', function() {
|
|
||||||
|
|
||||||
it('unit should be set to BITS in config.js', function() {
|
|
||||||
expect(config.unitToSatoshi).to.equal(100);
|
|
||||||
expect(config.unitName).to.equal('bits');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("Unit: Walletfactory Service", function() {
|
describe("Unit: Walletfactory Service", function() {
|
||||||
beforeEach(angular.mock.module('copayApp.services'));
|
beforeEach(angular.mock.module('copayApp.services'));
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,9 @@ var getCommitHash = function() {
|
||||||
//exec git command to get the hash of the current commit
|
//exec git command to get the hash of the current commit
|
||||||
//git rev-parse HEAD
|
//git rev-parse HEAD
|
||||||
|
|
||||||
var hash = shell.exec('git rev-parse HEAD',{silent:true}).output.trim().substr(0,7);
|
var hash = shell.exec('git rev-parse HEAD', {
|
||||||
|
silent: true
|
||||||
|
}).output.trim().substr(0, 7);
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -23,7 +25,7 @@ var createVersion = function() {
|
||||||
var json = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
|
var json = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
|
||||||
var content = 'module.exports.version="' + json.version + '";';
|
var content = 'module.exports.version="' + json.version + '";';
|
||||||
|
|
||||||
content = content + '\nmodule.exports.commitHash="' + getCommitHash() + '";';
|
content = content + '\nmodule.exports.commitHash="' + getCommitHash() + '";';
|
||||||
fs.writeFileSync("./version.js", content);
|
fs.writeFileSync("./version.js", content);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -43,9 +45,9 @@ var createBundle = function(opts) {
|
||||||
b.require('browser-request', {
|
b.require('browser-request', {
|
||||||
expose: 'request'
|
expose: 'request'
|
||||||
});
|
});
|
||||||
b.require('underscore', {
|
b.require('underscore');
|
||||||
expose: 'underscore'
|
b.require('assert');
|
||||||
});
|
b.require('preconditions');
|
||||||
|
|
||||||
b.require('./copay', {
|
b.require('./copay', {
|
||||||
expose: 'copay'
|
expose: 'copay'
|
||||||
|
|
@ -130,10 +132,10 @@ if (require.main === module) {
|
||||||
};
|
};
|
||||||
var program = require('commander');
|
var program = require('commander');
|
||||||
program
|
program
|
||||||
.version('0.0.1')
|
.version('0.0.1')
|
||||||
.option('-d, --debug', 'Development. Don\'t minify the codem and include debug packages.')
|
.option('-d, --debug', 'Development. Don\'t minify the codem and include debug packages.')
|
||||||
.option('-o, --stdout', 'Specify output as stdout')
|
.option('-o, --stdout', 'Specify output as stdout')
|
||||||
.parse(process.argv);
|
.parse(process.argv);
|
||||||
|
|
||||||
createVersion();
|
createVersion();
|
||||||
var copayBundle = createBundle(program);
|
var copayBundle = createBundle(program);
|
||||||
|
|
|
||||||
|
|
@ -7,36 +7,36 @@
|
||||||
|
|
||||||
<div class="large-12 medium-12" ng-if="!!(addresses|removeEmpty).length">
|
<div class="large-12 medium-12" ng-if="!!(addresses|removeEmpty).length">
|
||||||
<div class="large-12 medium-12" ng-init="showAll=0">
|
<div class="large-12 medium-12" ng-init="showAll=0">
|
||||||
<div class="panel radius oh" ng-repeat="addr in addresses|removeEmpty|limitAddress:showAll">
|
<div class="panel radius oh" ng-repeat="addr in addresses|removeEmpty|limitAddress:showAll">
|
||||||
<div class="row collapse">
|
<div class="row collapse">
|
||||||
<div class="large-10 medium-9 small-8 column" >
|
<div class="large-10 medium-9 small-8 column">
|
||||||
<div class="ellipsis list-addr">
|
<div class="ellipsis list-addr">
|
||||||
<i class="fi-thumbnails size-48 show-for-large-up" ng-click="openAddressModal(addr)"> </i>
|
<i class="fi-thumbnails size-48 show-for-large-up" ng-click="openAddressModal(addr)"> </i>
|
||||||
<span>
|
<span>
|
||||||
<contact address="{{addr.address}}" tooltip-popup-delay="500" tooltip tooltip-placement="right"/>
|
<contact address="{{addr.address}}" tooltip-popup-delay="500" tooltip tooltip-placement="right"/>
|
||||||
</span>
|
</span>
|
||||||
<span class="btn-copy" clip-copy="addr.address"> </span>
|
<span class="btn-copy" clip-copy="addr.address"> </span>
|
||||||
<small translate class="label" ng-if="addr.isChange">change</small>
|
<small translate class="label" ng-if="addr.isChange">change</small>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="large-2 medium-3 small-4 column text-right">
|
|
||||||
<span ng-if="$root.updatingBalance">
|
|
||||||
<i class="fi-bitcoin-circle icon-rotate spinner"></i>
|
|
||||||
</span>
|
|
||||||
<span class="size-12" ng-if="!$root.updatingBalance">
|
|
||||||
{{addr.balance || 0|noFractionNumber}} {{$root.unitName}}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="large-2 medium-3 small-4 column text-right">
|
||||||
|
<span ng-if="$root.updatingBalance">
|
||||||
|
<i class="fi-bitcoin-circle icon-rotate spinner"></i>
|
||||||
|
</span>
|
||||||
|
<span class="size-12" ng-if="!$root.updatingBalance">
|
||||||
|
{{addr.balance || 0|noFractionNumber}} {{$root.wallet.settings.unitName}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a class="secondary radius" ng-click="showAll=!showAll" ng-show="(addresses|removeEmpty).length != (addresses|removeEmpty|limitAddress).length">
|
</div>
|
||||||
<span translate ng-if="!showAll">Show all</span>
|
|
||||||
<span translate ng-if="showAll">Show less</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<a class="secondary radius" ng-click="showAll=!showAll" ng-show="(addresses|removeEmpty).length != (addresses|removeEmpty|limitAddress).length">
|
||||||
|
<span translate ng-if="!showAll">Show all</span>
|
||||||
|
<span translate ng-if="showAll">Show less</span>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,40 +25,29 @@
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="walletPassword"><span translate>Your Wallet Password</span>
|
<label for="walletPassword"><span translate>Your Wallet Password</span>
|
||||||
<small translate data-options="disable_for_touch:true" class="has-tip text-gray" tooltip="doesn't need to be shared" >Required</small>
|
<small translate data-options="disable_for_touch:true" class="has-tip text-gray" tooltip="doesn't need to be shared">Required</small>
|
||||||
</label>
|
</label>
|
||||||
<input id="walletPassword" type="password"
|
<input id="walletPassword" type="password" placeholder="{{'Choose your password'|translate}}" class="form-control" ng-model="$parent.walletPassword" name="walletPassword" check-strength="passwordStrength" tooltip-html-unsafe="Password strength:
|
||||||
placeholder="{{'Choose your password'|translate}}" class="form-control"
|
|
||||||
ng-model="$parent.walletPassword"
|
|
||||||
name="walletPassword"
|
|
||||||
check-strength="passwordStrength"
|
|
||||||
tooltip-html-unsafe="Password strength:
|
|
||||||
<i>{{passwordStrength}}</i><br/><span
|
<i>{{passwordStrength}}</i><br/><span
|
||||||
class='size-12'>Tip: Use lower and uppercase, numbers and
|
class='size-12'>Tip: Use lower and uppercase, numbers and
|
||||||
symbols</span>"
|
symbols</span>" tooltip-trigger="focus" required tooltip-placement="top">
|
||||||
tooltip-trigger="focus" required
|
|
||||||
tooltip-placement="top">
|
|
||||||
|
|
||||||
<input type="password"
|
<input type="password" placeholder="{{'Repeat password'|translate}}" name="walletPasswordConfirm" ng-model="walletPasswordConfirm" match="walletPassword" required>
|
||||||
placeholder="{{'Repeat password'|translate}}"
|
|
||||||
name="walletPasswordConfirm"
|
<div class="text-left">
|
||||||
ng-model="walletPasswordConfirm"
|
<input id="network-name" type="checkbox" ng-model="networkName" ng-true-value="testnet" ng-false-value="livenet" class="form-control" ng-click="changeNetwork()" ng-checked="networkName == 'testnet' ? true : false">
|
||||||
match="walletPassword"
|
<label for="network-name">Use test network</label>
|
||||||
required>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a class="expand small" ng-click="hideAdv=!hideAdv">
|
<a class="expand small" ng-click="hideAdv=!hideAdv">
|
||||||
<span translate ng-hide="!hideAdv">Show</span>
|
<span translate ng-hide="!hideAdv">Show</span>
|
||||||
<span translate ng-hide="hideAdv">Hide</span>
|
<span translate ng-hide="hideAdv">Hide</span>
|
||||||
<span translate>advanced options</span>
|
<span translate>advanced options</span>
|
||||||
</a>
|
</a>
|
||||||
<div ng-hide="hideAdv">
|
<div ng-hide="hideAdv">
|
||||||
<p>
|
<p>
|
||||||
<input type="text"
|
<input type="text" placeholder="{{'Private Key (Hex)'|translate}}" name="private" ng-model="private">
|
||||||
placeholder="{{'Private Key (Hex)'|translate}}"
|
|
||||||
name="private"
|
|
||||||
ng-model="private"
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -78,24 +67,17 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="box-setup-copayers" ng-show="!isSetupWalletPage">
|
<div class="box-setup-copayers" ng-show="!isSetupWalletPage">
|
||||||
<div class="box-setup-copayers p10">
|
<div class="box-setup-copayers p10">
|
||||||
<img class="br100 oh box-setup-copay m10" ng-repeat="i in getNumber(totalCopayers) track by $index"
|
<img class="br100 oh box-setup-copay m10" ng-repeat="i in getNumber(totalCopayers) track by $index" src="./img/satoshi.gif" title="Copayer {{$index+1}}-{{totalCopayers}}" ng-class="{'box-setup-copay-required': ($index+1) <= requiredCopayers}" width="50px">
|
||||||
src="./img/satoshi.gif"
|
|
||||||
title="Copayer {{$index+1}}-{{totalCopayers}}"
|
|
||||||
ng-class="{'box-setup-copay-required': ($index+1) <= requiredCopayers}"
|
|
||||||
width="50px">
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p translate class="comment" ng-show="totalCopayers>1 && !isSetupWalletPage">(*) The limits are imposed by the bitcoin network.</p>
|
<p translate class="comment" ng-show="totalCopayers>1 && !isSetupWalletPage">(*) The limits are imposed by the bitcoin network.</p>
|
||||||
<div class="text-right">
|
<div class="text-right">
|
||||||
<a ng-show="!isSetupWalletPage" class="back-button m20r"
|
<a ng-show="!isSetupWalletPage" class="back-button m20r" href="#!/">« <span translate>Back</span></a>
|
||||||
href="#!/">« <span translate>Back</span></a>
|
<a ng-show="isSetupWalletPage" class="back-button m20r" ng-click="setupWallet()">« <span translate>Back</span></a>
|
||||||
<a ng-show="isSetupWalletPage" class="back-button m20r"
|
|
||||||
ng-click="setupWallet()">« <span translate>Back</span></a>
|
|
||||||
<button translate ng-show="isSetupWalletPage" type="submit" class="button secondary m0" ng-disabled="setupForm.$invalid || loading">
|
<button translate ng-show="isSetupWalletPage" type="submit" class="button secondary m0" ng-disabled="setupForm.$invalid || loading">
|
||||||
Create {{requiredCopayers}}-of-{{totalCopayers}} wallet
|
Create {{requiredCopayers}}-of-{{totalCopayers}} wallet
|
||||||
</button>
|
</button>
|
||||||
<a translate class="button secondary m0" ng-show="!isSetupWalletPage"
|
<a translate class="button secondary m0" ng-show="!isSetupWalletPage" ng-click="setupWallet()">Next</a>
|
||||||
ng-click="setupWallet()">Next</a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -103,4 +85,3 @@
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,41 @@
|
||||||
</a>
|
</a>
|
||||||
<div ng-include="'views/includes/version.html'"></div>
|
<div ng-include="'views/includes/version.html'"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="line-sidebar-b"></div>
|
||||||
|
<div>
|
||||||
|
<div ng-if="$root.wallet" class="founds size-12 text-center box-founds p10t">
|
||||||
|
<div class="m10b">
|
||||||
|
Balance
|
||||||
|
<span ng-if="$root.updatingBalance">
|
||||||
|
<i class="fi-bitcoin-circle icon-rotate spinner"></i>
|
||||||
|
</span>
|
||||||
|
<span ng-if="$root.wallet && !$root.updatingBalance"
|
||||||
|
data-options="disable_for_touch:true"
|
||||||
|
tooltip="{{totalBalanceBTC |noFractionNumber:8}} BTC"
|
||||||
|
tooltip-trigger="mouseenter"
|
||||||
|
tooltip-placement="bottom">{{totalBalance || 0
|
||||||
|
|noFractionNumber}} {{$root.wallet.settings.unitName}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Locked
|
||||||
|
<span ng-if="$root.updatingBalance">
|
||||||
|
<i class="fi-bitcoin-circle icon-rotate spinner"></i>
|
||||||
|
</span>
|
||||||
|
<span ng-if="$root.wallet && !$root.updatingBalance"
|
||||||
|
data-options="disable_for_touch:true"
|
||||||
|
tooltip="{{lockedBalanceBTC |noFractionNumber:8}} BTC"
|
||||||
|
tooltip-trigger="mouseenter"
|
||||||
|
tooltip-placement="bottom">{{lockedBalance || 0|noFractionNumber}} {{$root.wallet.settings.unitName}}
|
||||||
|
</span> <i class="fi-info medium" tooltip="Balance locked in pending transaction proposals" tooltip-placement="bottom"></i>
|
||||||
|
</div>
|
||||||
|
<div class="line-sidebar-b"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</header>
|
</header>
|
||||||
<div class="line-sidebar-b"></div>
|
<div class="line-sidebar-b"></div>
|
||||||
<div class="founds size-12 box-founds p15" ng-disabled="$root.loading" ng-click="refresh()">
|
<div ng-if="$root.wallet" class="founds size-12 box-founds p15" ng-disabled="$root.loading" ng-click="refresh()">
|
||||||
<p class="text-gray">
|
<p class="text-gray">
|
||||||
<span>{{$root.wallet.getName()}}</span>
|
<span>{{$root.wallet.getName()}}</span>
|
||||||
<span class="size-12 right">{{$root.wallet.requiredCopayers}}-of-{{$root.wallet.totalCopayers}}</span>
|
<span class="size-12 right">{{$root.wallet.requiredCopayers}}-of-{{$root.wallet.totalCopayers}}</span>
|
||||||
|
|
@ -20,7 +52,7 @@
|
||||||
<i class="fi-bitcoin-circle icon-rotate spinner"></i>
|
<i class="fi-bitcoin-circle icon-rotate spinner"></i>
|
||||||
</span>
|
</span>
|
||||||
<span ng-if="!$root.updatingBalance">{{totalBalance || 0
|
<span ng-if="!$root.updatingBalance">{{totalBalance || 0
|
||||||
|noFractionNumber}} {{$root.unitName}}
|
|noFractionNumber}} {{$root.wallet.settings.unitName}}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="m10t" ng-show="lockedBalance">
|
<div class="m10t" ng-show="lockedBalance">
|
||||||
|
|
@ -28,7 +60,7 @@
|
||||||
<span ng-if="$root.updatingBalance">
|
<span ng-if="$root.updatingBalance">
|
||||||
<i class="fi-bitcoin-circle icon-rotate spinner"></i>
|
<i class="fi-bitcoin-circle icon-rotate spinner"></i>
|
||||||
</span>
|
</span>
|
||||||
<span ng-show="!$root.updatingBalance">{{lockedBalance || 0|noFractionNumber}} {{$root.unitName}}
|
<span ng-show="!$root.updatingBalance">{{lockedBalance || 0|noFractionNumber}} {{$root.wallet.settings.unitName}}
|
||||||
</span> <i class="fi-info medium" tooltip="{{'Balance locked in pending transaction proposals'|translate}}" tooltip-placement="bottom"></i>
|
</span> <i class="fi-info medium" tooltip="{{'Balance locked in pending transaction proposals'|translate}}" tooltip-placement="bottom"></i>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -20,26 +20,26 @@
|
||||||
<span ng-if="$root.updatingBalance">
|
<span ng-if="$root.updatingBalance">
|
||||||
<i class="fi-bitcoin-circle icon-rotate spinner"></i>
|
<i class="fi-bitcoin-circle icon-rotate spinner"></i>
|
||||||
</span>
|
</span>
|
||||||
<span ng-if="!$root.updatingBalance"
|
<span ng-if="$root.wallet && !$root.updatingBalance"
|
||||||
class="has-tip"
|
class="has-tip"
|
||||||
data-options="disable_for_touch:true"
|
data-options="disable_for_touch:true"
|
||||||
tooltip-popup-delay='500'
|
tooltip-popup-delay='500'
|
||||||
tooltip="{{totalBalanceAlternative |noFractionNumber:2}} {{alternativeIsoCode}}"
|
tooltip="{{totalBalanceAlternative |noFractionNumber:2}} {{alternativeIsoCode}}"
|
||||||
tooltip-trigger="mouseenter"
|
tooltip-trigger="mouseenter"
|
||||||
tooltip-placement="bottom">{{totalBalance || 0 |noFractionNumber}} {{$root.unitName}}
|
tooltip-placement="bottom">{{totalBalance || 0 |noFractionNumber}} {{$root.wallet.settings.unitName}}
|
||||||
</span>
|
</span>
|
||||||
<div class="m10t" ng-show="lockedBalance">
|
<div class="m10t" ng-show="lockedBalance">
|
||||||
<span translate>Locked</span>
|
<span translate>Locked</span>
|
||||||
<span ng-if="$root.updatingBalance">
|
<span ng-if="$root.updatingBalance">
|
||||||
<i class="fi-bitcoin-circle icon-rotate spinner"></i>
|
<i class="fi-bitcoin-circle icon-rotate spinner"></i>
|
||||||
</span>
|
</span>
|
||||||
<span ng-show="!$root.updatingBalance"
|
<span ng-if="$root.wallet && !$root.updatingBalance"
|
||||||
class="has-tip"
|
class="has-tip"
|
||||||
data-options="disable_for_touch:true"
|
data-options="disable_for_touch:true"
|
||||||
tooltip-popup-delay='500'
|
tooltip-popup-delay='500'
|
||||||
tooltip="{{lockedBalanceAlternative |noFractionNumber:2}} {{alternativeIsoCode}}"
|
tooltip="{{lockedBalanceAlternative |noFractionNumber:2}} {{alternativeIsoCode}}"
|
||||||
tooltip-trigger="mouseenter"
|
tooltip-trigger="mouseenter"
|
||||||
tooltip-placement="bottom">{{lockedBalance || 0|noFractionNumber}} {{$root.unitName}}
|
tooltip-placement="bottom">{{lockedBalance || 0|noFractionNumber}} {{$root.wallet.settings.unitName}}
|
||||||
</span> <i class="fi-info medium" tooltip="{{'Balance locked in pending transaction proposals'|translate}}" tooltip-placement="bottom"></i>
|
</span> <i class="fi-info medium" tooltip="{{'Balance locked in pending transaction proposals'|translate}}" tooltip-placement="bottom"></i>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -8,20 +8,19 @@
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="show-for-small-only small-12 columns m10b" ng-show="tx.comment">
|
<div class="show-for-small-only small-12 columns m10b" ng-show="tx.comment">
|
||||||
<p class="size-14 label" >
|
<p class="size-14 label">
|
||||||
{{tx.comment}} -
|
{{tx.comment}} - {{$root.wallet.publicKeyRing.nicknameForCopayer(tx.creator)}}
|
||||||
{{$root.wallet.publicKeyRing.nicknameForCopayer(tx.creator)}}
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="large-8 medium-8 small-8 columns">
|
<div class="large-8 medium-8 small-8 columns">
|
||||||
<div ng-repeat="out in tx.outs">
|
<div ng-repeat="out in tx.outs">
|
||||||
<div class="large-3 medium-3 small-3 columns">
|
<div class="large-3 medium-3 small-3 columns">
|
||||||
<p class="size-14 hide-for-small-only">{{out.value | noFractionNumber}} {{$root.unitName}}</p>
|
<p class="size-14 hide-for-small-only">{{out.value | noFractionNumber}} {{$root.wallet.settings.unitName}}</p>
|
||||||
<p class="size-12 show-for-small-only">{{out.value | noFractionNumber}} {{$root.unitName}}</p>
|
<p class="size-12 show-for-small-only">{{out.value | noFractionNumber}} {{$root.wallet.settings.unitName}}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="large-1 medium-1 small-2 columns fi-arrow-right"> </div>
|
<div class="large-1 medium-1 small-2 columns fi-arrow-right"></div>
|
||||||
<div class="large-8 medium-8 small-7 columns ellipsis">
|
<div class="large-8 medium-8 small-7 columns ellipsis">
|
||||||
<contact address="{{out.address}}" tooltip-popup-delay="500" tooltip tooltip-placement="right"/>
|
<contact address="{{out.address}}" tooltip-popup-delay="500" tooltip tooltip-placement="right" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -37,25 +36,25 @@
|
||||||
</a>
|
</a>
|
||||||
<div class="box-status">
|
<div class="box-status">
|
||||||
<a ng-if="c.actions.create" tooltip-popup-delay="1000" tooltip="Created {{c.actions.create | amTimeAgo}}">
|
<a ng-if="c.actions.create" tooltip-popup-delay="1000" tooltip="Created {{c.actions.create | amTimeAgo}}">
|
||||||
<i class="fi-crown icon-status icon-active"></i>
|
<i class="fi-crown icon-status icon-active"></i>
|
||||||
</a>
|
</a>
|
||||||
<a ng-if="!c.actions.create"><i class="fi-crown icon-status"></i></a>
|
<a ng-if="!c.actions.create"><i class="fi-crown icon-status"></i></a>
|
||||||
|
|
||||||
<a ng-if="c.actions.seen" tooltip-popup-delay="1000" tooltip="Seen {{c.actions.seen | amTimeAgo}}">
|
<a ng-if="c.actions.seen" tooltip-popup-delay="1000" tooltip="Seen {{c.actions.seen | amTimeAgo}}">
|
||||||
<i class="fi-eye icon-status icon-active"></i>
|
<i class="fi-eye icon-status icon-active"></i>
|
||||||
</a>
|
</a>
|
||||||
<a ng-if="!c.actions.seen"><i class="fi-eye icon-status"></i></a>
|
<a ng-if="!c.actions.seen"><i class="fi-eye icon-status"></i></a>
|
||||||
|
|
||||||
<a ng-if="c.actions.rejected" tooltip-popup-delay="1000" tooltip="Rejected {{c.actions.rejected | amTimeAgo}}">
|
<a ng-if="c.actions.rejected" tooltip-popup-delay="1000" tooltip="Rejected {{c.actions.rejected | amTimeAgo}}">
|
||||||
<i class="fi-x icon-status icon-active-x"></i>
|
<i class="fi-x icon-status icon-active-x"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a ng-if="c.actions.sign" tooltip-popup-delay="1000" tooltip="Signed {{c.actions.sign | amTimeAgo}}">
|
<a ng-if="c.actions.sign" tooltip-popup-delay="1000" tooltip="Signed {{c.actions.sign | amTimeAgo}}">
|
||||||
<i class="fi-check icon-status icon-active-check"></i>
|
<i class="fi-check icon-status icon-active-check"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a ng-if="!c.actions.sign && !c.actions.rejected && tx.missingSignatures" class="icon-status">
|
<a ng-if="!c.actions.sign && !c.actions.rejected && tx.missingSignatures" class="icon-status">
|
||||||
<i class="fi-loop icon-rotate"></i>
|
<i class="fi-loop icon-rotate"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -100,7 +99,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div ng-show="!tx.missingSignatures && tx.sentTs">
|
<div ng-show="!tx.missingSignatures && tx.sentTs">
|
||||||
<div class="is-valid m10b">
|
<div class="is-valid m10b">
|
||||||
<strong translate>Sent</strong> <span class="text-gray" am-time-ago="tx.sentTs"></span>
|
<strong translate>Sent</strong> <span class="text-gray" am-time-ago="tx.sentTs"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="ellipsis small">
|
<div class="ellipsis small">
|
||||||
<span translate>Transaction ID</span>:
|
<span translate>Transaction ID</span>:
|
||||||
|
|
@ -110,12 +109,12 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p translate class="text-gray m5b" ng-show="!tx.finallyRejected && tx.missingSignatures==1">
|
<p translate class="text-gray m5b" ng-show="!tx.finallyRejected && tx.missingSignatures==1">
|
||||||
One signature missing
|
One signature missing
|
||||||
</p>
|
</p>
|
||||||
<p translate class="text-gray m5b" ng-show="!tx.finallyRejected && tx.missingSignatures>1">
|
<p translate class="text-gray m5b" ng-show="!tx.finallyRejected && tx.missingSignatures>1">
|
||||||
{{tx.missingSignatures}} signatures missing</p>
|
{{tx.missingSignatures}} signatures missing</p>
|
||||||
<div class="ellipsis small text-gray">
|
<div class="ellipsis small text-gray">
|
||||||
<strong translate>Fee</strong>: {{tx.fee|noFractionNumber}} {{$root.unitName}}
|
<strong translate>Fee</strong>: {{tx.fee|noFractionNumber}} {{$root.wallet.settings.unitName}}
|
||||||
<strong translate>Proposal ID</strong>: {{tx.ntxid}}
|
<strong translate>Proposal ID</strong>: {{tx.ntxid}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<div ng-controller="VersionController">
|
<div ng-controller="VersionController">
|
||||||
<small>v{{version}} ({{defaultLanguage}})</small>
|
<small>v{{version}} ({{defaultLanguage}})</small>
|
||||||
<small>#{{commitHash}}</small>
|
<small>#{{commitHash}}</small>
|
||||||
<small ng-if="networkName=='testnet'">[ {{networkName}} ]</small>
|
<small ng-if="networkName ==='testnet' || networkName ==='livenet'">[ {{networkName}} ]</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
<i class="fi-bitcoin-circle icon-rotate spinner"></i>
|
<i class="fi-bitcoin-circle icon-rotate spinner"></i>
|
||||||
</span>
|
</span>
|
||||||
<p class="m15b" ng-if="!$root.updatingBalance">
|
<p class="m15b" ng-if="!$root.updatingBalance">
|
||||||
{{address.balance || 0|noFractionNumber}} {{$root.unitName}}
|
{{address.balance || 0|noFractionNumber}} {{$root.wallet.settings.unitName}}
|
||||||
</p>
|
</p>
|
||||||
<button class="m15t button secondary" open-external address="{{address.address}}">
|
<button class="m15t button secondary" open-external address="{{address.address}}">
|
||||||
<i class="fi-link"> </i> <span translate>Open in external application</span>
|
<i class="fi-link"> </i> <span translate>Open in external application</span>
|
||||||
|
|
|
||||||
|
|
@ -1,62 +1,79 @@
|
||||||
<div class="backup" ng-controller="MoreController">
|
<div class="backup" ng-controller="MoreController">
|
||||||
<h1 translate>Settings </h1>
|
<h1 translate>Settings </h1>
|
||||||
<div class="oh large-12 columns panel">
|
<div class="oh large-12 columns panel">
|
||||||
<h3><i class="fi-download m10r"></i> <span translate>Backup</span> </h3>
|
<h3><i class="fi-download m10r"></i> <span translate>Backup</span> </h3>
|
||||||
<p translate class="large-8 columns text-gray">It's important to backup your wallet so that you can recover it in case of disaster</p>
|
<p translate class="large-8 columns text-gray">It's important to backup your wallet so that you can recover it in case of disaster</p>
|
||||||
<div class="large-4 columns">
|
<div class="large-4 columns">
|
||||||
<a translate class="button primary expand" ng-click="downloadBackup()">Download File</a>
|
<a translate class="button primary expand" ng-click="downloadBackup()">Download File</a>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="large-12 columns line-dashed-h m15b"> </div>
|
</div>
|
||||||
<div>
|
<div class="large-12 columns line-dashed-h m15b"></div>
|
||||||
<div class="oh large-12 columns panel">
|
<div class="row collapse">
|
||||||
<h3><i class="fi-minus-circle m10r"></i>
|
<form name="settingsForm" class="large-6 small-12 columns">
|
||||||
<span translate>Delete Wallet</span> </h3>
|
<fieldset>
|
||||||
<p translate class="large-8 columns text-gray">If all funds have been removed from your wallet and you do not wish to have the wallet data stored on your computer anymore, you can delete your wallet.</p>
|
<legend translate>Wallet Unit</legend>
|
||||||
<div class="large-4 columns">
|
<select class="form-control" ng-model="selectedUnit" ng-options="o.name for o in unitOpts" required>
|
||||||
<a translate class="button warning expand"
|
</select>
|
||||||
ng-really-message="'Are you sure to delete this wallet from this computer?'|translate" ng-really-click="deleteWallet()"> Delete</a>
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<legend translate>Alternative Currency</legend>
|
||||||
|
<select class="form-control" ng-model="selectedAlternative" ng-options="alternative.name for alternative in alternativeOpts" required>
|
||||||
|
</select>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<div class="text-left">
|
||||||
|
<button translate type="submit" class="large-6 small-12 columns button primary m0 ng-binding" ng-disabled="setupForm.$invalid || loading" disabled="disabled" ng-click="save()">
|
||||||
|
Save
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="large-12 columns line-dashed-h m15b"></div>
|
||||||
|
<div class="oh large-12 columns panel">
|
||||||
|
<h3><i class="fi-minus-circle m10r"></i> <span translate> Delete Wallet </span></h3>
|
||||||
|
<p translate class="large-8 columns text-gray">If all funds have been removed from your wallet and you do not wish to have the wallet data stored on your computer anymore, you can delete your wallet.</p>
|
||||||
|
<div class="large-4 columns">
|
||||||
|
<a translate class="button warning expand" ng-really-message="'Are you sure to delete this wallet from this computer?'|translate" ng-really-click="deleteWallet()"> Delete</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<p>
|
||||||
<a class="expand small" ng-click="hideAdv=!hideAdv">
|
<a class="expand small" ng-click="hideAdv=!hideAdv">
|
||||||
<span translate ng-hide="!hideAdv">Show</span>
|
<span translate ng-hide="!hideAdv">Show</span>
|
||||||
<span translate ng-hide="hideAdv">Hide</span>
|
<span translate ng-hide="hideAdv">Hide</span>
|
||||||
<span translate>advanced options</span>
|
<span translate>advanced options</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|
||||||
<div ng-hide="hideAdv">
|
<div ng-hide="hideAdv">
|
||||||
<div class="oh large-12 columns panel">
|
<div class="oh large-12 columns panel">
|
||||||
<h3><i class="fi-minus-circle m10r"></i>
|
<h3><i class="fi-minus-circle m10r"></i>
|
||||||
<span translate>Master Private Key</span> </h3>
|
<span translate>Master Private Key</span> </h3>
|
||||||
<p translate class="large-8 columns text-gray">
|
<p translate class="large-8 columns text-gray">
|
||||||
Your master private key contains the information to sign <b>any</b> transaction on this wallet. Handle with care.
|
Your master private key contains the information to sign <b>any</b> transaction on this wallet. Handle with care.
|
||||||
</p>
|
</p>
|
||||||
<div class="large-4 columns">
|
<div class="large-4 columns">
|
||||||
<a class="button primary expand" ng-click="hidePriv=!hidePriv">
|
<a class="button primary expand" ng-click="hidePriv=!hidePriv">
|
||||||
<span translate ng-hide="!hidePriv">Show</span>
|
<span translate ng-hide="!hidePriv">Show</span>
|
||||||
<span translate ng-hide="hidePriv">Hide</span>
|
<span translate ng-hide="hidePriv">Hide</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<textarea ng-hide="hidePriv" readonly>{{priv}}</textarea>
|
<textarea ng-hide="hidePriv" readonly>{{priv}}</textarea>
|
||||||
</div>
|
</div>
|
||||||
<div class="oh large-12 columns panel">
|
<div class="oh large-12 columns panel">
|
||||||
<h3><i class="fi-minus-circle m10r"></i> <span translate>Scan Wallet Addresses</span> </h3>
|
<h3><i class="fi-minus-circle m10r"></i> <span translate>Scan Wallet Addresses</span> </h3>
|
||||||
<p translate class="large-8 columns text-gray">
|
<p translate class="large-8 columns text-gray">
|
||||||
This will scan the blockchain looking for addresses derived from your wallet, in case you have funds in addresses not yet generated (e.g.: you restored an old backup). This will also trigger a syncronization of addresses to other connected peers.
|
This will scan the blockchain looking for addresses derived from your wallet, in case you have funds in addresses not yet generated (e.g.: you restored an old backup). This will also trigger a syncronization of addresses to other connected peers.
|
||||||
</p>
|
</p>
|
||||||
<div class="large-4 columns">
|
<div class="large-4 columns">
|
||||||
<a translate class="button primary expand" ng-click="updateIndexes()">
|
<a translate class="button primary expand" ng-click="updateIndexes()">
|
||||||
Scan
|
Scan
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="oh large-12 columns panel">
|
<div class="oh large-12 columns panel">
|
||||||
<h3><i class="fi-minus-circle m10r"></i> <span translate>Purge Pending Transaction Proposals</span> </h3>
|
<h3><i class="fi-minus-circle m10r"></i> <span translate>Purge Pending Transaction Proposals</span> </h3>
|
||||||
<p translate class="large-8 columns text-gray">
|
<p translate class="large-8 columns text-gray">
|
||||||
Pending Transactions Proposals will be discarted. This need to be done on <b>ALL<b> peers of a wallet, to prevent the old proposals to be resynced again.
|
Pending Transactions Proposals will be discarted. This need to be done on <b>ALL<b> peers of a wallet, to prevent the old proposals to be resynced again.
|
||||||
</p>
|
</p>
|
||||||
<div class="large-4 columns">
|
<div class="large-4 columns">
|
||||||
<a translate class="button warning expand" ng-click="purge()">
|
<a translate class="button warning expand" ng-click="purge()">
|
||||||
|
|
@ -78,4 +95,3 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,14 +14,14 @@
|
||||||
<div class="row collapse">
|
<div class="row collapse">
|
||||||
<div class="large-12 columns">
|
<div class="large-12 columns">
|
||||||
<div class="row collapse">
|
<div class="row collapse">
|
||||||
<label for="address"><span translate>To address</span>
|
<label for="address"><span translate>To:</span>
|
||||||
<small translate ng-hide="!sendForm.address.$pristine || address">required</small>
|
<small translate ng-hide="!sendForm.address.$pristine || address">required</small>
|
||||||
<small translate class="is-valid" ng-show="!sendForm.address.$invalid && address">valid!</small>
|
<small translate class="is-valid" ng-show="!sendForm.address.$invalid && address">valid!</small>
|
||||||
<small translate class="has-error" ng-show="sendForm.address.$invalid && address">not valid</small>
|
<small translate class="has-error" ng-show="sendForm.address.$invalid && address">not valid</small>
|
||||||
</label>
|
</label>
|
||||||
<div class="small-10 columns">
|
<div class="small-10 columns">
|
||||||
<input type="text" id="address" name="address" ng-disabled="loading || !!$root.merchant"
|
<input type="text" id="address" name="address" ng-disabled="loading || !!$root.merchant"
|
||||||
placeholder="{{'Send to'|translate}}" ng-model="address" ng-change="onChanged()" valid-address required>
|
placeholder="{{'Bitcoin address'|translate}}" ng-model="address" ng-change="onChanged()" valid-address required>
|
||||||
<small class="icon-input" ng-show="!sendForm.address.$invalid && address"><i class="fi-check"></i></small>
|
<small class="icon-input" ng-show="!sendForm.address.$invalid && address"><i class="fi-check"></i></small>
|
||||||
<small class="icon-input" ng-show="sendForm.address.$invalid && address"><i class="fi-x"></i></small>
|
<small class="icon-input" ng-show="sendForm.address.$invalid && address"><i class="fi-x"></i></small>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -77,11 +77,11 @@
|
||||||
<a class="small input-note" title="{{'Send all funds'|translate}}"
|
<a class="small input-note" title="{{'Send all funds'|translate}}"
|
||||||
ng-show="$root.availableBalance > 0 && (!$root.merchant || +$root.merchant.total === 0)"
|
ng-show="$root.availableBalance > 0 && (!$root.merchant || +$root.merchant.total === 0)"
|
||||||
ng-click="topAmount(sendForm)">
|
ng-click="topAmount(sendForm)">
|
||||||
<span translate>Use all funds</span> ({{getAvailableAmount()}} {{$root.unitName}})
|
<span translate>Use all funds</span> ({{getAvailableAmount()}} {{$root.wallet.settings.unitName}})
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="small-3 columns">
|
<div class="small-3 columns">
|
||||||
<span class="postfix">{{$root.unitName}}</span>
|
<span class="postfix">{{$root.wallet.settings.unitName}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -138,13 +138,13 @@
|
||||||
</p>
|
</p>
|
||||||
<h6 translate>Total amount for this transaction:</h6>
|
<h6 translate>Total amount for this transaction:</h6>
|
||||||
<p class="text-gray" ng-class="{'hidden': sendForm.amount.$invalid || !amount > 0}">
|
<p class="text-gray" ng-class="{'hidden': sendForm.amount.$invalid || !amount > 0}">
|
||||||
<b>{{amount + defaultFee |noFractionNumber}}</b> {{$root.unitName}}
|
<b>{{amount + defaultFee |noFractionNumber}}</b> {{$root.wallet.settings.unitName}}
|
||||||
<small ng-if="isRateAvailable">
|
<small ng-if="isRateAvailable">
|
||||||
{{ rateService.toFiat((amount + defaultFee) * unitToSatoshi, alternativeIsoCode) | noFractionNumber: 2 }} {{ alternativeIsoCode }}
|
{{ rateService.toFiat((amount + defaultFee) * unitToSatoshi, alternativeIsoCode) | noFractionNumber: 2 }} {{ alternativeIsoCode }}
|
||||||
<br>
|
<br>
|
||||||
</small>
|
</small>
|
||||||
<small>
|
<small>
|
||||||
<span translate>Including fee of</span> {{defaultFee|noFractionNumber}} {{$root.unitName}}
|
<span translate>Including fee of</span> {{defaultFee|noFractionNumber}} {{$root.wallet.settings.unitName}}
|
||||||
</small>
|
</small>
|
||||||
</p>
|
</p>
|
||||||
<div ng-show="wallet.isShared()">
|
<div ng-show="wallet.isShared()">
|
||||||
|
|
|
||||||
|
|
@ -10,45 +10,20 @@
|
||||||
<form name="settingsForm">
|
<form name="settingsForm">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend translate>Language</legend>
|
<legend translate>Language</legend>
|
||||||
<select class="form-control" ng-model="selectedLanguage"
|
<select class="form-control" ng-model="selectedLanguage" ng-options="o.name for o in availableLanguages" required>
|
||||||
ng-options="o.name for o in availableLanguages" required>
|
|
||||||
</select>
|
</select>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend translate>Bitcoin Network</legend>
|
<legend translate>Insight API servers</legend>
|
||||||
<input id="network-name" type="checkbox" ng-model="networkName"
|
<label for="insight-livenet">Livenet</label>
|
||||||
ng-true-value="livenet" ng-false-value="testnet" class="form-control" ng-click="changeNetwork()"
|
<input type="text" ng-model="insightLivenet" class="form-control" name="insight-livenet">
|
||||||
ng-disabled="forceNetwork"
|
<label for="insight-testnet">Testnet</label>
|
||||||
ng-checked="networkName == 'livenet' ? true : false">
|
<input type="text" ng-model="insightTestnet" class="form-control" name="insight-testnet">
|
||||||
<label for="network-name">Livenet</label>
|
|
||||||
<div translate ng-show="forceNetwork">
|
|
||||||
Network has been fixed to <strong>{{networkName}}</strong> in this setup. See <a href="https://copay.io">copay.io</a> for options to use Copay on both livenet and testnet.
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
<fieldset>
|
|
||||||
<legend translate>Wallet Unit</legend>
|
|
||||||
<select class="form-control" ng-model="selectedUnit" ng-options="o.name for o in unitOpts" required>
|
|
||||||
</select>
|
|
||||||
</fieldset>
|
|
||||||
<fieldset>
|
|
||||||
<legend translate>Alternative Currency</legend>
|
|
||||||
<select class="form-control" ng-model="selectedAlternative" ng-options="alternative.name for alternative in alternativeOpts" required>
|
|
||||||
</select>
|
|
||||||
</fieldset>
|
|
||||||
<fieldset>
|
|
||||||
<legend translate>Insight API server</legend>
|
|
||||||
<label for="insight-host">Host</label>
|
|
||||||
<input type="text" ng-model="insightHost" class="form-control" name="insight-host">
|
|
||||||
<label for="insight-port" translate>Port</label>
|
|
||||||
<input type="number" ng-model="insightPort" class="form-control" name="insight-port">
|
|
||||||
<input id="insight-secure" type="checkbox" ng-model="insightSecure" class="form-control" ng-click="changeInsightSSL()">
|
|
||||||
<label for="insight-secure" translate>Use SSL</label>
|
|
||||||
|
|
||||||
<p translate class="small">
|
<p translate class="small">
|
||||||
Insight API server is open-source software. You can run your own instance, check <a href="http://insight.is" target="_blank">Insight API Homepage</a></p>
|
Insight API server is open-source software. You can run your own instances, check <a href="http://insight.is" target="_blank">Insight API Homepage</a>
|
||||||
|
</p>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<div class="text-right">
|
<div class="text-right">
|
||||||
<a class="back-button text-white m20r" href="#!/">« <span translate>Back</span></a>
|
<a class="back-button text-white m20r" href="#!/">« <span translate>Back</span></a>
|
||||||
<button translate type="submit" class="button primary m0 ng-binding" ng-disabled="setupForm.$invalid || loading" disabled="disabled" ng-click="save()">
|
<button translate type="submit" class="button primary m0 ng-binding" ng-disabled="setupForm.$invalid || loading" disabled="disabled" ng-click="save()">
|
||||||
|
|
@ -60,4 +35,3 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,8 @@
|
||||||
<div class="last-transactions" ng-repeat="tx in txs | paged">
|
<div class="last-transactions" ng-repeat="tx in txs | paged">
|
||||||
<div ng-include="'views/includes/transaction.html'"></div>
|
<div ng-include="'views/includes/transaction.html'"></div>
|
||||||
</div>
|
</div>
|
||||||
<p ng-show="txs.length == 0"><span translate>No transactions proposals yet.</span></p>
|
<p ng-show="txs.length == 0"><span translate>No transactions proposals yet.</span>
|
||||||
|
</p>
|
||||||
<pagination ng-show="txs.length > txpItemsPerPage" total-items="txs.length" items-per-page="txpItemsPerPage" page="txpCurrentPage" on-select-page="show()" class="pagination-small primary"></pagination>
|
<pagination ng-show="txs.length > txpItemsPerPage" total-items="txs.length" items-per-page="txpItemsPerPage" page="txpCurrentPage" on-select-page="show()" class="pagination-small primary"></pagination>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -20,10 +21,8 @@
|
||||||
|
|
||||||
<div class="large-12">
|
<div class="large-12">
|
||||||
<div class="m10b size-12" ng-hide="wallet.totalCopayers == 1">
|
<div class="m10b size-12" ng-hide="wallet.totalCopayers == 1">
|
||||||
<a class="text-gray active" ng-click="toogleLast()"
|
<a class="text-gray active" ng-click="toogleLast()" ng-disabled="loading" loading="Updating" ng-hide="lastShowed && !loading">[ <span translate>Show</span> ]</a>
|
||||||
ng-disabled="loading" loading="Updating" ng-hide="lastShowed && !loading">[ <span translate>Show</span> ]</a>
|
<a class="text-gray" ng-click="toogleLast()" ng-disabled="loading" loading="Updating" ng-show="lastShowed && !loading">[ <span translate>Hide</span> ]</a>
|
||||||
<a class="text-gray" ng-click="toogleLast()" ng-disabled="loading"
|
|
||||||
loading="Updating" ng-show="lastShowed && !loading">[ <span translate>Hide</span> ]</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="btransactions" ng-if="lastShowed">
|
<div class="btransactions" ng-if="lastShowed">
|
||||||
|
|
@ -52,9 +51,9 @@
|
||||||
<div class="last-transactions-content">
|
<div class="last-transactions-content">
|
||||||
<div class="large-5 medium-5 small-12 columns">
|
<div class="large-5 medium-5 small-12 columns">
|
||||||
<div ng-repeat="vin in btx.vinSimple">
|
<div ng-repeat="vin in btx.vinSimple">
|
||||||
<small class="right m5t">{{vin.value| noFractionNumber}} {{$root.unitName}}</small>
|
<small class="right m5t">{{vin.value| noFractionNumber}} {{$root.wallet.settings.unitName}}</small>
|
||||||
<p class="ellipsis text-gray size-12">
|
<p class="ellipsis text-gray size-12">
|
||||||
<contact address="{{vin.addr}}" tooltip-popup-delay="500" tooltip tooltip-placement="right"/>
|
<contact address="{{vin.addr}}" tooltip-popup-delay="500" tooltip tooltip-placement="right" />
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -66,20 +65,20 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="large-6 medium-6 small-12 columns">
|
<div class="large-6 medium-6 small-12 columns">
|
||||||
<div ng-repeat="vout in btx.voutSimple">
|
<div ng-repeat="vout in btx.voutSimple">
|
||||||
<small class="right m5t">{{vout.value| noFractionNumber}} {{$root.unitName}}</small>
|
<small class="right m5t">{{vout.value| noFractionNumber}} {{$root.wallet.settings.unitName}}</small>
|
||||||
<p class="ellipsis text-gray size-12">
|
<p class="ellipsis text-gray size-12">
|
||||||
<contact address="{{vout.addr}}" tooltip-popup-delay="500" tooltip tooltip-placement="right"/>
|
<contact address="{{vout.addr}}" tooltip-popup-delay="500" tooltip tooltip-placement="right" />
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="last-transactions-footer">
|
<div class="last-transactions-footer">
|
||||||
<div class="large-6 medium-6 small-6 columns">
|
<div class="large-6 medium-6 small-6 columns">
|
||||||
<p class="size-12"><span translate>Fee</span>: {{btx.fees | noFractionNumber}} {{$root.unitName}}</p>
|
<p class="size-12"><span translate>Fee</span>: {{btx.fees | noFractionNumber}} {{$root.wallet.settings.unitName}}</p>
|
||||||
<p class="size-12"><span translate>Confirmations</span>: {{btx.confirmations || 0}}</p>
|
<p class="size-12"><span translate>Confirmations</span>: {{btx.confirmations || 0}}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="large-6 medium-6 small-6 columns text-right">
|
<div class="large-6 medium-6 small-6 columns text-right">
|
||||||
<p class="label size-14"><span translate>Total</span>: {{btx.valueOut| noFractionNumber}} {{$root.unitName}}</p>
|
<p class="label size-14"><span translate>Total</span>: {{btx.valueOut| noFractionNumber}} {{$root.wallet.settings.unitName}}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -87,4 +86,3 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue