balances working

This commit is contained in:
Matias Alejo Garcia 2016-08-15 10:25:43 -03:00
parent 601fa94fde
commit 9f039d8c34
No known key found for this signature in database
GPG Key ID: 02470DB551277AB3
9 changed files with 656 additions and 271 deletions

View File

@ -1,20 +1,20 @@
<span ng-show="index.isShared" class="size-12"><span translate>{{index.m}}-of-{{index.n}}</span></span>
<span ng-show="index.isSingleAddress" class="size-12"><span translate>Auditable</span></span>
<img style="height:0.6em; margin-right: 1px;" ng-show="index.network != 'livenet'" src="img/icon-testnet-white.svg">
<img style="height:0.6em; margin-right: 1px;" ng-show="!index.canSign && !index.isPrivKeyExternal"
<span ng-show="wallet.isShared" class="size-12"><span translate>{{wallet.m}}-of-{{wallet.n}}</span></span>
<span ng-show="wallet.isSingleAddress" class="size-12"><span translate>Auditable</span></span>
<img style="height:0.6em; margin-right: 1px;" ng-show="wallet.network != 'livenet'" src="img/icon-testnet-white.svg">
<img style="height:0.6em; margin-right: 1px;" ng-show="!wallet.canSign() && !wallet.isPrivKeyExternal()"
src="img/icon-read-only-white.svg">
<img style="height:0.6em; margin-right: 1px;" ng-show="index.externalSource == 'trezor'"
<img style="height:0.6em; margin-right: 1px;" ng-show="wallet.getPrivKeyExternalSourceName() == 'trezor'"
src="img/icon-trezor-white.svg">
<img style="height:0.6em; margin-right: 1px;" ng-show="index.externalSource == 'ledger'"
<img style="height:0.6em; margin-right: 1px;" ng-show="wallet.getPrivKeyExternalSourceName() == 'ledger'"
src="img/icon-ledger-white.svg">
<span class="size-12 dib" style="height:0.6em; margin-right: 1px;" ng-show="index.account">#{{index.account || 0}} </span>
<span class="size-12 dib" style="height:0.6em; margin-right: 1px;" ng-show="wallet.account">#{{wallet.account || 0}} </span>
<img style="height:0.6em; margin-right: 1px;" ng-show="index.isPrivKeyEncrypted" src="img/icon-lock-white.svg">
<img style="height:0.6em; margin-right: 1px;" ng-show="wallet.isPrivKeyEncrypted()" src="img/icon-lock-white.svg">
<!-- <img style="height:1em" ng&#45;show="index.preferences.email" src="img/icon&#45;email.svg"> -->
<img style="height:0.6em; margin-right: 1px;" ng-show="index.usingCustomBWS" src="img/icon-bws-white.svg">
<!-- <img style="height:1em" ng&#45;show="wallet.preferences.email" src="img/icon&#45;email.svg"> -->
<img style="height:0.6em; margin-right: 1px;" ng-show="wallet.usingCustomBWS" src="img/icon-bws-white.svg">
<img style="height:0.6em" class="animated flash infinite" ng-show="index.loadingWallet ||
<img style="height:0.6em" class="animated flash infinite" ng-show="wallet.loadingWallet ||
index.updatingTxHistory" src="img/icon-sync-white.svg">

View File

@ -1,19 +1,25 @@
<ion-view view-title="Home">
<ion-content class="padding home" ng-controller="tabHomeController">
<a href="#/add"><i class="ion-ios-plus-outline right"></i></a>
<h2>Wallets </h2>
<a href="#/add">+</a>
<div class="list card">
<ul class="pr">
<li ng-show="wallets[0]"
ng-repeat="item in wallets track by $index" class="item item-icon-left"
menu-toggle href ui-sref="walletHome" on-tap="openWallet(item.id, index.walletId)">
menu-toggle href ui-sref="walletDetails({'walletId': item.id})">
<i class="icon icon-wallet size-21" ng-style="{'color':item.color}"></i>
{{item.name || item.id}}
<span class="item-note" ng-show="item.n > 1">
{{item.m}}-of-{{item.n}}
<span ng-show="item.n > 1">
[ {{item.m}}-of-{{item.n}} ]
</span>
<span class="item-note">
{{item.availableBalanceStr}}
</span>
</li>
</ul>
</div>

View File

