CPFP support (#395)
* CPFP support. fixes #5 fixes #353 fixes #360 * Use effectiveFeePerVsize for mempool statistics. * Renaming endpoint cpfp-info to just cpfp. * Renaming decended to BestDescendant. * Updating language file with new strings.
This commit is contained in:
@@ -40,6 +40,10 @@
|
||||
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/v1/fees/mempool-blocks" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/v1/fees/mempool-blocks</a></td>
|
||||
<td i18n="api-docs.fees.mempool-blocks|API Docs for /api/v1/fees/mempool-blocks">Returns current mempool as projected blocks.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/v1/cpfp/TXID" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/v1/cpfp/:txid</a></td>
|
||||
<td i18n="api-docs.fees.cpfp|API Docs for /api/v1/fees/cpfp">Returns the ancestors and the best descendant fees for a transaction.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</ng-template>
|
||||
|
||||
@@ -73,22 +73,7 @@
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-sm">
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="td-width" i18n="transaction.fee|Transaction fee">Fee</td>
|
||||
<td>{{ tx.fee | number }} <span i18n="transaction.fee.sat|Transaction Fee sat">sat</span> (<app-fiat [value]="tx.fee"></app-fiat>)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td i18n="transaction.fee-per-vbyte|Transaction fee">Fee per vByte</td>
|
||||
<td>
|
||||
{{ tx.fee / (tx.weight / 4) | number : '1.1-1' }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span>
|
||||
|
||||
<app-tx-fee-rating *ngIf="tx.fee" [tx]="tx"></app-tx-fee-rating>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<ng-container *ngTemplateOutlet="feeTable"></ng-container>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -146,18 +131,7 @@
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-sm">
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="td-width" i18n="transaction.fee|Transaction Fee">Fee</td>
|
||||
<td>{{ tx.fee | number }} <span i18n="transaction.fee.sat|Transaction Fee sat">sat</span> (<app-fiat [value]="tx.fee"></app-fiat>)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td i18n="transaction.fee-per-vbyte|Transaction fee">Fee per vByte</td>
|
||||
<td>{{ tx.fee / (tx.weight / 4) | number : '1.1-1' }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<ng-container *ngTemplateOutlet="feeTable"></ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -295,4 +269,36 @@
|
||||
<ng-template let-i #nextBlockEta i18n="mempool-blocks.eta-of-next-block|Block Frequency">In ~{{ i }} minute</ng-template>
|
||||
|
||||
<ng-template #blocksSingular let-i i18n="shared.block">{{ i }} block</ng-template>
|
||||
<ng-template #blocksPlural let-i i18n="shared.blocks">{{ i }} blocks</ng-template>
|
||||
<ng-template #blocksPlural let-i i18n="shared.blocks">{{ i }} blocks</ng-template>
|
||||
|
||||
<ng-template #feeTable>
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="td-width" i18n="transaction.fee|Transaction fee">Fee</td>
|
||||
<td>{{ tx.fee | number }} <span i18n="transaction.fee.sat|Transaction Fee sat">sat</span> (<app-fiat [value]="tx.fee"></app-fiat>)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td i18n="transaction.fee-per-vbyte|Transaction fee">Fee per vByte</td>
|
||||
<td>
|
||||
{{ tx.fee / (tx.weight / 4) | number : '1.1-1' }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span>
|
||||
<ng-template [ngIf]="tx.status.confirmed">
|
||||
|
||||
<app-tx-fee-rating *ngIf="tx.fee && (!tx.effectiveFeePerVsize || tx.effectiveFeePerVsize === tx.fee / (tx.weight / 4))" [tx]="tx"></app-tx-fee-rating>
|
||||
</ng-template>
|
||||
</td>
|
||||
</tr>
|
||||
<tr *ngIf="tx.effectiveFeePerVsize && tx.effectiveFeePerVsize !== tx.fee / (tx.weight / 4)">
|
||||
<td i18n="transaction.effective-fee|Effective transaction fee">Effective fee</td>
|
||||
<td>
|
||||
{{ tx.effectiveFeePerVsize | number : '1.1-1' }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span>
|
||||
<ng-template [ngIf]="tx.status.confirmed">
|
||||
|
||||
<app-tx-fee-rating *ngIf="tx.fee" [tx]="tx"></app-tx-fee-rating>
|
||||
</ng-template>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</ng-template>
|
||||
@@ -91,10 +91,28 @@ export class TransactionComponent implements OnInit, OnDestroy {
|
||||
this.getTransactionTime();
|
||||
}
|
||||
}
|
||||
|
||||
if (this.tx.status.confirmed) {
|
||||
this.stateService.markBlock$.next({ blockHeight: tx.status.block_height });
|
||||
} else {
|
||||
this.stateService.markBlock$.next({ txFeePerVSize: tx.fee / (tx.weight / 4) });
|
||||
if (tx.effectiveFeePerVsize) {
|
||||
this.stateService.markBlock$.next({ txFeePerVSize: tx.effectiveFeePerVsize });
|
||||
} else {
|
||||
this.apiService.getCpfpinfo$(this.tx.txid)
|
||||
.subscribe((cpfpInfo) => {
|
||||
let totalWeight = tx.weight + cpfpInfo.ancestors.reduce((prev, val) => prev + val.weight, 0);
|
||||
let totalFees = tx.fee + cpfpInfo.ancestors.reduce((prev, val) => prev + val.fee, 0);
|
||||
|
||||
if (cpfpInfo.bestDescendant) {
|
||||
totalWeight += cpfpInfo.bestDescendant.weight;
|
||||
totalFees += cpfpInfo.bestDescendant.fee;
|
||||
}
|
||||
|
||||
const effectiveFeePerVsize = totalFees / (totalWeight / 4);
|
||||
this.tx.effectiveFeePerVsize = effectiveFeePerVsize;
|
||||
this.stateService.markBlock$.next({ txFeePerVSize: effectiveFeePerVsize });
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
@@ -139,7 +157,7 @@ export class TransactionComponent implements OnInit, OnDestroy {
|
||||
return;
|
||||
}
|
||||
|
||||
const txFeePerVSize = this.tx.fee / (this.tx.weight / 4);
|
||||
const txFeePerVSize = this.tx.effectiveFeePerVsize || this.tx.fee / (this.tx.weight / 4);
|
||||
|
||||
for (const block of mempoolBlocks) {
|
||||
for (let i = 0; i < block.feeRange.length - 1; i++) {
|
||||
|
||||
@@ -52,7 +52,7 @@ export class TxFeeRatingComponent implements OnInit, OnChanges, OnDestroy {
|
||||
}
|
||||
|
||||
calculateRatings(block: Block) {
|
||||
const feePervByte = this.tx.fee / (this.tx.weight / 4);
|
||||
const feePervByte = this.tx.effectiveFeePerVsize || this.tx.fee / (this.tx.weight / 4);
|
||||
this.medianFeeNeeded = block.medianFee;
|
||||
|
||||
// Block not filled
|
||||
|
||||
@@ -11,6 +11,7 @@ export interface Transaction {
|
||||
|
||||
// Custom properties
|
||||
firstSeen?: number;
|
||||
effectiveFeePerVsize?: number;
|
||||
}
|
||||
|
||||
export interface Recent {
|
||||
|
||||
@@ -8,3 +8,20 @@ export interface OptimizedMempoolStats {
|
||||
mempool_byte_weight: number;
|
||||
vsizes: number[] | string[];
|
||||
}
|
||||
|
||||
interface Ancestor {
|
||||
txid: string;
|
||||
weight: number;
|
||||
fee: number;
|
||||
}
|
||||
|
||||
interface BestDescendant {
|
||||
txid: string;
|
||||
weight: number;
|
||||
fee: number;
|
||||
}
|
||||
|
||||
export interface CpfpInfo {
|
||||
ancestors: Ancestor[];
|
||||
bestDescendant: BestDescendant | null;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||
import { OptimizedMempoolStats } from '../interfaces/node-api.interface';
|
||||
import { CpfpInfo, OptimizedMempoolStats } from '../interfaces/node-api.interface';
|
||||
import { Observable } from 'rxjs';
|
||||
import { StateService } from './state.service';
|
||||
import { WebsocketResponse } from '../interfaces/websocket.interface';
|
||||
@@ -88,4 +88,8 @@ export class ApiService {
|
||||
getInitData$(): Observable<WebsocketResponse> {
|
||||
return this.httpClient.get<WebsocketResponse>(this.apiBaseUrl + this.apiBasePath + '/api/v1/init-data');
|
||||
}
|
||||
|
||||
getCpfpinfo$(txid: string): Observable<CpfpInfo> {
|
||||
return this.httpClient.get<CpfpInfo>(this.apiBaseUrl + this.apiBasePath + '/api/v1/cpfp/' + txid);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user