[V4] Feat: Show wallet total inputs and fix getMinAmount function

This commit is contained in:
Gabriel Bazán 2018-01-05 12:48:46 -03:00
parent 4a042b9954
commit 89efc4ec1f
9 changed files with 112 additions and 54 deletions

View File

@ -7,12 +7,14 @@
<ion-content> <ion-content>
<page-wallet-item [wallet]="wallet"></page-wallet-item> <page-wallet-item [wallet]="wallet"></page-wallet-item>
<div class="comment"> <div class="comment">
<span translate>Each bitcoin wallet can generate billions of addresses from your 12-word backup. <span translate>Each bitcoin wallet can generate billions of addresses from your 12-word backup. A new address is automatically generated
A new address is automatically generated and shown each time you receive a payment. and shown each time you receive a payment.
</span><a (click)="showInfo = !showInfo" *ngIf="!showInfo" translate>Why?</a> </span>
<a (click)="showInfo = !showInfo" *ngIf="!showInfo" translate>Why?</a>
<div *ngIf="showInfo"> <div *ngIf="showInfo">
<span translate>It's a good idea to avoid reusing addresses - this both protects your privacy and keeps your bitcoins secure against hypothetical attacks by quantum computers.</span> <span translate>It's a good idea to avoid reusing addresses - this both protects your privacy and keeps your bitcoins secure against
hypothetical attacks by quantum computers.</span>
<a (click)="showInfo = !showInfo" translate>Hide</a> <a (click)="showInfo = !showInfo" translate>Hide</a>
</div> </div>
</div> </div>
@ -41,10 +43,14 @@
<div *ngIf="latestUnused && latestUnused[0]"> <div *ngIf="latestUnused && latestUnused[0]">
<div *ngIf="gapReached"> <div *ngIf="gapReached">
<h5 translate>Unused addresses limit</h5> <h5 translate>Unused addresses limit</h5>
<p><span translate>The maximum number of consecutive unused addresses (20) has been reached. When one of your unused addresses receives a payment, a new address will be generated and shown in your Receive tab.</span>&nbsp <p>
<span translate>The maximum number of consecutive unused addresses (20) has been reached. When one of your unused addresses receives
a payment, a new address will be generated and shown in your Receive tab.</span>&nbsp
<a (click)="readMore()" *ngIf="!showMore" translate>Read more</a> <a (click)="readMore()" *ngIf="!showMore" translate>Read more</a>
</p> </p>
<p *ngIf="showMore"><span translate>The restore process will stop when 20 addresses are generated in a row which contain no funds. To safely generate more addresses, make a payment to one of the unused addresses which has already been generated.</span>&nbsp <p *ngIf="showMore">
<span translate>The restore process will stop when 20 addresses are generated in a row which contain no funds. To safely generate
more addresses, make a payment to one of the unused addresses which has already been generated.</span>&nbsp
<a (click)="readMore()" translate>Read less</a> <a (click)="readMore()" translate>Read less</a>
</p> </p>
</div> </div>
@ -65,7 +71,7 @@
</div> </div>
<div *ngIf="allUtxosNb"> <div *ngIf="allUtxosNb">
<ion-item-divider>{{'Wallet Inputs' | translate}}</ion-item-divider> <ion-item-divider color="light">{{'Wallet Inputs' | translate}}</ion-item-divider>
<ion-item> <ion-item>
<span translate>Total wallet inputs</span> <span translate>Total wallet inputs</span>

View File

