Add expected hashrate pie chart & eta to acceleration preview

This commit is contained in:
Mononaut
2024-06-06 18:26:43 +00:00
parent 05724b9d58
commit 396b7eb3d3
10 changed files with 150 additions and 57 deletions

View File

@@ -65,10 +65,21 @@
</div>
</div>
<br>
<h5 *ngIf="estimate?.pools?.length" i18n="accelerator.how-much-faster">How much faster?</h5>
<div class="row">
<div class="col">
<small class="form-text text-muted mb-2" i18n="accelerator.hashrate-percentage-description">Your transaction will be prioritized by up to {{ hashratePercentage | number : '1.1-1' }}% of miners.</small>
<small class="form-text text-muted mb-2" i18n="accelerator.time-estimate-description">This will reduce your expected waiting time until the first confirmation to <app-time kind="within" [time]="acceleratedETA" [fastRender]="false" [fixedRender]="true"></app-time></small>
</div>
<div class="col pie">
<app-active-acceleration-box [miningStats]="miningStats" [pools]="estimate.pools" [chartOnly]="true"></app-active-acceleration-box>
</div>
</div>
<br>
<h5 i18n="accelerator.pay-how-much">How much more are you willing to pay?</h5>
<div class="row">
<div class="col">
<small class="form-text text-muted mb-2" i18n="accelerator.transaction-fee-description">Choose the maximum extra transaction fee you're willing to pay to get into the next block.</small>
<small class="form-text text-muted mb-2" i18n="accelerator.transaction-fee-description">Choose the maximum extra transaction fee you're willing to pay.</small>
<div class="form-group">
<div class="fee-card">
<div class="d-flex mb-0">

View File

@@ -107,6 +107,11 @@
margin-top: 1em;
}
.col.pie {
flex-grow: 0;
padding: 0 1em;
}
.item {
white-space: initial;
}

View File

@@ -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
*/

View File

@@ -1,3 +1,6 @@
@if (chartOnly) {
<ng-container *ngTemplateOutlet="pieChart"></ng-container>
} @else {
<table>
<tbody>
<tr>
@@ -12,23 +15,7 @@
</div>
</td>
<td class="pie-chart" rowspan="2">
<div class="chart-container">
@if (tx && (tx.acceleratedBy || accelerationInfo) && miningStats) {
<div
echarts
*browserOnly
class="chart"
[initOpts]="chartInitOptions"
[options]="chartOptions"
style="height: 72px; width: 72px;"
(chartInit)="onChartInit($event)"
></div>
} @else {
<div class="chart-loading">
<div class="spinner-border text-light"></div>
</div>
}
</div>
<ng-container *ngTemplateOutlet="pieChart"></ng-container>
</td>
</tr>
<tr>
@@ -38,4 +25,25 @@
</td>
</tr>
</tbody>
</table>
</table>
}
<ng-template #pieChart>
<div class="chart-container">
@if (chartOptions && miningStats) {
<div
echarts
*browserOnly
class="chart"
[initOpts]="chartInitOptions"
[options]="chartOptions"
style="height: 72px; width: 72px;"
(chartInit)="onChartInit($event)"
></div>
} @else {
<div class="chart-loading">
<div class="spinner-border text-light"></div>
</div>
}
</div>
</ng-template>

View File

@@ -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,7 +76,7 @@ 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;
@@ -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),
}
]
};

View File

@@ -83,7 +83,7 @@
<div class="clearfix"></div>
<div class="box">
<app-accelerate-preview [tx]="tx" [scrollEvent]="scrollIntoAccelPreview"></app-accelerate-preview>
<app-accelerate-preview [tx]="tx" [miningStats]="miningStats" [mempoolPosition]="mempoolPosition" [scrollEvent]="scrollIntoAccelPreview"></app-accelerate-preview>
</div>
</ng-container>

View File

@@ -682,6 +682,10 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
return;
}
this.miningService.getMiningStats('1w').subscribe(stats => {
this.miningStats = stats;
});
document.location.hash = '#accelerate';
this.enterpriseService.goal(8);
this.showAccelerationSummary = true && this.acceleratorAvailable;

View File

@@ -5,6 +5,8 @@ import { TransactionComponent } from './transaction.component';
import { SharedModule } from '../../shared/shared.module';
import { TxBowtieModule } from '../tx-bowtie-graph/tx-bowtie.module';
import { GraphsModule } from '../../graphs/graphs.module';
import { AcceleratePreviewComponent } from '../accelerate-preview/accelerate-preview.component';
import { AccelerateFeeGraphComponent } from '../accelerate-preview/accelerate-fee-graph.component';
const routes: Routes = [
{
@@ -36,6 +38,8 @@ export class TransactionRoutingModule { }
],
declarations: [
TransactionComponent,
AcceleratePreviewComponent,
AccelerateFeeGraphComponent,
]
})
export class TransactionModule { }