@ -0,0 +1,230 @@
<div ng-controller="walletDetailsController">
<div class="onGoingProcess" ng-show="wallet.updating">
<div class="onGoingProcess-content" ng-style="{'background-color':wallet.color}">
<div class="spinner">
<div class="rect1"></div>
<div class="rect2"></div>
<div class="rect3"></div>
<div class="rect4"></div>
<div class="rect5"></div>
</div>
<span translate>Updating Wallet...</span>
</div>
</div>
<div class="oh">
<div id="walletHome" class="walletHome">
<ion-content delegate-handle="my-handle" overflow-scroll="true">
<div class="oh pr">
<div ng-style="{'background-color':wallet.color}" class="amount">
<div ng-if="!wallet.notAuthorized && !wallet.updating">
<div class="size-14 m10b" ng-if="wallet.totalBalanceAlternative">{{wallet.name}}</div>
<div class="m15t" ng-show="wallet.updateError" ng-click='update()'>
<span class="size-12 db m10b">{{wallet.updateError|translate}}</span>
<button class="outline white tiny round" translate>Tap to retry</button>
</div>
<div ng-show="wallet.walletScanStatus == 'error'" ng-click='wallet.retryScan()'>
<span translate>Scan status finished with error</span>
<br><span translate>Tap to retry</span>
</div>
<div ng-click='wallet.updateAll({triggerTxUpdate: true})' ng-show="!wallet.updateError && wallet.walletScanStatus != 'error' && !wallet.hideBalance" on-hold="hideToggle()">
<strong class="size-36">{{wallet.totalBalanceStr}}</strong>
<div class="size-14" ng-if="wallet.totalBalanceAlternative">{{wallet.totalBalanceAlternative}} {{wallet.alternativeIsoCode}}</div>
<div class="size-14" ng-if="wallet.pendingAmount">
<span translate>Pending Confirmation</span>: {{wallet.pendingAmountStr}}
</div>
</div>
<div ng-show="!wallet.updateError && wallet.walletScanStatus != 'error' && wallet.shouldHideBalance" on-hold="wallet.hideToggle()">
<strong class="size-24" translate>[Balance Hidden]</strong>
<div class="size-14" translate>
Tap and hold to show
</div>
</div>
</div>
<div ng-if="wallet.updating">
<div class="size-36">
<strong>...</strong>
</div>
</div>
</div> <!-- amount -->
<div class="wallet-info">
<span ng-include="'views/includes/walletInfo.html'"></span>
</div>
</div> <!-- oh -->
<div class="p60b">
<div class="oh pr m20t" ng-show="wallet.incorrectDerivation">
<div class="text-center text-warning">
<i class="fi-alert"></i>
<span translate>
WARNING: Key derivation is not working on this device/wallet. Actions cannot be performed on this wallet.
</span>
</div>
</div>
<div class="oh pr m20t" ng-show="wallet.notAuthorized && !wallet.updating">
<div class="text-center text-warning">
<i class="fi-alert"></i>
<span translate>
WARNING: Wallet not registered
</span>
</div>
<div class="text-center text-gray m15r m15l" translate>
This wallet is not registered at the given Bitcore Wallet Service (BWS). You can recreate it from the local information.
</div>
<div class="text-center m10t ">
<span class="button outline round dark-gray tiny"
ng-click="wallet.recreate()">
<span translate>Recreate</span>
</span>
</div>
</div>
<div class="release size-12" ng-show="newRelease" ng-click="$root.openExternalLink('https://github.com/bitpay/copay/releases/latest')">
<span>{{newRelease}}</span><i class="icon-arrow-right3 right size-18"></i>
</div>
<div ng-if="wallet.txps[0]">
<h4 ng-show="wallet.requiresMultipleSignatures" class="title m0" translate>Payment Proposals</h4>
<h4 ng-show="!wallet.requiresMultipleSignatures" class="title m0" translate>Unsent transactions</h4>
<div ng-repeat="tx in wallet.txps">
<div ng-include="wallet.txTemplateUrl"></div>
</div>
<div class="text-gray text-center size-12 p10t"
ng-show="wallet.lockedBalanceSat">
<span translate>Total Locked Balance</span>:
<b>{{wallet.lockedBalanceStr}} </b>
<span> {{wallet.lockedBalanceAlternative}}
{{wallet.alternativeIsoCode}} </span>
</div>
</div>
<!-- Activity -->
<h4 class="title" ng-click="wallet.startSearch(); openSearchModal()" ng-show="!wallet.notAuthorized">
<span translate>Activity</span>
<i class="dib m5l size-16 pointer fi-magnifying-glass"></i>
</h4>
<div class="oh pr m20t text-gray size-12 text-center"
ng-show="!wallet.loadingWallet && !wallet.txHistory[0] && !wallet.updatingTxHistory && !wallet.txHistoryError && !wallet.updateError && !wallet.notAuthorized"
translate>No transactions yet ZZZZ {{wallet.totalBalanceStr}}
</div>
<div class="oh pr" ng-show="(wallet.txHistory[0] || wallet.txProgress > 5) && !wallet.notAuthorized">
<div ng-show="wallet.updatingTxHistory && wallet.txProgress > 5">
<div class="row p20 text-center">
<div class="columns large-12 medium-12 small-12 m10b">
<ion-spinner class="spinner-dark" icon="lines"></ion-spinner>
</div>
<div class="size-12 text-gray m20t">
<div translate>{{wallet.txProgress}} transactions downloaded</div>
<div translate>Updating transaction history. Please stand by.</div>
</div>
</div>
</div>
<div ng-if="wallet.txHistory[0] && wallet.updatingTxHistory && wallet.newTx" class="row collapse last-transactions-content animated fadeInDown">
<div class="large-6 medium-6 small-6 columns size-14">
<div class="m10r left">
<img src="img/icon-new.svg" width="40">
</div>
<div class="m10t" style="background:#eee; width: 8em; margin-left: 52px; line-height:0.6em">
<span>&nbsp;</span>
</div>
<div style="margin-top:5px; background:#eee; width: 6em; margin-left: 52px; line-height:0.6em">
<span>&nbsp;</span>
</div>
</div>
</div>
<div ng-repeat="btx in wallet.txHistory track by btx.txid"
ng-click="openTxModal(btx)"
class="row collapse last-transactions-content">
<div class="large-6 medium-6 small-6 columns size-14">
<div class="m10r left">
<img src="img/icon-receive-history.svg" alt="sync" width="40" ng-show="btx.action == 'received'">
<img src="img/icon-sent-history.svg" alt="sync" width="40" ng-show="btx.action == 'sent'">
<img src="img/icon-moved.svg" alt="sync" width="40" ng-show="btx.action == 'moved'">
</div>
<div class="m10t">
<span ng-show="btx.action == 'received'">
<span class="ellipsis">
<span ng-if="btx.note.body">{{btx.note.body}}</span>
<span ng-if="!btx.note.body" translate> Received</span>
</span>
</span>
<span ng-show="btx.action == 'sent'">
<span class="ellipsis">
<span ng-if="btx.message">{{btx.message}}</span>
<span ng-if="!btx.message && btx.note.body">{{btx.note.body}}</span>
<span ng-if="!btx.message && !btx.note.body && wallet.addressbook[btx.addressTo]">{{wallet.addressbook[btx.addressTo]}}</span>
<span ng-if="!btx.message && !btx.note.body && !wallet.addressbook[btx.addressTo]" translate> Sent</span>
</span>
</span>
<span ng-show="btx.action == 'moved'">
<span class="ellipsis">
<span ng-if="btx.note.body">{{btx.note.body}}</span>
<span ng-if="!btx.note.body" translate>Moved</span>
</span>
</span>
<span class="label tu warning radius" ng-show="btx.action == 'invalid'" translate>Invalid</span>
</div>
</div>
<div class="large-5 medium-5 small-5 columns text-right" >
<span class="size-16" ng-class="{'text-bold': btx.recent}">
<span ng-if="btx.action == 'received'">+</span>
<span ng-if="btx.action == 'sent'">-</span>
<span class="size-12" ng-if="btx.action == 'invalid'" translate>
(possible double spend)
</span>
<span ng-if="btx.action != 'invalid'">
{{btx.amountStr}}
</span>
</span>
<div class="size-12 text-gray">
<time ng-if="btx.time">{{btx.time * 1000 | amTimeAgo}}</time>
<span translate class="text-warning"
ng-show="!btx.time && (!btx.confirmations || btx.confirmations == 0)">
Unconfirmed
</span>
</div>
</div>
<div class="large-1 medium-1 small-1 columns text-right m10t">
<i class="icon-arrow-right3 size-18"></i>
</div>
</div>
<div class="row m20t text-center" ng-show="wallet.historyRendering && !wallet.ching">
<div class="columns large-12 medium-12 small-12">
<ion-spinner class="spinner-stable" icon="lines"></ion-spinner>
</div>
</div>
<ion-infinite-scroll
ng-if="historyShowMore"
on-infinite="showMore()"
distance="1%">
</ion-infinite-scroll>
</div>
</div>
</ion-content>
<div class="extra-margin-bottom"></div>
</div> <!-- END WalletHome -->
</div>
</div>