@ -8,6 +8,7 @@ import { WalletProvider } from '../../../../../providers/wallet/wallet';
import { BwcErrorProvider } from '../../../../../providers/bwc-error/bwc-error'; import { BwcErrorProvider } from '../../../../../providers/bwc-error/bwc-error';
import { PopupProvider } from '../../../../../providers/popup/popup'; import { PopupProvider } from '../../../../../providers/popup/popup';
import { OnGoingProcessProvider } from '../../../../../providers/on-going-process/on-going-process'; import { OnGoingProcessProvider } from '../../../../../providers/on-going-process/on-going-process';
import { TxFormatProvider } from '../../../../../providers/tx-format/tx-format';
//pages //pages
import { AllAddressesPage } from './all-addresses/all-addresses'; import { AllAddressesPage } from './all-addresses/all-addresses';
@ -28,6 +29,13 @@ export class WalletAddressesPage {
public latestWithBalance: any; public latestWithBalance: any;
public viewAll: boolean; public viewAll: boolean;
public gapReached: boolean; public gapReached: boolean;
public lowUtxosNb: number;
public allUtxosNb: number;
public lowUtxosSum: string;
public allUtxosSum: string;
public minFee: string;
public minFeePer: string;
private UNUSED_ADDRESS_LIMIT: number; private UNUSED_ADDRESS_LIMIT: number;
private BALANCE_ADDRESS_LIMIT: number; private BALANCE_ADDRESS_LIMIT: number;
private withBalance: any; private withBalance: any;
@ -43,6 +51,7 @@ export class WalletAddressesPage {
private popupProvider: PopupProvider, private popupProvider: PopupProvider,
private onGoingProcessProvider: OnGoingProcessProvider, private onGoingProcessProvider: OnGoingProcessProvider,
private modalCtrl: ModalController, private modalCtrl: ModalController,
private txFormatProvider: TxFormatProvider
) { ) {
this.UNUSED_ADDRESS_LIMIT = 5; this.UNUSED_ADDRESS_LIMIT = 5;
this.BALANCE_ADDRESS_LIMIT = 5; this.BALANCE_ADDRESS_LIMIT = 5;
@ -79,6 +88,26 @@ export class WalletAddressesPage {
this.loading = false; this.loading = false;
this.popupProvider.ionicAlert(this.bwcErrorProvider.msg(err, 'Could not update wallet')); //TODO gettextcatalog this.popupProvider.ionicAlert(this.bwcErrorProvider.msg(err, 'Could not update wallet')); //TODO gettextcatalog
}); });
this.walletProvider.getLowUtxos(this.wallet).then((resp) => {
if (resp && resp.allUtxos && resp.allUtxos.length) {
let allSum = _.sumBy(resp.allUtxos || 0, 'satoshis');
let per = (resp.minFee / allSum) * 100;
this.lowUtxosNb = resp.lowUtxos.length;
this.allUtxosNb = resp.allUtxos.length;
this.lowUtxosSum = this.txFormatProvider.formatAmountStr(this.wallet.coin, _.sumBy(resp.lowUtxos || 0, 'satoshis'));
this.allUtxosSum = this.txFormatProvider.formatAmountStr(this.wallet.coin, allSum);
this.minFee = this.txFormatProvider.formatAmountStr(this.wallet.coin, resp.minFee || 0);
this.minFeePer = per.toFixed(2) + '%';
}
}).catch((err) => {
this.logger.warn(err);
});
} }
private processPaths(list: any): void { private processPaths(list: any): void {

View File

@ -10,11 +10,13 @@
<ion-label stacked translate>Wallet Service URL</ion-label> <ion-label stacked translate>Wallet Service URL</ion-label>
<ion-input type="text" formControlName="bwsurl" [value]="walletServiceForm.value.bwsurl" required></ion-input> <ion-input type="text" formControlName="bwsurl" [value]="walletServiceForm.value.bwsurl" required></ion-input>
</ion-item> </ion-item>
<div translate> <div class="comment">
{{appName}} depends on Bitcore Wallet Service (BWS) for blockchain information, networking and Copayer synchronization. The <span translate>{{appName}} depends on Bitcore Wallet Service (BWS) for blockchain information, networking and Copayer synchronization.
default configuration points to https://bws.bitpay.com (BitPay's public BWS instance). The default configuration points to https://bws.bitpay.com (BitPay's public BWS instance).</span>
<a (click)="resetDefaultUrl()" translate>
Use default url
</a>
</div> </div>
<button ion-button (click)="resetDefaultUrl()"><ion-icon name="refresh"></ion-icon></button>
<button ion-button block type="submit" [disabled]="!walletServiceForm.valid">Save</button> <button ion-button block type="submit" [disabled]="!walletServiceForm.valid">Save</button>
</form> </form>
</ion-content> </ion-content>

View File

@ -0,0 +1,5 @@
page-wallet-service-url {
.comment {
padding: 20px;
}
}

View File

@ -5,7 +5,7 @@
</ion-header> </ion-header>
<ion-content> <ion-content>
<page-wallet-item [wallet]="wallet"></page-wallet-item> <page-wallet-item [wallet]="wallet"></page-wallet-item>
<ion-item-divider *ngIf="!isCordova" color="light"></ion-item-divider> <ion-item-divider *ngIf="!isCordova" color="light"></ion-item-divider>
<button ion-item *ngIf="csvReady && !isCordova" (click)="downloadCSV()"> <button ion-item *ngIf="csvReady && !isCordova" (click)="downloadCSV()">
@ -21,12 +21,12 @@
{{err | translate}} {{err | translate}}
</ion-note> </ion-note>
</ion-item> </ion-item>
<button ion-item (click)="clearTransactionHistory()"> <button ion-item (click)="clearTransactionHistory()">
<span translate>Clear cache</span> <span translate>Clear cache</span>
</button> </button>
<div translate> <div class="comment" translate>
The transaction history and every new incoming transaction are cached in the app. This feature clean this up and synchronizes The transaction history and every new incoming transaction are cached in the app. This feature clean this up and synchronizes
again from the server again from the server
</div> </div>

View File

@ -3,5 +3,8 @@ page-wallet-transaction-history {
&.item { &.item {
color: color($colors, primary) !important; color: color($colors, primary) !important;
} }
} }
.comment {
padding: 20px;
}
} }

