Merge pull request #4480 from mempool/mononaut/mined-acceleration-info
Show accelerated fee rates for mined transactions
This commit is contained in:
commit
51a28b2e01
@ -512,18 +512,32 @@
|
|||||||
<app-fee-rate [fee]="tx.feePerVsize"></app-fee-rate>
|
<app-fee-rate [fee]="tx.feePerVsize"></app-fee-rate>
|
||||||
<ng-template [ngIf]="tx?.status?.confirmed">
|
<ng-template [ngIf]="tx?.status?.confirmed">
|
||||||
|
|
||||||
<app-tx-fee-rating *ngIf="tx.fee && !hasEffectiveFeeRate" [tx]="tx"></app-tx-fee-rating>
|
<app-tx-fee-rating *ngIf="tx.fee && !hasEffectiveFeeRate && !accelerationInfo" [tx]="tx"></app-tx-fee-rating>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr *ngIf="cpfpInfo && hasEffectiveFeeRate">
|
<tr *ngIf="!hasEffectiveFeeRate && accelerationInfo">
|
||||||
<td *ngIf="tx.acceleration" i18n="transaction.accelerated-fee-rate|Accelerated transaction fee rate">Accelerated fee rate</td>
|
<td i18n="transaction.accelerated-fee-rate|Accelerated transaction fee rate">Accelerated fee rate</td>
|
||||||
<td *ngIf="!tx.acceleration" i18n="transaction.effective-fee-rate|Effective transaction fee rate">Effective fee rate</td>
|
<td>
|
||||||
|
<div class="effective-fee-container">
|
||||||
|
<app-fee-rate [fee]="accelerationInfo.effectiveFee + accelerationInfo.feePaid - accelerationInfo.baseFee - accelerationInfo.vsizeFee" [weight]="accelerationInfo.effectiveVsize * 4"></app-fee-rate>
|
||||||
|
|
||||||
|
<span class="badge badge-accelerated" i18n="transaction.audit.accelerated">Accelerated</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr *ngIf="!accelerationInfo && cpfpInfo && hasEffectiveFeeRate">
|
||||||
|
<td *ngIf="tx.acceleration || accelerationInfo" i18n="transaction.accelerated-fee-rate|Accelerated transaction fee rate">Accelerated fee rate</td>
|
||||||
|
<td *ngIf="!(tx.acceleration || accelerationInfo)" i18n="transaction.effective-fee-rate|Effective transaction fee rate">Effective fee rate</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="effective-fee-container">
|
<div class="effective-fee-container">
|
||||||
<app-fee-rate [fee]="tx.effectiveFeePerVsize"></app-fee-rate>
|
<app-fee-rate [fee]="tx.effectiveFeePerVsize"></app-fee-rate>
|
||||||
<ng-template [ngIf]="tx?.status?.confirmed">
|
<ng-template [ngIf]="tx?.status?.confirmed">
|
||||||
<app-tx-fee-rating class="ml-2 mr-2 effective-fee-rating" *ngIf="tx.fee || tx.effectiveFeePerVsize" [tx]="tx"></app-tx-fee-rating>
|
<app-tx-fee-rating class="ml-2 mr-2 effective-fee-rating" *ngIf="!accelerationInfo && (tx.fee || tx.effectiveFeePerVsize)" [tx]="tx"></app-tx-fee-rating>
|
||||||
|
<ng-template [ngIf]="accelerationInfo">
|
||||||
|
|
||||||
|
<span class="badge badge-accelerated" i18n="transaction.audit.accelerated">Accelerated</span>
|
||||||
|
</ng-template>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</div>
|
</div>
|
||||||
<button *ngIf="cpfpInfo.bestDescendant || cpfpInfo.descendants?.length || cpfpInfo.ancestors?.length" type="button" class="btn btn-outline-info btn-sm btn-small-height float-right" (click)="showCpfpDetails = !showCpfpDetails">CPFP <fa-icon [icon]="['fas', 'info-circle']" [fixedWidth]="true"></fa-icon></button>
|
<button *ngIf="cpfpInfo.bestDescendant || cpfpInfo.descendants?.length || cpfpInfo.ancestors?.length" type="button" class="btn btn-outline-info btn-sm btn-small-height float-right" (click)="showCpfpDetails = !showCpfpDetails">CPFP <fa-icon [icon]="['fas', 'info-circle']" [fixedWidth]="true"></fa-icon></button>
|
||||||
|
@ -60,6 +60,11 @@
|
|||||||
top: -1px;
|
top: -1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.badge.badge-accelerated {
|
||||||
|
background-color: #653b9c;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
.btn-small-height {
|
.btn-small-height {
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ import { ApiService } from '../../services/api.service';
|
|||||||
import { SeoService } from '../../services/seo.service';
|
import { SeoService } from '../../services/seo.service';
|
||||||
import { StorageService } from '../../services/storage.service';
|
import { StorageService } from '../../services/storage.service';
|
||||||
import { seoDescriptionNetwork } from '../../shared/common.utils';
|
import { seoDescriptionNetwork } from '../../shared/common.utils';
|
||||||
import { BlockExtended, CpfpInfo, RbfTree, MempoolPosition, DifficultyAdjustment } from '../../interfaces/node-api.interface';
|
import { BlockExtended, CpfpInfo, RbfTree, MempoolPosition, DifficultyAdjustment, Acceleration } from '../../interfaces/node-api.interface';
|
||||||
import { LiquidUnblinding } from './liquid-ublinding';
|
import { LiquidUnblinding } from './liquid-ublinding';
|
||||||
import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe';
|
import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe';
|
||||||
import { Price, PriceService } from '../../services/price.service';
|
import { Price, PriceService } from '../../services/price.service';
|
||||||
@ -49,6 +49,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
fetchCpfpSubscription: Subscription;
|
fetchCpfpSubscription: Subscription;
|
||||||
fetchRbfSubscription: Subscription;
|
fetchRbfSubscription: Subscription;
|
||||||
fetchCachedTxSubscription: Subscription;
|
fetchCachedTxSubscription: Subscription;
|
||||||
|
fetchAccelerationSubscription: Subscription;
|
||||||
txReplacedSubscription: Subscription;
|
txReplacedSubscription: Subscription;
|
||||||
txRbfInfoSubscription: Subscription;
|
txRbfInfoSubscription: Subscription;
|
||||||
mempoolPositionSubscription: Subscription;
|
mempoolPositionSubscription: Subscription;
|
||||||
@ -62,12 +63,14 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
rbfReplaces: string[];
|
rbfReplaces: string[];
|
||||||
rbfInfo: RbfTree;
|
rbfInfo: RbfTree;
|
||||||
cpfpInfo: CpfpInfo | null;
|
cpfpInfo: CpfpInfo | null;
|
||||||
|
accelerationInfo: Acceleration | null = null;
|
||||||
sigops: number | null;
|
sigops: number | null;
|
||||||
adjustedVsize: number | null;
|
adjustedVsize: number | null;
|
||||||
showCpfpDetails = false;
|
showCpfpDetails = false;
|
||||||
fetchCpfp$ = new Subject<string>();
|
fetchCpfp$ = new Subject<string>();
|
||||||
fetchRbfHistory$ = new Subject<string>();
|
fetchRbfHistory$ = new Subject<string>();
|
||||||
fetchCachedTx$ = new Subject<string>();
|
fetchCachedTx$ = new Subject<string>();
|
||||||
|
fetchAcceleration$ = new Subject<string>();
|
||||||
isCached: boolean = false;
|
isCached: boolean = false;
|
||||||
now = Date.now();
|
now = Date.now();
|
||||||
da$: Observable<DifficultyAdjustment>;
|
da$: Observable<DifficultyAdjustment>;
|
||||||
@ -238,6 +241,24 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.fetchAccelerationSubscription = this.fetchAcceleration$.pipe(
|
||||||
|
tap(() => {
|
||||||
|
this.accelerationInfo = null;
|
||||||
|
}),
|
||||||
|
switchMap((blockHash: string) => {
|
||||||
|
return this.apiService.getAccelerationHistory$({ blockHash });
|
||||||
|
}),
|
||||||
|
catchError(() => {
|
||||||
|
return of(null);
|
||||||
|
})
|
||||||
|
).subscribe((accelerationHistory) => {
|
||||||
|
for (const acceleration of accelerationHistory) {
|
||||||
|
if (acceleration.txid === this.txId) {
|
||||||
|
this.accelerationInfo = acceleration;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.mempoolPositionSubscription = this.stateService.mempoolTxPosition$.subscribe(txPosition => {
|
this.mempoolPositionSubscription = this.stateService.mempoolTxPosition$.subscribe(txPosition => {
|
||||||
this.now = Date.now();
|
this.now = Date.now();
|
||||||
if (txPosition && txPosition.txid === this.txId && txPosition.position) {
|
if (txPosition && txPosition.txid === this.txId && txPosition.position) {
|
||||||
@ -365,6 +386,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
this.getTransactionTime();
|
this.getTransactionTime();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
this.fetchAcceleration$.next(tx.status.block_hash);
|
||||||
this.transactionTime = 0;
|
this.transactionTime = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -417,6 +439,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
};
|
};
|
||||||
this.stateService.markBlock$.next({ blockHeight: block.height });
|
this.stateService.markBlock$.next({ blockHeight: block.height });
|
||||||
this.audioService.playSound('magic');
|
this.audioService.playSound('magic');
|
||||||
|
this.fetchAcceleration$.next(block.id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -585,6 +608,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
this.rbfInfo = null;
|
this.rbfInfo = null;
|
||||||
this.rbfReplaces = [];
|
this.rbfReplaces = [];
|
||||||
this.showCpfpDetails = false;
|
this.showCpfpDetails = false;
|
||||||
|
this.accelerationInfo = null;
|
||||||
this.txInBlockIndex = null;
|
this.txInBlockIndex = null;
|
||||||
this.mempoolPosition = null;
|
this.mempoolPosition = null;
|
||||||
document.body.scrollTo(0, 0);
|
document.body.scrollTo(0, 0);
|
||||||
@ -664,6 +688,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
this.fetchCpfpSubscription.unsubscribe();
|
this.fetchCpfpSubscription.unsubscribe();
|
||||||
this.fetchRbfSubscription.unsubscribe();
|
this.fetchRbfSubscription.unsubscribe();
|
||||||
this.fetchCachedTxSubscription.unsubscribe();
|
this.fetchCachedTxSubscription.unsubscribe();
|
||||||
|
this.fetchAccelerationSubscription.unsubscribe();
|
||||||
this.txReplacedSubscription.unsubscribe();
|
this.txReplacedSubscription.unsubscribe();
|
||||||
this.txRbfInfoSubscription.unsubscribe();
|
this.txRbfInfoSubscription.unsubscribe();
|
||||||
this.queryParamsSubscription.unsubscribe();
|
this.queryParamsSubscription.unsubscribe();
|
||||||
|
@ -302,3 +302,26 @@ export interface INode {
|
|||||||
funding_balance?: number;
|
funding_balance?: number;
|
||||||
closing_balance?: number;
|
closing_balance?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Acceleration {
|
||||||
|
txid: string;
|
||||||
|
status: 'requested' | 'accelerating' | 'mined' | 'completed' | 'failed';
|
||||||
|
pools: number[];
|
||||||
|
feePaid: number;
|
||||||
|
added: number; // timestamp
|
||||||
|
lastUpdated: number; // timestamp
|
||||||
|
baseFee: number;
|
||||||
|
vsizeFee: number;
|
||||||
|
effectiveFee: number;
|
||||||
|
effectiveVsize: number;
|
||||||
|
feeDelta: number;
|
||||||
|
blockHash: string;
|
||||||
|
blockHeight: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AccelerationHistoryParams {
|
||||||
|
timeframe?: string,
|
||||||
|
status?: string,
|
||||||
|
pool?: string,
|
||||||
|
blockHash?: string,
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { HttpClient, HttpParams, HttpResponse } from '@angular/common/http';
|
import { HttpClient, HttpParams, HttpResponse } from '@angular/common/http';
|
||||||
import { CpfpInfo, OptimizedMempoolStats, AddressInformation, LiquidPegs, ITranslators,
|
import { CpfpInfo, OptimizedMempoolStats, AddressInformation, LiquidPegs, ITranslators,
|
||||||
PoolStat, BlockExtended, TransactionStripped, RewardStats, AuditScore, BlockSizesAndWeights, RbfTree, BlockAudit } from '../interfaces/node-api.interface';
|
PoolStat, BlockExtended, TransactionStripped, RewardStats, AuditScore, BlockSizesAndWeights, RbfTree, BlockAudit, Acceleration, AccelerationHistoryParams } from '../interfaces/node-api.interface';
|
||||||
import { BehaviorSubject, Observable, catchError, filter, of, shareReplay, take, tap } from 'rxjs';
|
import { BehaviorSubject, Observable, catchError, filter, of, shareReplay, take, tap } from 'rxjs';
|
||||||
import { StateService } from './state.service';
|
import { StateService } from './state.service';
|
||||||
import { IBackendInfo, WebsocketResponse } from '../interfaces/websocket.interface';
|
import { IBackendInfo, WebsocketResponse } from '../interfaces/websocket.interface';
|
||||||
@ -428,4 +428,12 @@ export class ApiService {
|
|||||||
accelerate$(txInput: string, userBid: number) {
|
accelerate$(txInput: string, userBid: number) {
|
||||||
return this.httpClient.post<any>(`${SERVICES_API_PREFIX}/accelerator/accelerate`, { txInput: txInput, userBid: userBid });
|
return this.httpClient.post<any>(`${SERVICES_API_PREFIX}/accelerator/accelerate`, { txInput: txInput, userBid: userBid });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getAccelerations$(): Observable<Acceleration[]> {
|
||||||
|
return this.httpClient.get<Acceleration[]>(`${SERVICES_API_PREFIX}/accelerator/accelerations`);
|
||||||
|
}
|
||||||
|
|
||||||
|
getAccelerationHistory$(params: AccelerationHistoryParams): Observable<Acceleration[]> {
|
||||||
|
return this.httpClient.get<Acceleration[]>(`${SERVICES_API_PREFIX}/accelerator/accelerations/history`, { params: { ...params } });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user