diff --git a/frontend/src/app/components/accelerate-preview/accelerate-preview.component.scss b/frontend/src/app/components/accelerate-preview/accelerate-preview.component.scss
index fa598f3a3..1191d882e 100644
--- a/frontend/src/app/components/accelerate-preview/accelerate-preview.component.scss
+++ b/frontend/src/app/components/accelerate-preview/accelerate-preview.component.scss
@@ -107,6 +107,11 @@
margin-top: 1em;
}
+.col.pie {
+ flex-grow: 0;
+ padding: 0 1em;
+}
+
.item {
white-space: initial;
}
diff --git a/frontend/src/app/components/accelerate-preview/accelerate-preview.component.ts b/frontend/src/app/components/accelerate-preview/accelerate-preview.component.ts
index 76833bb1a..6d4c88a00 100644
--- a/frontend/src/app/components/accelerate-preview/accelerate-preview.component.ts
+++ b/frontend/src/app/components/accelerate-preview/accelerate-preview.component.ts
@@ -6,6 +6,9 @@ import { nextRoundNumber } from '../../shared/common.utils';
import { ServicesApiServices } from '../../services/services-api.service';
import { AudioService } from '../../services/audio.service';
import { StateService } from '../../services/state.service';
+import { MiningStats } from '../../services/mining.service';
+import { EtaService } from '../../services/eta.service';
+import { DifficultyAdjustment, MempoolPosition, SinglePoolStats } from '../../interfaces/node-api.interface';
export type AccelerationEstimate = {
txSummary: TxSummary;
@@ -40,7 +43,9 @@ export const MAX_BID_RATIO = 4;
styleUrls: ['accelerate-preview.component.scss']
})
export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges {
- @Input() tx: Transaction | undefined;
+ @Input() tx: Transaction;
+ @Input() mempoolPosition: MempoolPosition;
+ @Input() miningStats: MiningStats;
@Input() scrollEvent: boolean;
math = Math;
@@ -48,7 +53,12 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges
showSuccess = false;
estimateSubscription: Subscription;
accelerationSubscription: Subscription;
+ difficultySubscription: Subscription;
+ da: DifficultyAdjustment;
estimate: any;
+ hashratePercentage?: number;
+ ETA?: number;
+ acceleratedETA?: number;
hasAncestors: boolean = false;
minExtraCost = 0;
minBidAllowed = 0;
@@ -67,6 +77,7 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges
public stateService: StateService,
private servicesApiService: ServicesApiServices,
private storageService: StorageService,
+ private etaService: EtaService,
private audioService: AudioService,
private cd: ChangeDetectorRef
) {
@@ -76,16 +87,24 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges
if (this.estimateSubscription) {
this.estimateSubscription.unsubscribe();
}
+ this.difficultySubscription.unsubscribe();
}
ngOnInit() {
this.accelerationUUID = window.crypto.randomUUID();
+ this.difficultySubscription = this.stateService.difficultyAdjustment$.subscribe(da => {
+ this.da = da;
+ this.updateETA();
+ })
}
ngOnChanges(changes: SimpleChanges): void {
if (changes.scrollEvent) {
this.scrollToPreview('acceleratePreviewAnchor', 'start');
}
+ if (changes.miningStats || changes.mempoolPosition) {
+ this.updateETA();
+ }
}
ngAfterViewInit() {
@@ -113,6 +132,8 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges
}
}
+ this.updateETA();
+
this.hasAncestors = this.estimate.txSummary.ancestorCount > 1;
// Make min extra fee at least 50% of the current tx fee
@@ -157,6 +178,36 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges
).subscribe();
}
+ updateETA(): void {
+ if (!this.mempoolPosition || !this.estimate?.pools?.length || !this.miningStats || !this.da) {
+ this.hashratePercentage = undefined;
+ this.ETA = undefined;
+ this.acceleratedETA = undefined;
+ return;
+ }
+ const pools: { [id: number]: SinglePoolStats } = {};
+ for (const pool of this.miningStats.pools) {
+ pools[pool.poolUniqueId] = pool;
+ }
+
+ let totalAcceleratedHashrate = 0;
+ for (const poolId of this.estimate.pools) {
+ const pool = pools[poolId];
+ if (!pool) {
+ continue;
+ }
+ totalAcceleratedHashrate += pool.lastEstimatedHashrate;
+ }
+ const acceleratingHashrateFraction = (totalAcceleratedHashrate / this.miningStats.lastEstimatedHashrate)
+ this.hashratePercentage = acceleratingHashrateFraction * 100;
+
+ this.ETA = Date.now() + this.da.timeAvg * this.mempoolPosition.block;
+ this.acceleratedETA = this.etaService.calculateETAFromShares([
+ { block: this.mempoolPosition.block, hashrateShare: (1 - acceleratingHashrateFraction) },
+ { block: 0, hashrateShare: acceleratingHashrateFraction },
+ ], this.da).time;
+ }
+
/**
* User changed his bid
*/
diff --git a/frontend/src/app/components/acceleration/active-acceleration-box/active-acceleration-box.component.html b/frontend/src/app/components/acceleration/active-acceleration-box/active-acceleration-box.component.html
index d009a5e63..711269a47 100644
--- a/frontend/src/app/components/acceleration/active-acceleration-box/active-acceleration-box.component.html
+++ b/frontend/src/app/components/acceleration/active-acceleration-box/active-acceleration-box.component.html
@@ -1,3 +1,6 @@
+@if (chartOnly) {
+
+} @else {
@@ -12,23 +15,7 @@
-
- @if (tx && (tx.acceleratedBy || accelerationInfo) && miningStats) {
-
- } @else {
-
- }
-
+
|
@@ -38,4 +25,25 @@
-
\ No newline at end of file
+
+}
+
+
+
+ @if (chartOptions && miningStats) {
+
+ } @else {
+
+ }
+
+
\ No newline at end of file
diff --git a/frontend/src/app/components/acceleration/active-acceleration-box/active-acceleration-box.component.ts b/frontend/src/app/components/acceleration/active-acceleration-box/active-acceleration-box.component.ts
index f52c45041..2d94cad50 100644
--- a/frontend/src/app/components/acceleration/active-acceleration-box/active-acceleration-box.component.ts
+++ b/frontend/src/app/components/acceleration/active-acceleration-box/active-acceleration-box.component.ts
@@ -15,10 +15,12 @@ export class ActiveAccelerationBox implements OnChanges {
@Input() tx: Transaction;
@Input() accelerationInfo: Acceleration;
@Input() miningStats: MiningStats;
+ @Input() pools: number[];
+ @Input() chartOnly: boolean = false;
acceleratedByPercentage: string = '';
- chartOptions: EChartsOption = {};
+ chartOptions: EChartsOption;
chartInitOptions = {
renderer: 'svg',
};
@@ -28,12 +30,13 @@ export class ActiveAccelerationBox implements OnChanges {
constructor() {}
ngOnChanges(changes: SimpleChanges): void {
- if (this.tx && (this.tx.acceleratedBy || this.accelerationInfo) && this.miningStats) {
- this.prepareChartOptions();
+ const pools = this.pools || this.accelerationInfo?.pools || this.tx.acceleratedBy;
+ if (pools && this.miningStats) {
+ this.prepareChartOptions(pools);
}
}
- getChartData() {
+ getChartData(poolList: number[]) {
const data: object[] = [];
const pools: { [id: number]: SinglePoolStats } = {};
for (const pool of this.miningStats.pools) {
@@ -73,22 +76,22 @@ export class ActiveAccelerationBox implements OnChanges {
});
let totalAcceleratedHashrate = 0;
- for (const poolId of (this.accelerationInfo?.pools || this.tx.acceleratedBy || [])) {
+ for (const poolId of poolList || []) {
const pool = pools[poolId];
if (!pool) {
continue;
}
- totalAcceleratedHashrate += parseFloat(pool.lastEstimatedHashrate);
+ totalAcceleratedHashrate += pool.lastEstimatedHashrate;
}
- this.acceleratedByPercentage = ((totalAcceleratedHashrate / parseFloat(this.miningStats.lastEstimatedHashrate)) * 100).toFixed(1) + '%';
+ this.acceleratedByPercentage = ((totalAcceleratedHashrate / this.miningStats.lastEstimatedHashrate) * 100).toFixed(1) + '%';
data.push(getDataItem(
totalAcceleratedHashrate,
'var(--mainnet-alt)',
`${this.acceleratedByPercentage} accelerating`,
) as PieSeriesOption);
- const notAcceleratedByPercentage = ((1 - (totalAcceleratedHashrate / parseFloat(this.miningStats.lastEstimatedHashrate))) * 100).toFixed(1) + '%';
+ const notAcceleratedByPercentage = ((1 - (totalAcceleratedHashrate / this.miningStats.lastEstimatedHashrate)) * 100).toFixed(1) + '%';
data.push(getDataItem(
- (parseFloat(this.miningStats.lastEstimatedHashrate) - totalAcceleratedHashrate),
+ (this.miningStats.lastEstimatedHashrate - totalAcceleratedHashrate),
'rgba(127, 127, 127, 0.3)',
`${notAcceleratedByPercentage} not accelerating`,
) as PieSeriesOption);
@@ -96,7 +99,7 @@ export class ActiveAccelerationBox implements OnChanges {
return data;
}
- prepareChartOptions() {
+ prepareChartOptions(pools: number[]) {
this.chartOptions = {
animation: false,
grid: {
@@ -113,7 +116,7 @@ export class ActiveAccelerationBox implements OnChanges {
{
type: 'pie',
radius: '100%',
- data: this.getChartData(),
+ data: this.getChartData(pools),
}
]
};
diff --git a/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts b/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts
index dee770cd8..b4d698bb2 100644
--- a/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts
+++ b/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts
@@ -2,6 +2,7 @@ import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRe
import { Subscription, Observable, of, combineLatest } from 'rxjs';
import { MempoolBlock } from '../../interfaces/websocket.interface';
import { StateService } from '../../services/state.service';
+import { EtaService } from '../../services/eta.service';
import { Router } from '@angular/router';
import { delay, filter, map, switchMap, tap } from 'rxjs/operators';
import { feeLevels } from '../../app.constants';
@@ -89,6 +90,7 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy {
constructor(
private router: Router,
public stateService: StateService,
+ private etaService: EtaService,
private themeService: ThemeService,
private cd: ChangeDetectorRef,
private relativeUrlPipe: RelativeUrlPipe,
@@ -437,34 +439,9 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy {
this.rightPosition = positionOfBlock + positionInBlock;
}
} else {
- let found = false;
- for (let txInBlockIndex = 0; txInBlockIndex < this.mempoolBlocks.length && !found; txInBlockIndex++) {
- const block = this.mempoolBlocks[txInBlockIndex];
- for (let i = 0; i < block.feeRange.length - 1 && !found; i++) {
- if (this.txFeePerVSize < block.feeRange[i + 1] && this.txFeePerVSize >= block.feeRange[i]) {
- const feeRangeIndex = i;
- const feeRangeChunkSize = 1 / (block.feeRange.length - 1);
-
- const txFee = this.txFeePerVSize - block.feeRange[i];
- const max = block.feeRange[i + 1] - block.feeRange[i];
- const blockLocation = txFee / max;
-
- const chunkPositionOffset = blockLocation * feeRangeChunkSize;
- const feePosition = feeRangeChunkSize * feeRangeIndex + chunkPositionOffset;
-
- const blockedFilledPercentage = (block.blockVSize > this.stateService.blockVSize ? this.stateService.blockVSize : block.blockVSize) / this.stateService.blockVSize;
- const arrowRightPosition = txInBlockIndex * (this.blockWidth + this.blockPadding)
- + ((1 - feePosition) * blockedFilledPercentage * this.blockWidth);
-
- this.rightPosition = arrowRightPosition;
- found = true;
- }
- }
- if (this.txFeePerVSize >= block.feeRange[block.feeRange.length - 1]) {
- this.rightPosition = txInBlockIndex * (this.blockWidth + this.blockPadding);
- found = true;
- }
- }
+ const estimatedPosition = this.etaService.mempoolPositionFromFees(this.txFeePerVSize, this.mempoolBlocks);
+ this.rightPosition = estimatedPosition.block * (this.blockWidth + this.blockPadding)
+ + ((estimatedPosition.vsize / this.stateService.blockVSize) * this.blockWidth)
}
this.rightPosition = Math.min(this.maxArrowPosition, this.rightPosition);
}
diff --git a/frontend/src/app/components/pool-ranking/pool-ranking.component.ts b/frontend/src/app/components/pool-ranking/pool-ranking.component.ts
index 2d78252ef..2e8a820be 100644
--- a/frontend/src/app/components/pool-ranking/pool-ranking.component.ts
+++ b/frontend/src/app/components/pool-ranking/pool-ranking.component.ts
@@ -163,7 +163,7 @@ export class PoolRankingComponent implements OnInit {
const i = pool.blockCount.toString();
if (this.miningWindowPreference === '24h') {
return `
${pool.name} (${pool.share}%)` +
- pool.lastEstimatedHashrate.toString() + ' ' + miningStats.miningUnits.hashrateUnit +
+ pool.lastEstimatedHashrate.toFixed(2) + ' ' + miningStats.miningUnits.hashrateUnit +
`
` + $localize`${ i }:INTERPOLATION: blocks`;
} else {
return `
${pool.name} (${pool.share}%)` +
@@ -291,7 +291,7 @@ export class PoolRankingComponent implements OnInit {
*/
getEmptyMiningStat(): MiningStats {
return {
- lastEstimatedHashrate: 'Error',
+ lastEstimatedHashrate: 0,
blockCount: 0,
totalEmptyBlock: 0,
totalEmptyBlockRatio: '',
diff --git a/frontend/src/app/components/tracker/tracker.component.html b/frontend/src/app/components/tracker/tracker.component.html
index 1d1399a07..571c02f96 100644
--- a/frontend/src/app/components/tracker/tracker.component.html
+++ b/frontend/src/app/components/tracker/tracker.component.html
@@ -54,7 +54,7 @@