@@ -6,12 +6,11 @@ import { StateService } from '../..//services/state.service';
|
||||
interface EpochProgress {
|
||||
base: string;
|
||||
change: number;
|
||||
progress: string;
|
||||
progress: number;
|
||||
remainingBlocks: number;
|
||||
newDifficultyHeight: number;
|
||||
colorAdjustments: string;
|
||||
colorPreviousAdjustments: string;
|
||||
timeAvg: string;
|
||||
remainingTime: number;
|
||||
previousRetarget: number;
|
||||
blocksUntilHalving: number;
|
||||
@@ -38,85 +37,52 @@ export class DifficultyComponent implements OnInit {
|
||||
|
||||
ngOnInit(): void {
|
||||
this.isLoadingWebSocket$ = this.stateService.isLoadingWebSocket$;
|
||||
this.difficultyEpoch$ = timer(0, 1000)
|
||||
.pipe(
|
||||
switchMap(() => combineLatest([
|
||||
this.stateService.blocks$.pipe(map(([block]) => block)),
|
||||
this.stateService.lastDifficultyAdjustment$,
|
||||
this.stateService.previousRetarget$
|
||||
])),
|
||||
map(([block, DATime, previousRetarget]) => {
|
||||
const now = new Date().getTime() / 1000;
|
||||
const diff = now - DATime;
|
||||
const blocksInEpoch = block.height % 2016;
|
||||
const progress = (blocksInEpoch >= 0) ? (blocksInEpoch / 2016 * 100).toFixed(2) : `100`;
|
||||
const remainingBlocks = 2016 - blocksInEpoch;
|
||||
const newDifficultyHeight = block.height + remainingBlocks;
|
||||
this.difficultyEpoch$ = combineLatest([
|
||||
this.stateService.blocks$.pipe(map(([block]) => block)),
|
||||
this.stateService.difficultyAdjustment$,
|
||||
])
|
||||
.pipe(
|
||||
map(([block, da]) => {
|
||||
let colorAdjustments = '#ffffff66';
|
||||
if (da.difficultyChange > 0) {
|
||||
colorAdjustments = '#3bcc49';
|
||||
}
|
||||
if (da.difficultyChange < 0) {
|
||||
colorAdjustments = '#dc3545';
|
||||
}
|
||||
|
||||
let change = 0;
|
||||
if (remainingBlocks < 1870) {
|
||||
if (blocksInEpoch > 0) {
|
||||
change = (600 / (diff / blocksInEpoch ) - 1) * 100;
|
||||
}
|
||||
if (change > 300) {
|
||||
change = 300;
|
||||
}
|
||||
if (change < -75) {
|
||||
change = -75;
|
||||
}
|
||||
let colorPreviousAdjustments = '#dc3545';
|
||||
if (da.previousRetarget) {
|
||||
if (da.previousRetarget >= 0) {
|
||||
colorPreviousAdjustments = '#3bcc49';
|
||||
}
|
||||
|
||||
const timeAvgDiff = change * 0.1;
|
||||
|
||||
let timeAvgMins = 10;
|
||||
if (timeAvgDiff > 0) {
|
||||
timeAvgMins -= Math.abs(timeAvgDiff);
|
||||
} else {
|
||||
timeAvgMins += Math.abs(timeAvgDiff);
|
||||
}
|
||||
|
||||
const timeAvg = timeAvgMins.toFixed(0);
|
||||
const remainingTime = (remainingBlocks * timeAvgMins * 60 * 1000) + (now * 1000);
|
||||
|
||||
let colorAdjustments = '#ffffff66';
|
||||
if (change > 0) {
|
||||
colorAdjustments = '#3bcc49';
|
||||
}
|
||||
if (change < 0) {
|
||||
colorAdjustments = '#dc3545';
|
||||
}
|
||||
|
||||
let colorPreviousAdjustments = '#dc3545';
|
||||
if (previousRetarget) {
|
||||
if (previousRetarget >= 0) {
|
||||
colorPreviousAdjustments = '#3bcc49';
|
||||
}
|
||||
if (previousRetarget === 0) {
|
||||
colorPreviousAdjustments = '#ffffff66';
|
||||
}
|
||||
} else {
|
||||
if (da.previousRetarget === 0) {
|
||||
colorPreviousAdjustments = '#ffffff66';
|
||||
}
|
||||
} else {
|
||||
colorPreviousAdjustments = '#ffffff66';
|
||||
}
|
||||
|
||||
const blocksUntilHalving = 210000 - (block.height % 210000);
|
||||
const timeUntilHalving = (blocksUntilHalving * timeAvgMins * 60 * 1000) + (now * 1000);
|
||||
const timeAvgMins = da.timeAvg;
|
||||
const now = new Date().getTime() / 1000;
|
||||
const blocksUntilHalving = 210000 - (block.height % 210000);
|
||||
const timeUntilHalving = (blocksUntilHalving * timeAvgMins * 60 * 1000) + (now * 1000);
|
||||
|
||||
return {
|
||||
base: `${progress}%`,
|
||||
change,
|
||||
progress,
|
||||
remainingBlocks,
|
||||
timeAvg,
|
||||
colorAdjustments,
|
||||
colorPreviousAdjustments,
|
||||
blocksInEpoch,
|
||||
newDifficultyHeight,
|
||||
remainingTime,
|
||||
previousRetarget,
|
||||
blocksUntilHalving,
|
||||
timeUntilHalving,
|
||||
};
|
||||
})
|
||||
);
|
||||
const data = {
|
||||
base: `${da.progressPercent.toFixed(2)}%`,
|
||||
change: da.difficultyChange,
|
||||
progress: da.progressPercent,
|
||||
remainingBlocks: da.remainingBlocks,
|
||||
colorAdjustments,
|
||||
colorPreviousAdjustments,
|
||||
newDifficultyHeight: da.nextRetargetHeight,
|
||||
remainingTime: da.remainingTime,
|
||||
previousRetarget: da.previousRetarget,
|
||||
blocksUntilHalving,
|
||||
timeUntilHalving,
|
||||
};
|
||||
return data;
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<ng-container *ngIf="(loadingBlocks$ | async) === false; else loadingBlocks">
|
||||
<div class="mempool-blocks-container" *ngIf="(timeAvg$ | async) as timeAvg;">
|
||||
<div class="mempool-blocks-container" *ngIf="(difficultyAdjustments$ | async) as da;">
|
||||
<div class="flashing">
|
||||
<ng-template ngFor let-projectedBlock [ngForOf]="mempoolBlocks$ | async" let-i="index" [ngForTrackBy]="trackByFn">
|
||||
<div class="bitcoin-block text-center mempool-block" id="mempool-block-{{ i }}" [ngStyle]="mempoolBlockStyles[i]" [class.blink-bg]="projectedBlock.blink">
|
||||
@@ -26,7 +26,7 @@
|
||||
<app-time-until [time]="(1 * i) + now + 61000" [fastRender]="false" [fixedRender]="true"></app-time-until>
|
||||
</ng-template>
|
||||
<ng-template #timeDiffMainnet>
|
||||
<app-time-until [time]="(timeAvg * i) + now + timeAvg + timeOffset" [fastRender]="false" [fixedRender]="true" [forceFloorOnTimeIntervals]="['hour']"></app-time-until>
|
||||
<app-time-until [time]="da.timeAvg * (i + 1) + now + da.timeOffset" [fastRender]="false" [fixedRender]="true" [forceFloorOnTimeIntervals]="['hour']"></app-time-until>
|
||||
</ng-template>
|
||||
</div>
|
||||
<ng-template #mergedBlock>
|
||||
|
||||
@@ -8,6 +8,7 @@ import { feeLevels, mempoolFeeColors } from 'src/app/app.constants';
|
||||
import { specialBlocks } from 'src/app/app.constants';
|
||||
import { RelativeUrlPipe } from 'src/app/shared/pipes/relative-url/relative-url.pipe';
|
||||
import { Location } from '@angular/common';
|
||||
import { DifficultyAdjustment } from 'src/app/interfaces/node-api.interface';
|
||||
|
||||
@Component({
|
||||
selector: 'app-mempool-blocks',
|
||||
@@ -20,7 +21,7 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
|
||||
mempoolBlocks: MempoolBlock[] = [];
|
||||
mempoolEmptyBlocks: MempoolBlock[] = this.mountEmptyBlocks();
|
||||
mempoolBlocks$: Observable<MempoolBlock[]>;
|
||||
timeAvg$: Observable<number>;
|
||||
difficultyAdjustments$: Observable<DifficultyAdjustment>;
|
||||
loadingBlocks$: Observable<boolean>;
|
||||
blocksSubscription: Subscription;
|
||||
|
||||
@@ -123,40 +124,11 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
|
||||
})
|
||||
);
|
||||
|
||||
this.timeAvg$ = timer(0, 1000)
|
||||
this.difficultyAdjustments$ = this.stateService.difficultyAdjustment$
|
||||
.pipe(
|
||||
switchMap(() => combineLatest([
|
||||
this.stateService.blocks$.pipe(map(([block]) => block)),
|
||||
this.stateService.lastDifficultyAdjustment$
|
||||
])),
|
||||
map(([block, DATime]) => {
|
||||
map((da) => {
|
||||
this.now = new Date().getTime();
|
||||
const now = new Date().getTime() / 1000;
|
||||
const diff = now - DATime;
|
||||
const blocksInEpoch = block.height % 2016;
|
||||
let difficultyChange = 0;
|
||||
if (blocksInEpoch > 0) {
|
||||
difficultyChange = (600 / (diff / blocksInEpoch ) - 1) * 100;
|
||||
}
|
||||
const timeAvgDiff = difficultyChange * 0.1;
|
||||
|
||||
let timeAvgMins = 10;
|
||||
if (timeAvgDiff > 0 ){
|
||||
timeAvgMins -= Math.abs(timeAvgDiff);
|
||||
} else {
|
||||
timeAvgMins += Math.abs(timeAvgDiff);
|
||||
}
|
||||
|
||||
// testnet difficulty is set to 1 after 20 minutes of no blockSize
|
||||
// therefore the time between blocks will always be below 20 minutes (1200s)
|
||||
if (this.stateService.network === 'testnet' && now - block.timestamp + timeAvgMins * 60 > 1200) {
|
||||
this.timeOffset = -Math.min(now - block.timestamp, 1200) * 1000;
|
||||
timeAvgMins = 20;
|
||||
} else {
|
||||
this.timeOffset = 0;
|
||||
}
|
||||
|
||||
return timeAvgMins * 60 * 1000;
|
||||
return da;
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
@@ -66,29 +66,8 @@ export class TransactionComponent implements OnInit, OnDestroy {
|
||||
|
||||
this.timeAvg$ = timer(0, 1000)
|
||||
.pipe(
|
||||
switchMap(() => combineLatest([
|
||||
this.stateService.blocks$.pipe(map(([block]) => block)),
|
||||
this.stateService.lastDifficultyAdjustment$
|
||||
])),
|
||||
map(([block, DATime]) => {
|
||||
this.now = new Date().getTime();
|
||||
const now = new Date().getTime() / 1000;
|
||||
const diff = now - DATime;
|
||||
const blocksInEpoch = block.height % 2016;
|
||||
let difficultyChange = 0;
|
||||
if (blocksInEpoch > 0) {
|
||||
difficultyChange = (600 / (diff / blocksInEpoch ) - 1) * 100;
|
||||
}
|
||||
const timeAvgDiff = difficultyChange * 0.1;
|
||||
|
||||
let timeAvgMins = 10;
|
||||
if (timeAvgDiff > 0 ){
|
||||
timeAvgMins -= Math.abs(timeAvgDiff);
|
||||
} else {
|
||||
timeAvgMins += Math.abs(timeAvgDiff);
|
||||
}
|
||||
return timeAvgMins * 60 * 1000;
|
||||
})
|
||||
switchMap(() => this.stateService.difficultyAdjustment$),
|
||||
map((da) => da.timeAvg)
|
||||
);
|
||||
|
||||
this.fetchCpfpSubscription = this.fetchCpfp$
|
||||
|
||||
@@ -26,12 +26,15 @@ export interface CpfpInfo {
|
||||
}
|
||||
|
||||
export interface DifficultyAdjustment {
|
||||
progressPercent: number;
|
||||
difficultyChange: number;
|
||||
estimatedRetargetDate: number;
|
||||
previousRetarget: number;
|
||||
progressPercent: number;
|
||||
remainingBlocks: number;
|
||||
remainingTime: number;
|
||||
previousRetarget: number;
|
||||
nextRetargetHeight: number;
|
||||
timeAvg: number;
|
||||
timeOffset: number;
|
||||
}
|
||||
|
||||
export interface AddressInformation {
|
||||
@@ -111,4 +114,3 @@ export interface BlockExtension {
|
||||
export interface BlockExtended extends Block {
|
||||
extras?: BlockExtension;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ILoadingIndicators } from '../services/state.service';
|
||||
import { Transaction } from './electrs.interface';
|
||||
import { BlockExtended } from './node-api.interface';
|
||||
import { BlockExtended, DifficultyAdjustment } from './node-api.interface';
|
||||
|
||||
export interface WebsocketResponse {
|
||||
block?: BlockExtended;
|
||||
@@ -10,7 +10,6 @@ export interface WebsocketResponse {
|
||||
historicalDate?: string;
|
||||
mempoolInfo?: MempoolInfo;
|
||||
vBytesPerSecond?: number;
|
||||
lastDifficultyAdjustment?: number;
|
||||
previousRetarget?: number;
|
||||
action?: string;
|
||||
data?: string[];
|
||||
@@ -21,6 +20,7 @@ export interface WebsocketResponse {
|
||||
transactions?: TransactionStripped[];
|
||||
loadingIndicators?: ILoadingIndicators;
|
||||
backendInfo?: IBackendInfo;
|
||||
da?: DifficultyAdjustment;
|
||||
'track-tx'?: string;
|
||||
'track-address'?: string;
|
||||
'track-asset'?: string;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||
import { CpfpInfo, OptimizedMempoolStats, DifficultyAdjustment, AddressInformation, LiquidPegs, ITranslators, PoolsStats, PoolStat, BlockExtended, BlockExtension } from '../interfaces/node-api.interface';
|
||||
import { CpfpInfo, OptimizedMempoolStats, AddressInformation, LiquidPegs, ITranslators, PoolsStats, PoolStat, BlockExtended } from '../interfaces/node-api.interface';
|
||||
import { Observable } from 'rxjs';
|
||||
import { StateService } from './state.service';
|
||||
import { WebsocketResponse } from '../interfaces/websocket.interface';
|
||||
@@ -105,10 +105,6 @@ export class ApiService {
|
||||
return this.httpClient.get<CpfpInfo>(this.apiBaseUrl + this.apiBasePath + '/api/v1/cpfp/' + txid);
|
||||
}
|
||||
|
||||
getDifficultyAdjustment$(): Observable<DifficultyAdjustment> {
|
||||
return this.httpClient.get<DifficultyAdjustment>(this.apiBaseUrl + this.apiBasePath + '/api/v1/difficulty-adjustment');
|
||||
}
|
||||
|
||||
validateAddress$(address: string): Observable<AddressInformation> {
|
||||
return this.httpClient.get<AddressInformation>(this.apiBaseUrl + this.apiBasePath + '/api/v1/validate-address/' + address);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Inject, Injectable, PLATFORM_ID } from '@angular/core';
|
||||
import { ReplaySubject, BehaviorSubject, Subject, fromEvent, Observable } from 'rxjs';
|
||||
import { Transaction } from '../interfaces/electrs.interface';
|
||||
import { IBackendInfo, MempoolBlock, MempoolInfo, ReplacedTransaction, TransactionStripped } from '../interfaces/websocket.interface';
|
||||
import { BlockExtended, OptimizedMempoolStats } from '../interfaces/node-api.interface';
|
||||
import { BlockExtended, DifficultyAdjustment, OptimizedMempoolStats } from '../interfaces/node-api.interface';
|
||||
import { Router, NavigationStart } from '@angular/router';
|
||||
import { isPlatformBrowser } from '@angular/common';
|
||||
import { map, shareReplay } from 'rxjs/operators';
|
||||
@@ -82,11 +82,11 @@ export class StateService {
|
||||
mempoolBlocks$ = new ReplaySubject<MempoolBlock[]>(1);
|
||||
txReplaced$ = new Subject<ReplacedTransaction>();
|
||||
utxoSpent$ = new Subject<object>();
|
||||
difficultyAdjustment$ = new ReplaySubject<DifficultyAdjustment>(1);
|
||||
mempoolTransactions$ = new Subject<Transaction>();
|
||||
blockTransactions$ = new Subject<Transaction>();
|
||||
isLoadingWebSocket$ = new ReplaySubject<boolean>(1);
|
||||
vbytesPerSecond$ = new ReplaySubject<number>(1);
|
||||
lastDifficultyAdjustment$ = new ReplaySubject<number>(1);
|
||||
previousRetarget$ = new ReplaySubject<number>(1);
|
||||
backendInfo$ = new ReplaySubject<IBackendInfo>(1);
|
||||
loadingIndicators$ = new ReplaySubject<ILoadingIndicators>(1);
|
||||
|
||||
@@ -259,6 +259,10 @@ export class WebsocketService {
|
||||
this.stateService.utxoSpent$.next(response.utxoSpent);
|
||||
}
|
||||
|
||||
if (response.da) {
|
||||
this.stateService.difficultyAdjustment$.next(response.da);
|
||||
}
|
||||
|
||||
if (response.backendInfo) {
|
||||
this.stateService.backendInfo$.next(response.backendInfo);
|
||||
|
||||
@@ -301,10 +305,6 @@ export class WebsocketService {
|
||||
this.stateService.vbytesPerSecond$.next(response.vBytesPerSecond);
|
||||
}
|
||||
|
||||
if (response.lastDifficultyAdjustment !== undefined) {
|
||||
this.stateService.lastDifficultyAdjustment$.next(response.lastDifficultyAdjustment);
|
||||
}
|
||||
|
||||
if (response.previousRetarget !== undefined) {
|
||||
this.stateService.previousRetarget$.next(response.previousRetarget);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user