View File

@ -19,35 +19,14 @@ angular.module('copayApp.controllers').controller('tabHomeController',
self.setWallets();
});
self.setWallets = function() {
if (!profileService.profile) return;
var config = configService.getSync();
config.colorFor = config.colorFor || {};
config.aliasFor = config.aliasFor || {};
// Sanitize empty wallets (fixed in BWC 1.8.1, and auto fixed when wallets completes)
var credentials = lodash.filter(profileService.profile.credentials, 'walletName');
var ret = lodash.map(credentials, function(c) {
return {
m: c.m,
n: c.n,
name: config.aliasFor[c.walletId] || c.walletName,
id: c.walletId,
color: config.colorFor[c.walletId] || '#4A90E2',
};
});
$scope.wallets = lodash.sortBy(ret, 'name');
self.setWallets = function() {
$scope.wallets = profileService.getWallets();
};
self.updateAllClients = function() {
lodash.each(profileService.getClients(), function(client) {
walletService.updateStatus(client, {}, function(err, status) {
if (err)
console.log('[tab-home.js.47]', err); //TODO
console.log('[tab-home.js.47:console:]',status); //TODO
self.updateAllClients = function() {
lodash.each(profileService.getWallets(), function(wallet) {
walletService.updateStatus(wallet, {}, function(err, status) {
if (err) {} // TODO
});
});
}
@ -55,7 +34,4 @@ console.log('[tab-home.js.47:console:]',status); //TODO
self.setWallets();
self.updateAllClients();
$scope.bitpayCardEnabled = true; // TODO
});

View File

@ -0,0 +1,76 @@
'use strict';
angular.module('copayApp.controllers').controller('walletDetailsController', function($scope, $rootScope, $interval, $timeout, $filter, $log, $ionicModal, $ionicPopover, $stateParams, profileService, lodash, configService, gettext, gettextCatalog, platformInfo, go, walletService ) {
console.log('[walletDetails.js.5]', $stateParams); //TODO
var isCordova = platformInfo.isCordova;
var isWP = platformInfo.isWP;
var isAndroid = platformInfo.isAndroid;
var isChromeApp = platformInfo.isChromeApp;
var self = this;
$rootScope.shouldHideMenuBar = false;
$rootScope.wpInputFocused = false;
var config = configService.getSync();
var configWallet = config.wallet;
var walletSettings = configWallet.settings;
var ret = {};
// INIT. Global value
ret.unitToSatoshi = walletSettings.unitToSatoshi;
ret.satToUnit = 1 / ret.unitToSatoshi;
ret.unitName = walletSettings.unitName;
ret.alternativeIsoCode = walletSettings.alternativeIsoCode;
ret.alternativeName = walletSettings.alternativeName;
ret.alternativeAmount = 0;
ret.unitDecimals = walletSettings.unitDecimals;
ret.isCordova = isCordova;
ret.addresses = [];
ret.isMobile = platformInfo.isMobile;
ret.isWindowsPhoneApp = platformInfo.isWP;
ret.countDown = null;
ret.sendMaxInfo = {};
ret.showAlternative = false;
$scope.openSearchModal = function() {
var fc = profileService.focusedClient;
$scope.color = fc.backgroundColor;
$scope.self = self;
$ionicModal.fromTemplateUrl('views/modals/search.html', {
scope: $scope,
focusFirstInput: true
}).then(function(modal) {
$scope.searchModal = modal;
$scope.searchModal.show();
});
};
this.openTxModal = function(btx) {
var self = this;
$scope.btx = lodash.cloneDeep(btx);
$scope.self = self;
$ionicModal.fromTemplateUrl('views/modals/tx-details.html', {
scope: $scope,
hideDelay: 500
}).then(function(modal) {
$scope.txDetailsModal = modal;
$scope.txDetailsModal.show();
});
};
$scope.update = function() {
console.log('[walletDetails.js.65:update:] TODO'); //TODO
// {triggerTxUpdate: true}
};
$scope.hideToggle = function() {
console.log('[walletDetails.js.70:hideToogle:] TODO'); //TODO
};
$scope.wallet = profileService.getWallet($stateParams.walletId);
console.log('[walletDetails.js.66]',$scope.wallet); //TODO
});

View File

@ -14,7 +14,7 @@ if (window && window.navigator) {
//Setting up route
angular.module('copayApp').config(function(historicLogProvider, $provide, $logProvider, $stateProvider, $urlRouterProvider, $compileProvider) {
$urlRouterProvider.otherwise('/');
$urlRouterProvider.otherwise('/tabs');
$logProvider.debugEnabled(true);
$provide.decorator('$log', ['$delegate', 'platformInfo',
@ -93,8 +93,20 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
}
}
})
.state('walletDetails', {
url: '/details',
needProfile: true,
views: {
'main': {
templateUrl: 'views/walletDetails.html',
},
},
params: {
walletId: null,
},
})
.state('walletHome', {
url: '/',
url: '/old',
needProfile: true,
views: {
'main': {

View File

@ -3,82 +3,5 @@
angular.module('copayApp.services')
.factory('addressService', function(storageService, profileService, $log, $timeout, lodash, bwcError, gettextCatalog) {
var root = {};
root.expireAddress = function(walletId, cb) {
$log.debug('Cleaning Address ' + walletId);
storageService.clearLastAddress(walletId, function(err) {
return cb(err);
});
};
root.isUsed = function(walletId, byAddress, cb) {
storageService.getLastAddress(walletId, function(err, addr) {
var used = lodash.find(byAddress, {
address: addr
});
return cb(null, used);
});
};
root._createAddress = function(walletId, cb) {
var client = profileService.getClient(walletId);
$log.debug('Creating address for wallet:', walletId);
client.createAddress({}, function(err, addr) {
if (err) {
var prefix = gettextCatalog.getString('Could not create address');
if (err.error && err.error.match(/locked/gi)) {
$log.debug(err.error);
return $timeout(function() {
root._createAddress(walletId, cb);
}, 5000);
} else if (err.message && err.message == 'MAIN_ADDRESS_GAP_REACHED') {
$log.warn(err.message);
prefix = null;
client.getMainAddresses({
reverse: true,
limit: 1
}, function(err, addr) {
if (err) return cb(err);
return cb(null, addr[0].address);
});
}
return bwcError.cb(err, prefix, cb);
}
return cb(null, addr.address);
});
};
root.getAddress = function(walletId, forceNew, cb) {
var firstStep;
if (forceNew) {
firstStep = storageService.clearLastAddress;
} else {
firstStep = function(walletId, cb) {
return cb();
};
}
firstStep(walletId, function(err) {
if (err) return cb(err);
storageService.getLastAddress(walletId, function(err, addr) {
if (err) return cb(err);
if (addr) return cb(null, addr);
root._createAddress(walletId, function(err, addr) {
if (err) return cb(err);
storageService.storeLastAddress(walletId, addr, function() {
if (err) return cb(err);
return cb(null, addr);
});
});
});
});
};
return root;
});

View File

@ -17,31 +17,19 @@ angular.module('copayApp.services')
root.profile = null;
root.focusedClient = null;
root.walletClients = {};
root.Utils = bwcService.getUtils();
root.formatAmount = function(amount, fullPrecision) {
var config = configService.getSync().wallet.settings;
if (config.unitCode == 'sat') return amount;
//TODO : now only works for english, specify opts to change thousand separator and decimal separator
var opts = {
fullPrecision: !!fullPrecision
};
return this.Utils.formatAmount(amount, config.unitCode, opts);
};
root.wallet = {}; // decorated version of client
root._setFocus = function(walletId, cb) {
$log.debug('Set focus:', walletId);
// Set local object
if (walletId)
root.focusedClient = root.walletClients[walletId];
root.focusedClient = root.wallet[walletId];
else
root.focusedClient = [];
if (lodash.isEmpty(root.focusedClient)) {
root.focusedClient = root.walletClients[lodash.keys(root.walletClients)[0]];
root.focusedClient = root.wallet[lodash.keys(root.wallet)[0]];
}
// Still nothing?
@ -51,7 +39,7 @@ angular.module('copayApp.services')
$rootScope.$emit('Local/NewFocusedWallet');
// Set update period
lodash.each(root.walletClients, function(client, id) {
lodash.each(root.wallet, function(client, id) {
client.setNotificationsInterval(BACKGROUND_UPDATE_PERIOD);
});
root.focusedClient.setNotificationsInterval(FOREGROUND_UPDATE_PERIOD);
@ -66,18 +54,37 @@ angular.module('copayApp.services')
});
};
root.setCustomBWSFlag = function(wallet) {
var defaults = configService.getDefaults();
var config = configService.getSync();
wallet.usingCustomBWS = config.bwsFor && config.bwsFor[wallet.id] && (config.bwsFor[wallet.id] != defaults.bws.url);
};
// Adds a wallet client to profileService
root.bindWalletClient = function(client, opts) {
var opts = opts || {};
var walletId = client.credentials.walletId;
var config = configService.getSync();
config.colorFor = config.colorFor || {};
config.aliasFor = config.aliasFor || {};
if ((root.walletClients[walletId] && root.walletClients[walletId].started) || opts.force) {
if ((root.wallet[walletId] && root.wallet[walletId].started) || opts.force) {
return false;
}
root.walletClients[walletId] = client;
root.walletClients[walletId].started = true;
root.walletClients[walletId].doNotVerifyPayPro = isChromeApp;
// INIT WALLET CLIENT VIEWMODEL
var c = client;
c.id = walletId;
c.started = true;
c.doNotVerifyPayPro = isChromeApp;
c.name = config.aliasFor[walletId] || client.credentials.walletName;
c.color = config.colorFor[walletId] || '#4A90E2';
c.network = client.credentials.network;
root.setCustomBWSFlag(c);
root.wallet[walletId] = c;
client.removeAllListeners();
client.on('report', function(n) {
@ -162,7 +169,7 @@ angular.module('copayApp.services')
// Used when reading wallets from the profile
root.bindWallet = function(credentials, cb) {
if (!credentials.walletId)
if (!credentials.walletId || !credentials.m)
return cb('bindWallet should receive credentials JSON');
@ -239,7 +246,7 @@ angular.module('copayApp.services')
root.pushNotificationsInit = function() {
var defaults = configService.getDefaults();
var push = pushNotificationsService.init(root.walletClients);
var push = pushNotificationsService.init(root.wallet);
push.on('notification', function(data) {
if (!data.additionalData.foreground) {
@ -431,14 +438,15 @@ angular.module('copayApp.services')
});
};
root.getClient = function(walletId) {
return root.walletClients[walletId];
root.getWallet = function(walletId) {
return root.wallet[walletId];
};
root.deleteWalletClient = function(client, cb) {
var walletId = client.credentials.walletId;
pushNotificationsService.unsubscribe(root.getClient(walletId), function(err) {
pushNotificationsService.unsubscribe(root.getWallet(walletId), function(err) {
if (err) $log.warn('Unsubscription error: ' + err.message);
else $log.debug('Unsubscribed from push notifications service');
});
@ -448,7 +456,7 @@ angular.module('copayApp.services')
root.profile.deleteWallet(walletId);
delete root.walletClients[walletId];
delete root.wallet[walletId];
root.focusedClient = null;
@ -529,7 +537,7 @@ angular.module('copayApp.services')
storageService.storeProfile(root.profile, function(err) {
var config = configService.getSync();
if (config.pushNotifications.enabled)
pushNotificationsService.enableNotifications(root.walletClients);
pushNotificationsService.enableNotifications(root.wallet);
return cb(err, walletId);
});
@ -705,8 +713,22 @@ angular.module('copayApp.services')
storageService.storeProfile(root.profile, cb);
};
root.getClients = function() {
return lodash.values(root.walletClients);
root.getWallets = function(network, n) {
var ret = lodash.values(root.wallet);
if (network) {
ret = lodash.filter(ret, function(x) {
return (x.credentials.network == network);
});
}
if (n) {
ret = lodash.filter(ret, function(w) {
return (w.credentials.n == n);
});
}
return lodash.sortBy(ret, 'name');
};
root.needsBackup = function(client, cb) {
@ -732,36 +754,5 @@ angular.module('copayApp.services')
});
};
root.getWallets = function(network, n) {
if (!root.profile) return [];
var config = configService.getSync();
config.colorFor = config.colorFor || {};
config.aliasFor = config.aliasFor || {};
var ret = lodash.map(root.profile.credentials, function(c) {
return {
m: c.m,
n: c.n,
name: config.aliasFor[c.walletId] || c.walletName,
id: c.walletId,
network: c.network,
color: config.colorFor[c.walletId] || '#4A90E2',
copayerId: c.copayerId
};
});
if (network) {
ret = lodash.filter(ret, function(w) {
return (w.network == network);
});
}
if (n) {
ret = lodash.filter(ret, function(w) {
return (w.n == n);
});
}
return lodash.sortBy(ret, 'name');
};
return root;
});

View File

@ -1,15 +1,42 @@
'use strict';
// DO NOT INCLUDE STORAGE HERE \/ \/
angular.module('copayApp.services').factory('walletService', function($log, $timeout, lodash, trezor, ledger, storageService, configService, uxLanguage) {
angular.module('copayApp.services').factory('walletService', function($log, $timeout, lodash, trezor, ledger, storageService, configService, rateService, uxLanguage, bwcService, $filter) {
// DO NOT INCLUDE STORAGE HERE ^^
//
//
// `wallet` is a decorated version of client.
var root = {};
var _signWithLedger = function(client, txp, cb) {
// // RECEIVE
// // Check address
// root.isUsed(wallet.walletId, balance.byAddress, function(err, used) {
// if (used) {
// $log.debug('Address used. Creating new');
// $rootScope.$emit('Local/AddressIsUsed');
// }
// });
//
root.Utils = bwcService.getUtils();
root.formatAmount = function(amount, fullPrecision) {
var config = configService.getSync().wallet.settings;
if (config.unitCode == 'sat') return amount;
//TODO : now only works for english, specify opts to change thousand separator and decimal separator
var opts = {
fullPrecision: !!fullPrecision
};
return this.Utils.formatAmount(amount, config.unitCode, opts);
};
var _signWithLedger = function(wallet, txp, cb) {
$log.info('Requesting Ledger Chrome app to sign the transaction');
ledger.signTx(txp, client.credentials.account, function(result) {
ledger.signTx(txp, wallet.credentials.account, function(result) {
$log.debug('Ledger response', result);
if (!result.success)
return cb(result.message || result.error);
@ -17,27 +44,27 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
txp.signatures = lodash.map(result.signatures, function(s) {
return s.substring(0, s.length - 2);
});
return client.signTxProposal(txp, cb);
return wallet.signTxProposal(txp, cb);
});
};
var _signWithTrezor = function(client, txp, cb) {
var _signWithTrezor = function(wallet, txp, cb) {
$log.info('Requesting Trezor to sign the transaction');
var xPubKeys = lodash.pluck(client.credentials.publicKeyRing, 'xPubKey');
trezor.signTx(xPubKeys, txp, client.credentials.account, function(err, result) {
var xPubKeys = lodash.pluck(wallet.credentials.publicKeyRing, 'xPubKey');
trezor.signTx(xPubKeys, txp, wallet.credentials.account, function(err, result) {
if (err) return cb(err);
$log.debug('Trezor response', result);
txp.signatures = result.signatures;
return client.signTxProposal(txp, cb);
return wallet.signTxProposal(txp, cb);
});
};
root.needsBackup = function(client) {
if (client.isPrivKeyExternal()) return false;
if (!client.credentials.mnemonic) return false;
if (client.credentials.network == 'testnet') return false;
root.needsBackup = function(wallet) {
if (wallet.isPrivKeyExternal()) return false;
if (!wallet.credentials.mnemonic) return false;
if (wallet.credentials.network == 'testnet') return false;
return true;
};
@ -58,10 +85,10 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
// trigger from async events (like updates).
// Debounce function avoids multiple popups
var _handleError = function(err) {
$log.warn('Client ERROR: ', err);
$log.warn('wallet ERROR: ', err);
$log.warn('TODO');
return ; // TODO!!!
return; // TODO!!!
if (err instanceof errors.NOT_AUTHORIZED) {
self.notAuthorized = true;
go.walletHome();
@ -76,14 +103,83 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
};
root.handleError = lodash.debounce(_handleError, 1000);
// emits
// statusUpdated walletId, err ,statusObj
//
root.updateStatus = function(client, opts, cb, initStatusHash, tries) {
root.setBalance = function(wallet, balance) {
if (!balance) return;
var config = configService.getSync().wallet.settings;
var COIN = 1e8;
// Address with Balance
wallet.balanceByAddress = balance.byAddress;
// Spend unconfirmed funds
if (wallet.spendUnconfirmed) {
wallet.totalBalanceSat = balance.totalAmount;
wallet.lockedBalanceSat = balance.lockedAmount;
wallet.availableBalanceSat = balance.availableAmount;
wallet.totalBytesToSendMax = balance.totalBytesToSendMax;
wallet.pendingAmount = null;
} else {
wallet.totalBalanceSat = balance.totalConfirmedAmount;
wallet.lockedBalanceSat = balance.lockedConfirmedAmount;
wallet.availableBalanceSat = balance.availableConfirmedAmount;
wallet.totalBytesToSendMax = balance.totalBytesToSendConfirmedMax;
wallet.pendingAmount = balance.totalAmount - balance.totalConfirmedAmount;
}
// Selected unit
wallet.unitToSatoshi = config.unitToSatoshi;
wallet.satToUnit = 1 / wallet.unitToSatoshi;
wallet.unitName = config.unitName;
//STR
wallet.totalBalanceStr = root.formatAmount(wallet.totalBalanceSat) + ' ' + wallet.unitName;
wallet.lockedBalanceStr = root.formatAmount(wallet.lockedBalanceSat) + ' ' + wallet.unitName;
wallet.availableBalanceStr = root.formatAmount(wallet.availableBalanceSat) + ' ' + wallet.unitName;
if (wallet.pendingAmount) {
wallet.pendingAmountStr = root.formatAmount(wallet.pendingAmount) + ' ' + wallet.unitName;
} else {
wallet.pendingAmountStr = null;
}
wallet.alternativeName = config.alternativeName;
wallet.alternativeIsoCode = config.alternativeIsoCode;
rateService.whenAvailable(function() {
var totalBalanceAlternative = rateService.toFiat(wallet.totalBalanceSat, wallet.alternativeIsoCode);
var lockedBalanceAlternative = rateService.toFiat(wallet.lockedBalanceSat, wallet.alternativeIsoCode);
var alternativeConversionRate = rateService.toFiat(100000000, wallet.alternativeIsoCode);
wallet.totalBalanceAlternative = $filter('formatFiatAmount')(totalBalanceAlternative);
wallet.lockedBalanceAlternative = $filter('formatFiatAmount')(lockedBalanceAlternative);
wallet.alternativeConversionRate = $filter('formatFiatAmount')(alternativeConversionRate);
wallet.alternativeBalanceAvailable = true;
wallet.isRateAvailable = true;
});
};
root.setStatus = function(wallet, status) {
wallet.status = status;
wallet.statusUpdatedOn = Date.now();
wallet.isValid = true;
root.setBalance(wallet, status.balance);
};
root.updateStatus = function(wallet, opts, cb, initStatusHash, tries) {
tries = tries || 0;
opts = opts || {};
var walletId = client.credentials.walletId
if (wallet.isValid && ! opts.force)
return;
var walletId = wallet.id;
if (opts.untilItChanges && lodash.isUndefined(initStatusHash)) {
initStatusHash = _walletStatusHash();
@ -94,7 +190,7 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
if (opts.walletStatus)
return cb(null, opts.walletStatus);
else {
return client.getStatus({
return wallet.getStatus({
twoStep: true
}, function(err, ret) {
if (err)
@ -102,7 +198,7 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
// TODO??
// self.isSingleAddress = !!ret.wallet.singleAddress;
// self.updating = ret.wallet.scanStatus == 'running';
return cb(err);
return cb(null, ret);
});
}
};
@ -117,16 +213,16 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
$timeout(function() {
// if (!opts.quiet)
// self.updating = true;
// self.updating = true;
$log.debug('Updating Status:', client.credentials.walletName, tries);
$log.debug('Updating Status:', wallet.credentials.walletName, tries);
get(function(err, walletStatus) {
var currentStatusHash = _walletStatusHash(walletStatus);
$log.debug('Status update. hash:' + currentStatusHash + ' Try:' + tries);
if (!err && opts.untilItChanges && initStatusHash == currentStatusHash && tries < 7 && walletId == profileService.focusedClient.credentials.walletId) {
return $timeout(function() {
$log.debug('Retrying update... ' + walletId + ' Try:' + tries)
return root.updateStatus(client, {
return root.updateStatus(wallet, {
walletStatus: null,
untilItChanges: true,
triggerTxUpdate: opts.triggerTxUpdate,
@ -138,8 +234,9 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
root.handleError(err);
return cb(err);
}
$log.debug('Got Wallet Status for:' + wallet.credentials.walletName);
$log.debug('Wallet Status:' + client.credentials.walletName, walletStatus);
root.setStatus(wallet, walletStatus);
// self.setPendingTxps(walletStatus.pendingTxps);
//
@ -160,11 +257,11 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
// TODO
if (opts.triggerTxUpdate && opts.untilItChanges) {
$timeout(function() {
root.debounceUpdateHistory();
}, 1);
$timeout(function() {
root.debounceUpdateHistory();
}, 1);
}
return cb(null, walletStatus);
return cb();
// } else {
// self.loadingWallet = false;
// }
@ -192,10 +289,10 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
});
};
var getTxsFromServer = function(client, skip, endingTxid, limit, cb) {
var getTxsFromServer = function(wallet, skip, endingTxid, limit, cb) {
var res = [];
client.getTxHistory({
wallet.getTxHistory({
skip: skip,
limit: limit
}, function(err, txsFromServer) {
@ -213,11 +310,11 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
};
var updateLocalTxHistory = function(client, cb) {
var updateLocalTxHistory = function(wallet, cb) {
var FIRST_LIMIT = 5;
var LIMIT = 50;
var requestLimit = FIRST_LIMIT;
var walletId = client.credentials.walletId;
var walletId = wallet.credentials.walletId;
var config = configService.getSync().wallet.settings;
var fixTxsUnit = function(txs) {
@ -233,8 +330,8 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
$log.debug('Fixing Tx Cache Unit to:' + name)
lodash.each(txs, function(tx) {
tx.amountStr = profileService.formatAmount(tx.amount) + name;
tx.feeStr = profileService.formatAmount(tx.fees) + name;
tx.amountStr = root.formatAmount(tx.amount) + name;
tx.feeStr = root.formatAmount(tx.fees) + name;
});
};
@ -260,7 +357,7 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
historyUpdateInProgress[walletId] = true;
function getNewTxs(newTxs, skip, i_cb) {
getTxsFromServer(client, skip, endingTxid, requestLimit, function(err, res, shouldContinue) {
getTxsFromServer(wallet, skip, endingTxid, requestLimit, function(err, res, shouldContinue) {
if (err) return i_cb(err);
newTxs = newTxs.concat(lodash.compact(res));
@ -306,7 +403,7 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
if (!endingTs) return cb2();
$log.debug('Syncing notes from: ' + endingTs);
client.getTxNotes({
wallet.getTxNotes({
minTs: endingTs
}, function(err, notes) {
if (err) {
@ -352,10 +449,10 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
};
root.updateHistory = function(client) {
var walletId = client.credentials.walletId;
root.updateHistory = function(wallet) {
var walletId = wallet.credentials.walletId;
if (!client.isComplete()) return;
if (!wallet.isComplete()) return;
$log.debug('Updating Transaction History');
@ -363,7 +460,7 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
self.updatingTxHistory = true;
$timeout(function() {
updateLocalTxHistory(client, function(err) {
updateLocalTxHistory(wallet, function(err) {
historyUpdateInProgress[walletId] = self.updatingTxHistory = false;
self.loadingWallet = false;
self.txProgress = 0;
@ -382,45 +479,45 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
root.isEncrypted = function(client) {
if (lodash.isEmpty(client)) return;
var isEncrypted = client.isPrivKeyEncrypted();
root.isEncrypted = function(wallet) {
if (lodash.isEmpty(wallet)) return;
var isEncrypted = wallet.isPrivKeyEncrypted();
if (isEncrypted) $log.debug('Wallet is encrypted');
return isEncrypted;
};
root.lock = function(client) {
root.lock = function(wallet) {
try {
client.lock();
wallet.lock();
} catch (e) {
$log.warn('Encrypting wallet:', e);
};
};
root.unlock = function(client, password) {
if (lodash.isEmpty(client))
root.unlock = function(wallet, password) {
if (lodash.isEmpty(wallet))
return 'MISSING_PARAMETER';
if (lodash.isEmpty(password))
return 'NO_PASSWORD_GIVEN';
try {
client.unlock(password);
wallet.unlock(password);
} catch (e) {
$log.warn('Decrypting wallet:', e);
return 'PASSWORD_INCORRECT';
}
};
root.createTx = function(client, txp, cb) {
if (lodash.isEmpty(txp) || lodash.isEmpty(client))
root.createTx = function(wallet, txp, cb) {
if (lodash.isEmpty(txp) || lodash.isEmpty(wallet))
return cb('MISSING_PARAMETER');
if (txp.sendMax) {
client.createTxProposal(txp, function(err, createdTxp) {
wallet.createTxProposal(txp, function(err, createdTxp) {
if (err) return cb(err);
else return cb(null, createdTxp);
});
} else {
client.getFeeLevels(client.credentials.network, function(err, levels) {
wallet.getFeeLevels(wallet.credentials.network, function(err, levels) {
if (err) return cb(err);
var feeLevelValue = lodash.find(levels, {
@ -435,7 +532,7 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
$log.debug('Dynamic fee: ' + txp.feeLevel + ' ' + feeLevelValue.feePerKB + ' SAT');
txp.feePerKb = feeLevelValue.feePerKB;
client.createTxProposal(txp, function(err, createdTxp) {
wallet.createTxProposal(txp, function(err, createdTxp) {
if (err) return cb(err);
else {
$log.debug('Transaction created');
@ -446,11 +543,11 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
}
};
root.publishTx = function(client, txp, cb) {
if (lodash.isEmpty(txp) || lodash.isEmpty(client))
root.publishTx = function(wallet, txp, cb) {
if (lodash.isEmpty(txp) || lodash.isEmpty(wallet))
return cb('MISSING_PARAMETER');
client.publishTxProposal({
wallet.publishTxProposal({
txp: txp
}, function(err, publishedTx) {
if (err) return cb(err);
@ -461,25 +558,25 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
});
};
root.signTx = function(client, txp, cb) {
if (lodash.isEmpty(txp) || lodash.isEmpty(client))
root.signTx = function(wallet, txp, cb) {
if (lodash.isEmpty(txp) || lodash.isEmpty(wallet))
return cb('MISSING_PARAMETER');
if (client.isPrivKeyExternal()) {
switch (client.getPrivKeyExternalSourceName()) {
if (wallet.isPrivKeyExternal()) {
switch (wallet.getPrivKeyExternalSourceName()) {
case 'ledger':
return _signWithLedger(client, txp, cb);
return _signWithLedger(wallet, txp, cb);
case 'trezor':
return _signWithTrezor(client, txp, cb);
return _signWithTrezor(wallet, txp, cb);
default:
var msg = 'Unsupported External Key:' + client.getPrivKeyExternalSourceName();
var msg = 'Unsupported External Key:' + wallet.getPrivKeyExternalSourceName();
$log.error(msg);
return cb(msg);
}
} else {
try {
client.signTxProposal(txp, function(err, signedTxp) {
wallet.signTxProposal(txp, function(err, signedTxp) {
$log.debug('Transaction signed');
return cb(err, signedTxp);
});
@ -490,14 +587,14 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
}
};
root.broadcastTx = function(client, txp, cb) {
if (lodash.isEmpty(txp) || lodash.isEmpty(client))
root.broadcastTx = function(wallet, txp, cb) {
if (lodash.isEmpty(txp) || lodash.isEmpty(wallet))
return cb('MISSING_PARAMETER');
if (txp.status != 'accepted')
return cb('TX_NOT_ACCEPTED');
client.broadcastTxProposal(txp, function(err, broadcastedTxp, memo) {
wallet.broadcastTxProposal(txp, function(err, broadcastedTxp, memo) {
if (err)
return cb(err);
@ -508,21 +605,21 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
});
};
root.rejectTx = function(client, txp, cb) {
if (lodash.isEmpty(txp) || lodash.isEmpty(client))
root.rejectTx = function(wallet, txp, cb) {
if (lodash.isEmpty(txp) || lodash.isEmpty(wallet))
return cb('MISSING_PARAMETER');
client.rejectTxProposal(txp, null, function(err, rejectedTxp) {
wallet.rejectTxProposal(txp, null, function(err, rejectedTxp) {
$log.debug('Transaction rejected');
return cb(err, rejectedTxp);
});
};
root.removeTx = function(client, txp, cb) {
if (lodash.isEmpty(txp) || lodash.isEmpty(client))
root.removeTx = function(wallet, txp, cb) {
if (lodash.isEmpty(txp) || lodash.isEmpty(wallet))
return cb('MISSING_PARAMETER');
client.removeTxProposal(txp, function(err) {
wallet.removeTxProposal(txp, function(err) {
$log.debug('Transaction removed');
return cb(err);
});
@ -535,11 +632,11 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
clients = [clients];
function updateRemotePreferencesFor(clients, prefs, cb) {
var client = clients.shift();
if (!client) return cb();
$log.debug('Saving remote preferences', client.credentials.walletName, prefs);
var wallet = clients.shift();
if (!wallet) return cb();
$log.debug('Saving remote preferences', wallet.credentials.walletName, prefs);
client.savePreferences(prefs, function(err) {
wallet.savePreferences(prefs, function(err) {
// we ignore errors here
if (err) $log.warn(err);
@ -603,9 +700,9 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
});
};
root.recreate = function(client, cb) {
root.recreate = function(wallet, cb) {
ongoingProcess.set('recreating', true);
client.recreateWallet(function(err) {
wallet.recreateWallet(function(err) {
self.notAuthorized = false;
ongoingProcess.set('recreating', false);
@ -615,20 +712,20 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
return;
}
profileService.bindWalletClient(client, {
profileService.bindWalletClient(wallet, {
force: true
});
self.startScan(client);
self.startScan(wallet);
});
};
root.startScan = function(client) {
$log.debug('Scanning wallet ' + client.credentials.walletId);
if (!client.isComplete()) return;
root.startScan = function(wallet) {
$log.debug('Scanning wallet ' + wallet.credentials.walletId);
if (!wallet.isComplete()) return;
// self.updating = true;
// self.updating = true;
client.startScan({
wallet.startScan({
includeCopayerBranches: true,
}, function(err) {
@ -642,6 +739,80 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
};
root.expireAddress = function(wallet, cb) {
$log.debug('Cleaning Address ' + wallet.id);
storageService.clearLastAddress(wallet.id, function(err) {
return cb(err);
});
};
root.isUsed = function(wallet, byAddress, cb) {
storageService.getLastAddress(wallet.id, function(err, addr) {
var used = lodash.find(byAddress, {
address: addr
});
return cb(null, used);
});
};
root._createAddress = function(wallet, cb) {
$log.debug('Creating address for wallet:', wallet.id);
wallet.createAddress({}, function(err, addr) {
if (err) {
var prefix = gettextCatalog.getString('Could not create address');
if (err.error && err.error.match(/locked/gi)) {
$log.debug(err.error);
return $timeout(function() {
root._createAddress(walletId, cb);
}, 5000);
} else if (err.message && err.message == 'MAIN_ADDRESS_GAP_REACHED') {
$log.warn(err.message);
prefix = null;
wallet.getMainAddresses({
reverse: true,
limit: 1
}, function(err, addr) {
if (err) return cb(err);
return cb(null, addr[0].address);
});
}
return bwcError.cb(err, prefix, cb);
}
return cb(null, addr.address);
});
};
root.getAddress = function(wallet, forceNew, cb) {
var firstStep;
if (forceNew) {
firstStep = storageService.clearLastAddress;
} else {
firstStep = function(walletId, cb) {
return cb();
};
}
firstStep(wallet.id, function(err) {
if (err) return cb(err);
storageService.getLastAddress(wallet.id, function(err, addr) {
if (err) return cb(err);
if (addr) return cb(null, addr);
root._createAddress(wallet, function(err, addr) {
if (err) return cb(err);
storageService.storeLastAddress(wallet.id, addr, function() {
if (err) return cb(err);
return cb(null, addr);
});
});
});
});
};
return root;
});