diff --git a/frontend/src/app/dashboard/dashboard.component.html b/frontend/src/app/dashboard/dashboard.component.html
index 620678a28..90ec01e1d 100644
--- a/frontend/src/app/dashboard/dashboard.component.html
+++ b/frontend/src/app/dashboard/dashboard.component.html
@@ -75,36 +75,31 @@
-
- Latest blocks
+
+ Latest Replacements
-
+
- | Height |
- Mined |
- Pool |
- TXs |
- Size |
+ TXID |
+ Old fee |
+ New fee |
+ |
-
- | {{ block.height }} |
- |
-
-
-
- {{ block.extras.pool.name }}
+
+ |
+
+
|
- {{ block.tx_count | number }} |
-
-
+ | |
+ |
+
+ Mined
+ Full RBF
+ RBF
|
|
diff --git a/frontend/src/app/dashboard/dashboard.component.scss b/frontend/src/app/dashboard/dashboard.component.scss
index eb466fc16..5633a3c7e 100644
--- a/frontend/src/app/dashboard/dashboard.component.scss
+++ b/frontend/src/app/dashboard/dashboard.component.scss
@@ -175,40 +175,34 @@
height: 18px;
}
-.lastest-blocks-table {
+.lastest-replacements-table {
width: 100%;
text-align: left;
+ table-layout:fixed;
tr, td, th {
border: 0px;
- padding-top: 0.65rem !important;
- padding-bottom: 0.7rem !important;
+ padding-top: 0.71rem !important;
+ padding-bottom: 0.75rem !important;
}
- .table-cell-height {
- width: 15%;
+ td {
+ overflow:hidden;
+ width: 25%;
}
- .table-cell-mined {
- width: 35%;
- text-align: left;
+ .table-cell-txid {
+ width: 33%;
+ text-align: start;
}
- .table-cell-transaction-count {
- display: none;
- text-align: right;
- width: 20%;
- display: table-cell;
+ .table-cell-old-fee {
+ width: 33%;
+ text-align: end;
}
- .table-cell-size {
- display: none;
- text-align: center;
- width: 30%;
- @media (min-width: 485px) {
- display: table-cell;
- }
- @media (min-width: 768px) {
- display: none;
- }
- @media (min-width: 992px) {
- display: table-cell;
- }
+ .table-cell-new-fee {
+ width: 33%;
+ text-align: end;
+ }
+ .table-cell-badges {
+ width: 25%;
+ text-align: end;
}
}
diff --git a/frontend/src/app/dashboard/dashboard.component.ts b/frontend/src/app/dashboard/dashboard.component.ts
index 6cf487be6..4ef4501aa 100644
--- a/frontend/src/app/dashboard/dashboard.component.ts
+++ b/frontend/src/app/dashboard/dashboard.component.ts
@@ -1,7 +1,7 @@
import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core';
import { combineLatest, merge, Observable, of, Subscription } from 'rxjs';
import { filter, map, scan, share, switchMap, tap } from 'rxjs/operators';
-import { BlockExtended, OptimizedMempoolStats } from '../interfaces/node-api.interface';
+import { BlockExtended, OptimizedMempoolStats, RbfTree } from '../interfaces/node-api.interface';
import { MempoolInfo, TransactionStripped } from '../interfaces/websocket.interface';
import { ApiService } from '../services/api.service';
import { StateService } from '../services/state.service';
@@ -25,6 +25,17 @@ interface MempoolStatsData {
weightPerSecond: any;
}
+interface ReplacementInfo {
+ tree: RbfTree;
+ mined: boolean;
+ fullRbf: boolean;
+ txid: string;
+ oldFee: number;
+ oldVsize: number;
+ newFee: number;
+ newVsize: number;
+}
+
@Component({
selector: 'app-dashboard',
templateUrl: './dashboard.component.html',
@@ -38,8 +49,8 @@ export class DashboardComponent implements OnInit, OnDestroy {
mempoolInfoData$: Observable;
mempoolLoadingStatus$: Observable;
vBytesPerSecondLimit = 1667;
- blocks$: Observable;
transactions$: Observable;
+ replacements$: Observable;
latestBlockHeight: number;
mempoolTransactionsWeightPerSecondData: any;
mempoolStats$: Observable;
@@ -64,6 +75,7 @@ export class DashboardComponent implements OnInit, OnDestroy {
this.isLoadingWebSocket$ = this.stateService.isLoadingWebSocket$;
this.seoService.resetTitle();
this.websocketService.want(['blocks', 'stats', 'mempool-blocks', 'live-2h-chart']);
+ this.websocketService.startTrackRbf('all');
this.network$ = merge(of(''), this.stateService.networkChanged$);
this.mempoolLoadingStatus$ = this.stateService.loadingIndicators$
.pipe(
@@ -130,23 +142,6 @@ export class DashboardComponent implements OnInit, OnDestroy {
}),
);
- this.blocks$ = this.stateService.blocks$
- .pipe(
- tap((blocks) => {
- this.latestBlockHeight = blocks[0].height;
- }),
- switchMap((blocks) => {
- if (this.stateService.env.MINING_DASHBOARD === true) {
- for (const block of blocks) {
- // @ts-ignore: Need to add an extra field for the template
- block.extras.pool.logo = `/resources/mining-pools/` +
- block.extras.pool.name.toLowerCase().replace(' ', '').replace('.', '') + '.svg';
- }
- }
- return of(blocks.slice(0, 6));
- })
- );
-
this.transactions$ = this.stateService.transactions$
.pipe(
scan((acc, tx) => {
@@ -159,6 +154,31 @@ export class DashboardComponent implements OnInit, OnDestroy {
}, []),
);
+ this.replacements$ = this.stateService.rbfLatest$.pipe(
+ switchMap((rbfList) => {
+ const replacements = rbfList.slice(0, 6).map(rbfTree => {
+ let oldFee = 0;
+ let oldVsize = 0;
+ for (const replaced of rbfTree.replaces) {
+ oldFee += replaced.tx.fee;
+ oldVsize += replaced.tx.vsize;
+ }
+ this.checkFullRbf(rbfTree);
+ return {
+ tree: rbfTree,
+ txid: rbfTree.tx.txid,
+ mined: rbfTree.tx.mined,
+ fullRbf: rbfTree.tx.fullRbf,
+ oldFee,
+ oldVsize,
+ newFee: rbfTree.tx.fee,
+ newVsize: rbfTree.tx.vsize,
+ };
+ });
+ return of(replacements);
+ })
+ );
+
this.mempoolStats$ = this.stateService.connectionState$
.pipe(
filter((state) => state === 2),
@@ -219,4 +239,16 @@ export class DashboardComponent implements OnInit, OnDestroy {
trackByBlock(index: number, block: BlockExtended) {
return block.height;
}
+
+ checkFullRbf(tree: RbfTree): void {
+ let fullRbf = false;
+ for (const replaced of tree.replaces) {
+ if (!replaced.tx.rbf) {
+ fullRbf = true;
+ }
+ replaced.replacedBy = tree.tx;
+ this.checkFullRbf(replaced);
+ }
+ tree.tx.fullRbf = fullRbf;
+ }
}