View File

@ -24,6 +24,7 @@ export class FeeProvider {
updateTs: 0, updateTs: 0,
coin: '' coin: ''
} }
constructor( constructor(
private configProvider: ConfigProvider, private configProvider: ConfigProvider,
private logger: Logger, private logger: Logger,
@ -106,5 +107,4 @@ export class FeeProvider {
}); });
} }
} }

View File

@ -55,8 +55,6 @@ export class ProfileProvider {
public setWalletOrder(walletId: string, index: number): void { public setWalletOrder(walletId: string, index: number): void {
this.persistenceProvider.setWalletOrder(walletId, index); this.persistenceProvider.setWalletOrder(walletId, index);
this.logger.debug('Wallet new order stored'); this.logger.debug('Wallet new order stored');
console.log(this.wallet);
console.log(walletId);
this.wallet[walletId].order = index; this.wallet[walletId].order = index;
} }

View File

@ -14,6 +14,7 @@ import { OnGoingProcessProvider } from '../on-going-process/on-going-process';
import { TouchIdProvider } from '../touchid/touchid'; import { TouchIdProvider } from '../touchid/touchid';
import * as lodash from 'lodash'; import * as lodash from 'lodash';
import { FeeProvider } from '../fee/fee';
/* TODO LIST: /* TODO LIST:
@ -55,7 +56,8 @@ export class WalletProvider {
private popupProvider: PopupProvider, private popupProvider: PopupProvider,
private ongoingProcess: OnGoingProcessProvider, private ongoingProcess: OnGoingProcessProvider,
private touchidProvider: TouchIdProvider, private touchidProvider: TouchIdProvider,
private events: Events private events: Events,
private feeProvider: FeeProvider
) { ) {
this.logger.info('WalletService initialized.'); this.logger.info('WalletService initialized.');
} }
@ -426,7 +428,7 @@ export class WalletProvider {
}); });
} }
private updateLocalTxHistory(wallet: any, opts: any) { private updateLocalTxHistory(wallet: any, opts: any): Promise<any> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
opts = opts ? opts : {}; opts = opts ? opts : {};
let FIRST_LIMIT = 5; let FIRST_LIMIT = 5;
@ -436,10 +438,6 @@ export class WalletProvider {
this.progressFn[walletId] = opts.progressFn || (() => { }); this.progressFn[walletId] = opts.progressFn || (() => { });
let foundLimitTx = []; let foundLimitTx = [];
if (opts.feeLevels) {
opts.lowAmount = this.getLowAmount(wallet, opts.feeLevels);
};
let fixTxsUnit = (txs: any): void => { let fixTxsUnit = (txs: any): void => {
if (!txs || !txs[0] || !txs[0].amountStr) return; if (!txs || !txs[0] || !txs[0].amountStr) return;
@ -570,7 +568,11 @@ export class WalletProvider {
}); });
}; };
updateLowAmount(txs); this.getLowAmount(wallet).then((fee) => {
opts.lowAmount = fee;
updateLowAmount(txs);
});
updateNotes().then(() => { updateNotes().then(() => {
@ -661,21 +663,31 @@ export class WalletProvider {
} }
// Approx utxo amount, from which the uxto is economically redeemable // Approx utxo amount, from which the uxto is economically redeemable
public getLowAmount(wallet: any, feeLevels?: any, nbOutputs?: number) { public getLowAmount(wallet: any): Promise<any> {
let minFee: number = this.getMinFee(wallet, feeLevels, nbOutputs); return new Promise((resolve, reject) => {
return (minFee / this.LOW_AMOUNT_RATIO); this.getMinFee(wallet).then((fee) => {
let minFee: number = fee;
return resolve(minFee / this.LOW_AMOUNT_RATIO);
}).catch((err) => {
return reject(err);
});
});
} }
// Approx utxo amount, from which the uxto is economically redeemable // Approx utxo amount, from which the uxto is economically redeemable
private getMinFee(wallet: any, feeLevels: any, nbOutputs?: number): number { public getMinFee(wallet: any, nbOutputs?: number): Promise<any> {
let level: any = lodash.find(feeLevels[wallet.network], (o) => { return new Promise((resolve, reject) => {
return o.level == 'normal'; this.feeProvider.getFeeLevels(wallet.coin).then((data) => {
}); let lowLevelRate = (lodash.find(data.levels[wallet.network], {
let lowLevelRate = parseInt((level.feePerKb / 1000).toFixed(0)); level: 'normal',
}).feePerKb / 1000).toFixed(0);
var size = this.getEstimatedTxSize(wallet, nbOutputs); let size = this.getEstimatedTxSize(wallet, nbOutputs);
return size * lowLevelRate; return resolve(size * parseInt(lowLevelRate));
} }).catch((err) => {
return reject(err);
});
})
};
// These 2 functions were taken from // These 2 functions were taken from
// https://github.com/bitpay/bitcore-wallet-service/blob/master/lib/model/txproposal.js#L243 // https://github.com/bitpay/bitcore-wallet-service/blob/master/lib/model/txproposal.js#L243
@ -1007,30 +1019,33 @@ export class WalletProvider {
}); });
} }
public getLowUtxos(wallet: any, levels: any): Promise<any> { public getLowUtxos(wallet: any): Promise<any> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
wallet.getUtxos({ wallet.getUtxos({
coin: wallet.coin coin: wallet.coin
}, (err, resp) => { }, (err, resp) => {
if (err || !resp || !resp.length) return reject(err); if (err || !resp || !resp.length) return reject(err);
let minFee = this.getMinFee(wallet, levels, resp.length); this.getMinFee(wallet, resp.length).then((fee) => {
let minFee = fee;
let balance = lodash.sumBy(resp, 'satoshis');
let balance = lodash.sumBy(resp, 'satoshis'); // for 2 outputs
this.getLowAmount(wallet).then((fee) => {
let lowAmount = fee;
let lowUtxos = lodash.filter(resp, (x: any) => {
return x.satoshis < lowAmount;
});
// for 2 outputs let totalLow = lodash.sumBy(lowUtxos, 'satoshis');
let lowAmount = this.getLowAmount(wallet, levels); return resolve({
let lowUtxos = lodash.filter(resp, (x: any) => { allUtxos: resp || [],
return x.satoshis < lowAmount; lowUtxos: lowUtxos || [],
}); totalLow: totalLow,
warning: minFee / balance > this.TOTAL_LOW_WARNING_RATIO,
let totalLow = lodash.sumBy(lowUtxos, 'satoshis'); minFee: minFee,
return resolve({ });
allUtxos: resp || [], });
lowUtxos: lowUtxos || [],
totalLow: totalLow,
warning: minFee / balance > this.TOTAL_LOW_WARNING_RATIO,
minFee: minFee,
}); });
}); });
}); });