[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>
<page-wallet-item [wallet]="wallet"></page-wallet-item>
<div class="comment">
<span translate>Each bitcoin wallet can generate billions of addresses from your 12-word backup.
A new address is automatically generated and shown each time you receive a payment.
</span><a (click)="showInfo = !showInfo" *ngIf="!showInfo" translate>Why?</a>
<span translate>Each bitcoin wallet can generate billions of addresses from your 12-word backup. A new address is automatically generated
and shown each time you receive a payment.
</span>
<a (click)="showInfo = !showInfo" *ngIf="!showInfo" translate>Why?</a>
<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>
</div>
</div>
@ -41,10 +43,14 @@
<div *ngIf="latestUnused && latestUnused[0]">
<div *ngIf="gapReached">
<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>
</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>
</p>
</div>
@ -65,7 +71,7 @@
</div>
<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>
<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 { PopupProvider } from '../../../../../providers/popup/popup';
import { OnGoingProcessProvider } from '../../../../../providers/on-going-process/on-going-process';
import { TxFormatProvider } from '../../../../../providers/tx-format/tx-format';
//pages
import { AllAddressesPage } from './all-addresses/all-addresses';
@ -28,6 +29,13 @@ export class WalletAddressesPage {
public latestWithBalance: any;
public viewAll: 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 BALANCE_ADDRESS_LIMIT: number;
private withBalance: any;
@ -43,6 +51,7 @@ export class WalletAddressesPage {
private popupProvider: PopupProvider,
private onGoingProcessProvider: OnGoingProcessProvider,
private modalCtrl: ModalController,
private txFormatProvider: TxFormatProvider
) {
this.UNUSED_ADDRESS_LIMIT = 5;
this.BALANCE_ADDRESS_LIMIT = 5;
@ -79,6 +88,26 @@ export class WalletAddressesPage {
this.loading = false;
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 {

View File

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

View File

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

View File

@ -5,7 +5,7 @@
</ion-header>
<ion-content>
<page-wallet-item [wallet]="wallet"></page-wallet-item>
<ion-item-divider *ngIf="!isCordova" color="light"></ion-item-divider>
<button ion-item *ngIf="csvReady && !isCordova" (click)="downloadCSV()">
@ -21,12 +21,12 @@
{{err | translate}}
</ion-note>
</ion-item>
<button ion-item (click)="clearTransactionHistory()">
<span translate>Clear cache</span>
</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
again from the server
</div>

View File

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

View File

@ -24,6 +24,7 @@ export class FeeProvider {
updateTs: 0,
coin: ''
}
constructor(
private configProvider: ConfigProvider,
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 {
this.persistenceProvider.setWalletOrder(walletId, index);
this.logger.debug('Wallet new order stored');
console.log(this.wallet);
console.log(walletId);
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 * as lodash from 'lodash';
import { FeeProvider } from '../fee/fee';
/* TODO LIST:
@ -55,7 +56,8 @@ export class WalletProvider {
private popupProvider: PopupProvider,
private ongoingProcess: OnGoingProcessProvider,
private touchidProvider: TouchIdProvider,
private events: Events
private events: Events,
private feeProvider: FeeProvider
) {
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) => {
opts = opts ? opts : {};
let FIRST_LIMIT = 5;
@ -436,10 +438,6 @@ export class WalletProvider {
this.progressFn[walletId] = opts.progressFn || (() => { });
let foundLimitTx = [];
if (opts.feeLevels) {
opts.lowAmount = this.getLowAmount(wallet, opts.feeLevels);
};
let fixTxsUnit = (txs: any): void => {
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(() => {
@ -661,21 +663,31 @@ export class WalletProvider {
}
// Approx utxo amount, from which the uxto is economically redeemable
public getLowAmount(wallet: any, feeLevels?: any, nbOutputs?: number) {
let minFee: number = this.getMinFee(wallet, feeLevels, nbOutputs);
return (minFee / this.LOW_AMOUNT_RATIO);
public getLowAmount(wallet: any): Promise<any> {
return new Promise((resolve, reject) => {
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
private getMinFee(wallet: any, feeLevels: any, nbOutputs?: number): number {
let level: any = lodash.find(feeLevels[wallet.network], (o) => {
return o.level == 'normal';
});
let lowLevelRate = parseInt((level.feePerKb / 1000).toFixed(0));
var size = this.getEstimatedTxSize(wallet, nbOutputs);
return size * lowLevelRate;
}
public getMinFee(wallet: any, nbOutputs?: number): Promise<any> {
return new Promise((resolve, reject) => {
this.feeProvider.getFeeLevels(wallet.coin).then((data) => {
let lowLevelRate = (lodash.find(data.levels[wallet.network], {
level: 'normal',
}).feePerKb / 1000).toFixed(0);
let size = this.getEstimatedTxSize(wallet, nbOutputs);
return resolve(size * parseInt(lowLevelRate));
}).catch((err) => {
return reject(err);
});
})
};
// These 2 functions were taken from
// 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) => {
wallet.getUtxos({
coin: wallet.coin
}, (err, resp) => {
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 lowAmount = this.getLowAmount(wallet, levels);
let lowUtxos = lodash.filter(resp, (x: any) => {
return x.satoshis < lowAmount;
});
let totalLow = lodash.sumBy(lowUtxos, 'satoshis');
return resolve({
allUtxos: resp || [],
lowUtxos: lowUtxos || [],
totalLow: totalLow,
warning: minFee / balance > this.TOTAL_LOW_WARNING_RATIO,
minFee: minFee,
let totalLow = lodash.sumBy(lowUtxos, 'satoshis');
return resolve({
allUtxos: resp || [],
lowUtxos: lowUtxos || [],
totalLow: totalLow,
warning: minFee / balance > this.TOTAL_LOW_WARNING_RATIO,
minFee: minFee,
});
});
});
});
});