Merge branch 'master' into natsoni/more-fiat-currencies
This commit is contained in:
commit
23076172e4
12
backend/package-lock.json
generated
12
backend/package-lock.json
generated
@ -4027,9 +4027,9 @@
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"node_modules/ip": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz",
|
||||
"integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ=="
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ip/-/ip-2.0.1.tgz",
|
||||
"integrity": "sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ=="
|
||||
},
|
||||
"node_modules/ipaddr.js": {
|
||||
"version": "1.9.1",
|
||||
@ -10683,9 +10683,9 @@
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"ip": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz",
|
||||
"integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ=="
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ip/-/ip-2.0.1.tgz",
|
||||
"integrity": "sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ=="
|
||||
},
|
||||
"ipaddr.js": {
|
||||
"version": "1.9.1",
|
||||
|
@ -56,6 +56,9 @@ class AccelerationRepository {
|
||||
}
|
||||
|
||||
public async $getAccelerationInfo(poolSlug: string | null = null, height: number | null = null, interval: string | null = null): Promise<PublicAcceleration[]> {
|
||||
if (!interval || !['24h', '3d', '1w', '1m'].includes(interval)) {
|
||||
interval = '1m';
|
||||
}
|
||||
interval = Common.getSqlInterval(interval);
|
||||
|
||||
if (!config.MEMPOOL_SERVICES.ACCELERATIONS || (interval == null && poolSlug == null && height == null)) {
|
||||
|
91
frontend/package-lock.json
generated
91
frontend/package-lock.json
generated
@ -8070,13 +8070,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/es5-ext": {
|
||||
"version": "0.10.53",
|
||||
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz",
|
||||
"integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==",
|
||||
"version": "0.10.64",
|
||||
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz",
|
||||
"integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"es6-iterator": "~2.0.3",
|
||||
"es6-symbol": "~3.1.3",
|
||||
"next-tick": "~1.0.0"
|
||||
"es6-iterator": "^2.0.3",
|
||||
"es6-symbol": "^3.1.3",
|
||||
"esniff": "^2.0.1",
|
||||
"next-tick": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/es6-iterator": {
|
||||
@ -8624,6 +8629,25 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/esniff": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz",
|
||||
"integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==",
|
||||
"dependencies": {
|
||||
"d": "^1.0.1",
|
||||
"es5-ext": "^0.10.62",
|
||||
"event-emitter": "^0.3.5",
|
||||
"type": "^2.7.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/esniff/node_modules/type": {
|
||||
"version": "2.7.2",
|
||||
"resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz",
|
||||
"integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw=="
|
||||
},
|
||||
"node_modules/espree": {
|
||||
"version": "9.4.1",
|
||||
"resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz",
|
||||
@ -10397,9 +10421,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ip": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz",
|
||||
"integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ=="
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ip/-/ip-2.0.1.tgz",
|
||||
"integrity": "sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ=="
|
||||
},
|
||||
"node_modules/ipaddr.js": {
|
||||
"version": "2.1.0",
|
||||
@ -12508,9 +12532,9 @@
|
||||
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
|
||||
},
|
||||
"node_modules/next-tick": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
|
||||
"integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw="
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
|
||||
"integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ=="
|
||||
},
|
||||
"node_modules/ngx-echarts": {
|
||||
"version": "16.2.0",
|
||||
@ -23055,13 +23079,14 @@
|
||||
}
|
||||
},
|
||||
"es5-ext": {
|
||||
"version": "0.10.53",
|
||||
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz",
|
||||
"integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==",
|
||||
"version": "0.10.64",
|
||||
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz",
|
||||
"integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==",
|
||||
"requires": {
|
||||
"es6-iterator": "~2.0.3",
|
||||
"es6-symbol": "~3.1.3",
|
||||
"next-tick": "~1.0.0"
|
||||
"es6-iterator": "^2.0.3",
|
||||
"es6-symbol": "^3.1.3",
|
||||
"esniff": "^2.0.1",
|
||||
"next-tick": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"es6-iterator": {
|
||||
@ -23472,6 +23497,24 @@
|
||||
"integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==",
|
||||
"dev": true
|
||||
},
|
||||
"esniff": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz",
|
||||
"integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==",
|
||||
"requires": {
|
||||
"d": "^1.0.1",
|
||||
"es5-ext": "^0.10.62",
|
||||
"event-emitter": "^0.3.5",
|
||||
"type": "^2.7.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"type": {
|
||||
"version": "2.7.2",
|
||||
"resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz",
|
||||
"integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"espree": {
|
||||
"version": "9.4.1",
|
||||
"resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz",
|
||||
@ -24802,9 +24845,9 @@
|
||||
}
|
||||
},
|
||||
"ip": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz",
|
||||
"integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ=="
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ip/-/ip-2.0.1.tgz",
|
||||
"integrity": "sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ=="
|
||||
},
|
||||
"ipaddr.js": {
|
||||
"version": "2.1.0",
|
||||
@ -26374,9 +26417,9 @@
|
||||
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
|
||||
},
|
||||
"next-tick": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
|
||||
"integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw="
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
|
||||
"integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ=="
|
||||
},
|
||||
"ngx-echarts": {
|
||||
"version": "16.2.0",
|
||||
|
@ -9,23 +9,38 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<form [formGroup]="radioGroupForm" class="formRadioGroup" *ngIf="(statsObservable$ | async) as stats">
|
||||
<form [formGroup]="radioGroupForm" class="formRadioGroup" *ngIf="daysAvailable">
|
||||
<div class="btn-group btn-group-toggle" name="radioBasic" [class]="{'disabled': isLoading}">
|
||||
<label class="btn btn-primary btn-sm" [class.active]="radioGroupForm.get('dateSpan').value === '24h'">
|
||||
<input type="radio" [value]="'24h'" fragment="24h" [routerLink]="['/graphs/acceleration/fees' | relativeUrl]" formControlName="dateSpan"> 24H
|
||||
</label>
|
||||
<label class="btn btn-primary btn-sm" [class.active]="radioGroupForm.get('dateSpan').value === '3d'">
|
||||
<label class="btn btn-primary btn-sm" *ngIf="daysAvailable >= 1" [class.active]="radioGroupForm.get('dateSpan').value === '3d'">
|
||||
<input type="radio" [value]="'3d'" fragment="3d" [routerLink]="['/graphs/acceleration/fees' | relativeUrl]" formControlName="dateSpan"> 3D
|
||||
</label>
|
||||
<label class="btn btn-primary btn-sm" [class.active]="radioGroupForm.get('dateSpan').value === '1w'">
|
||||
<label class="btn btn-primary btn-sm" *ngIf="daysAvailable >= 3" [class.active]="radioGroupForm.get('dateSpan').value === '1w'">
|
||||
<input type="radio" [value]="'1w'" fragment="1w" [routerLink]="['/graphs/acceleration/fees' | relativeUrl]" formControlName="dateSpan"> 1W
|
||||
</label>
|
||||
<label class="btn btn-primary btn-sm" [class.active]="radioGroupForm.get('dateSpan').value === '1m'">
|
||||
<label class="btn btn-primary btn-sm" *ngIf="daysAvailable >= 7" [class.active]="radioGroupForm.get('dateSpan').value === '1m'">
|
||||
<input type="radio" [value]="'1m'" fragment="1m" [routerLink]="['/graphs/acceleration/fees' | relativeUrl]" formControlName="dateSpan"> 1M
|
||||
</label>
|
||||
<label class="btn btn-primary btn-sm" [class.active]="radioGroupForm.get('dateSpan').value === '3m'">
|
||||
<label class="btn btn-primary btn-sm" *ngIf="daysAvailable >= 30" [class.active]="radioGroupForm.get('dateSpan').value === '3m'">
|
||||
<input type="radio" [value]="'3m'" fragment="3m" [routerLink]="['/graphs/acceleration/fees' | relativeUrl]" formControlName="dateSpan"> 3M
|
||||
</label>
|
||||
<label class="btn btn-primary btn-sm" *ngIf="daysAvailable >= 90" [class.active]="radioGroupForm.get('dateSpan').value === '6m'">
|
||||
<input type="radio" [value]="'6m'" fragment="6m" [routerLink]="['/graphs/acceleration/fees' | relativeUrl]" formControlName="dateSpan"> 6M
|
||||
</label>
|
||||
<label class="btn btn-primary btn-sm" *ngIf="daysAvailable >= 180" [class.active]="radioGroupForm.get('dateSpan').value === '1y'">
|
||||
<input type="radio" [value]="'1y'" fragment="1y" [routerLink]="['/graphs/acceleration/fees' | relativeUrl]" formControlName="dateSpan"> 1Y
|
||||
</label>
|
||||
<label class="btn btn-primary btn-sm" *ngIf="daysAvailable >= 360" [class.active]="radioGroupForm.get('dateSpan').value === '2y'">
|
||||
<input type="radio" [value]="'2y'" fragment="2y" [routerLink]="['/graphs/acceleration/fees' | relativeUrl]" formControlName="dateSpan"> 2Y
|
||||
</label>
|
||||
<label class="btn btn-primary btn-sm" *ngIf="daysAvailable >= 720" [class.active]="radioGroupForm.get('dateSpan').value === '3y'">
|
||||
<input type="radio" [value]="'3y'" fragment="3y" [routerLink]="['/graphs/acceleration/fees' | relativeUrl]" formControlName="dateSpan"> 3Y
|
||||
</label>
|
||||
<label class="btn btn-primary btn-sm" [class.active]="radioGroupForm.get('dateSpan').value === 'all'">
|
||||
<input type="radio" [value]="'all'" fragment="all" [routerLink]="['/graphs/acceleration/fees' | relativeUrl]" formControlName="dateSpan"> ALL
|
||||
</label>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Input, LOCALE_ID, OnDestroy, OnInit } from '@angular/core';
|
||||
import { EChartsOption } from 'echarts';
|
||||
import { Observable, Subscription, combineLatest, fromEvent } from 'rxjs';
|
||||
import { Observable, Subscription, combineLatest, fromEvent, share } from 'rxjs';
|
||||
import { startWith, switchMap, tap } from 'rxjs/operators';
|
||||
import { SeoService } from '../../../services/seo.service';
|
||||
import { formatNumber } from '@angular/common';
|
||||
@ -41,8 +41,7 @@ export class AccelerationFeesGraphComponent implements OnInit, OnDestroy {
|
||||
renderer: 'svg',
|
||||
};
|
||||
|
||||
hrStatsObservable$: Observable<any>;
|
||||
statsObservable$: Observable<any>;
|
||||
aggregatedHistory$: Observable<any>;
|
||||
statsSubscription: Subscription;
|
||||
isLoading = true;
|
||||
formatNumber = formatNumber;
|
||||
@ -50,6 +49,7 @@ export class AccelerationFeesGraphComponent implements OnInit, OnDestroy {
|
||||
chartInstance: any = undefined;
|
||||
|
||||
currency: string;
|
||||
daysAvailable: number = 0;
|
||||
|
||||
constructor(
|
||||
@Inject(LOCALE_ID) public locale: string,
|
||||
@ -81,7 +81,7 @@ export class AccelerationFeesGraphComponent implements OnInit, OnDestroy {
|
||||
this.radioGroupForm.controls.dateSpan.setValue(fragment, { emitEvent: false });
|
||||
}
|
||||
});
|
||||
this.statsObservable$ = combineLatest([
|
||||
this.aggregatedHistory$ = combineLatest([
|
||||
this.radioGroupForm.get('dateSpan').valueChanges.pipe(
|
||||
startWith(this.radioGroupForm.controls.dateSpan.value),
|
||||
switchMap((timespan) => {
|
||||
@ -95,14 +95,17 @@ export class AccelerationFeesGraphComponent implements OnInit, OnDestroy {
|
||||
),
|
||||
fromEvent(window, 'resize').pipe(startWith(null)),
|
||||
]).pipe(
|
||||
tap(([history]) => {
|
||||
tap(([response]) => {
|
||||
const history: Acceleration[] = response.body;
|
||||
this.daysAvailable = (new Date().getTime() / 1000 - response.headers.get('x-oldest-accel')) / (24 * 3600);
|
||||
this.isLoading = false;
|
||||
this.prepareChartOptions(history);
|
||||
this.cd.markForCheck();
|
||||
})
|
||||
}),
|
||||
share(),
|
||||
);
|
||||
|
||||
this.statsObservable$.subscribe();
|
||||
this.aggregatedHistory$.subscribe();
|
||||
}
|
||||
|
||||
prepareChartOptions(data) {
|
||||
@ -247,6 +250,7 @@ export class AccelerationFeesGraphComponent implements OnInit, OnDestroy {
|
||||
type: 'bar',
|
||||
barWidth: '90%',
|
||||
large: true,
|
||||
barMinHeight: 1,
|
||||
},
|
||||
],
|
||||
dataZoom: (this.widget || data.length === 0 )? undefined : [{
|
||||
|
@ -44,7 +44,7 @@ export class AccelerationsListComponent implements OnInit {
|
||||
|
||||
this.accelerationList$ = this.pageSubject.pipe(
|
||||
switchMap((page) => {
|
||||
const accelerationObservable$ = this.accelerations$ || (this.pending ? this.servicesApiService.getAccelerations$() : this.servicesApiService.getAccelerationHistoryObserveResponse$({ timeframe: '1y', page: page }));
|
||||
const accelerationObservable$ = this.accelerations$ || (this.pending ? this.servicesApiService.getAccelerations$() : this.servicesApiService.getAccelerationHistoryObserveResponse$({ page: page }));
|
||||
return accelerationObservable$.pipe(
|
||||
switchMap(response => {
|
||||
let accelerations = response;
|
||||
|
@ -60,7 +60,7 @@ export class AcceleratorDashboardComponent implements OnInit {
|
||||
this.accelerations$ = this.stateService.chainTip$.pipe(
|
||||
distinctUntilChanged(),
|
||||
switchMap(() => {
|
||||
return this.serviceApiServices.getAccelerationHistory$({ timeframe: '3m', page: 1, pageLength: 100}).pipe(
|
||||
return this.serviceApiServices.getAccelerationHistory$({}).pipe(
|
||||
catchError(() => {
|
||||
return of([]);
|
||||
}),
|
||||
|
@ -3,7 +3,7 @@
|
||||
<div class="block-overview-graph">
|
||||
<canvas class="block-overview-canvas" [class.clickable]="!!hoverTx" #blockCanvas></canvas>
|
||||
<div class="loader-wrapper" [class.hidden]="(!isLoading || disableSpinner) && !unavailable">
|
||||
<div *ngIf="isLoading" class="spinner-border ml-3 loading" role="status"></div>
|
||||
<div *ngIf="!unavailable" class="spinner-border ml-3 loading" role="status"></div>
|
||||
<div *ngIf="!isLoading && unavailable" class="ml-3" i18n="block.not-available">not available</div>
|
||||
</div>
|
||||
<app-block-overview-tooltip
|
||||
|
@ -60,6 +60,5 @@
|
||||
|
||||
&.hidden {
|
||||
opacity: 0;
|
||||
transition: opacity 500ms;
|
||||
}
|
||||
}
|
||||
|
@ -433,6 +433,9 @@
|
||||
<td i18n="block.total-fees|Total fees in a block">Total fees</td>
|
||||
<td>
|
||||
<app-amount [satoshis]="block.extras.totalFees" digitsInfo="1.2-3" [noFiat]="true"></app-amount>
|
||||
<span *ngIf="oobFees" class="oobFees" i18n-ngbTooltip="Acceleration Fees" ngbTooltip="Acceleration fees paid out-of-band">
|
||||
+<app-amount [satoshis]="oobFees" digitsInfo="1.2-4" [noFiat]="true"></app-amount>
|
||||
</span>
|
||||
<span *ngIf="blockAudit.feeDelta" class="difference" [class.positive]="blockAudit.feeDelta <= 0" [class.negative]="blockAudit.feeDelta > 0">
|
||||
{{ blockAudit.feeDelta < 0 ? '+' : '' }}{{ (-blockAudit.feeDelta * 100) | amountShortener: 2 }}%
|
||||
</span>
|
||||
|
@ -288,6 +288,10 @@ h1 {
|
||||
@media (max-width: 767.98px) {
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
|
||||
.oobFees {
|
||||
color: #653b9c;
|
||||
}
|
||||
}
|
||||
|
||||
.graph-col {
|
||||
|
@ -9,7 +9,7 @@ import { StateService } from '../../services/state.service';
|
||||
import { SeoService } from '../../services/seo.service';
|
||||
import { WebsocketService } from '../../services/websocket.service';
|
||||
import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe';
|
||||
import { BlockAudit, BlockExtended, TransactionStripped } from '../../interfaces/node-api.interface';
|
||||
import { AccelerationInfo, BlockAudit, BlockExtended, TransactionStripped } from '../../interfaces/node-api.interface';
|
||||
import { ApiService } from '../../services/api.service';
|
||||
import { BlockOverviewGraphComponent } from '../../components/block-overview-graph/block-overview-graph.component';
|
||||
import { detectWebGL } from '../../shared/graphs.utils';
|
||||
@ -43,6 +43,7 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||
latestBlock: BlockExtended;
|
||||
latestBlocks: BlockExtended[] = [];
|
||||
transactions: Transaction[];
|
||||
oobFees: number = 0;
|
||||
isLoadingTransactions = true;
|
||||
strippedTransactions: TransactionStripped[];
|
||||
overviewTransitionDirection: string;
|
||||
@ -85,6 +86,7 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||
timeLtr: boolean;
|
||||
childChangeSubscription: Subscription;
|
||||
auditPrefSubscription: Subscription;
|
||||
oobSubscription: Subscription;
|
||||
|
||||
priceSubscription: Subscription;
|
||||
blockConversion: Price;
|
||||
@ -168,6 +170,7 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||
this.page = 1;
|
||||
this.error = undefined;
|
||||
this.fees = undefined;
|
||||
this.oobFees = 0;
|
||||
|
||||
if (history.state.data && history.state.data.blockHeight) {
|
||||
this.blockHeight = history.state.data.blockHeight;
|
||||
@ -446,7 +449,7 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||
inBlock[tx.txid] = true;
|
||||
}
|
||||
|
||||
blockAudit.feeDelta = blockAudit.expectedFees > 0 ? (blockAudit.expectedFees - this.block.extras.totalFees) / blockAudit.expectedFees : 0;
|
||||
blockAudit.feeDelta = blockAudit.expectedFees > 0 ? (blockAudit.expectedFees - (this.block.extras.totalFees + this.oobFees)) / blockAudit.expectedFees : 0;
|
||||
blockAudit.weightDelta = blockAudit.expectedWeight > 0 ? (blockAudit.expectedWeight - this.block.weight) / blockAudit.expectedWeight : 0;
|
||||
blockAudit.txDelta = blockAudit.template.length > 0 ? (blockAudit.template.length - this.block.tx_count) / blockAudit.template.length : 0;
|
||||
this.blockAudit = blockAudit;
|
||||
@ -462,6 +465,32 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||
this.setupBlockGraphs();
|
||||
});
|
||||
|
||||
this.oobSubscription = block$.pipe(
|
||||
switchMap((block) => this.apiService.getAccelerationsByHeight$(block.height)
|
||||
.pipe(
|
||||
map(accelerations => {
|
||||
return { block, accelerations };
|
||||
}),
|
||||
catchError((err) => {
|
||||
return of({ block, accelerations: [] });
|
||||
}))
|
||||
),
|
||||
).subscribe(({ block, accelerations}) => {
|
||||
let totalFees = 0;
|
||||
for (const acc of accelerations) {
|
||||
totalFees += acc.boost_cost;
|
||||
}
|
||||
this.oobFees = totalFees;
|
||||
if (block && this.block && this.blockAudit && block?.height === this.block?.height) {
|
||||
this.blockAudit.feeDelta = this.blockAudit.expectedFees > 0 ? (this.blockAudit.expectedFees - (this.block.extras.totalFees + this.oobFees)) / this.blockAudit.expectedFees : 0;
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
this.error = error;
|
||||
this.isLoadingBlock = false;
|
||||
this.isLoadingOverview = false;
|
||||
});
|
||||
|
||||
this.networkChangedSubscription = this.stateService.networkChanged$
|
||||
.subscribe((network) => this.network = network);
|
||||
|
||||
@ -529,6 +558,7 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||
this.unsubscribeNextBlockSubscriptions();
|
||||
this.childChangeSubscription?.unsubscribe();
|
||||
this.priceSubscription?.unsubscribe();
|
||||
this.oobSubscription?.unsubscribe();
|
||||
}
|
||||
|
||||
unsubscribeNextBlockSubscriptions() {
|
||||
|
@ -56,7 +56,7 @@
|
||||
<app-time kind="since" [time]="block.timestamp" [fastRender]="true" [precision]="1" minUnit="minute"></app-time></div>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="animated" [class]="markHeight === block.height ? 'hide' : 'show'" *ngIf="block.extras?.pool != undefined">
|
||||
<div class="animated" [class]="markHeight === block.height ? 'hide' : 'show'" *ngIf="block.extras?.pool != undefined && showPools">
|
||||
<a [attr.data-cy]="'bitcoin-block-' + offset + '-index-' + i + '-pool'" class="badge badge-primary"
|
||||
[routerLink]="[('/mining/pool/' + block.extras.pool.slug) | relativeUrl]">
|
||||
{{ block.extras.pool.name}}</a>
|
||||
|
@ -27,6 +27,7 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy {
|
||||
@Input() minimal: boolean = false;
|
||||
@Input() blockWidth: number = 125;
|
||||
@Input() spotlight: number = 0;
|
||||
@Input() showPools: boolean = true;
|
||||
@Input() getHref?: (index, block) => string = (index, block) => `/block/${block.id}`;
|
||||
|
||||
specialBlocks = specialBlocks;
|
||||
|
@ -16,6 +16,7 @@
|
||||
[minimal]="true"
|
||||
[count]="blockchainBlocks"
|
||||
[blockWidth]="blockWidth"
|
||||
[showPools]="false"
|
||||
[spotlight]="mode === 'mined' ? -index - 1 : 0"
|
||||
[getHref]="getMinedUrl"
|
||||
></app-blockchain-blocks>
|
||||
|
@ -16,9 +16,9 @@
|
||||
</ng-container>
|
||||
|
||||
<a class="navbar-brand" [ngClass]="{'dual-logos': subdomain}" [routerLink]="['/' | relativeUrl]" (click)="brandClick($event)">
|
||||
<ng-template [ngIf]="subdomain">
|
||||
<ng-template [ngIf]="subdomain && enterpriseInfo">
|
||||
<div class="subdomain_container">
|
||||
<img [src]="'/api/v1/services/enterprise/images/' + subdomain + '/logo'" class="subdomain_logo">
|
||||
<img [src]="'/api/v1/services/enterprise/images/' + subdomain + '/logo?imageMd5=' + enterpriseInfo.imageMd5" class="subdomain_logo">
|
||||
</div>
|
||||
</ng-template>
|
||||
<ng-container *ngIf="{ val: connectionState$ | async } as connectionState">
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Component, OnInit, Input, ViewChild } from '@angular/core';
|
||||
import { Component, OnInit, OnDestroy, Input, ViewChild } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { Env, StateService } from '../../services/state.service';
|
||||
import { Observable, merge, of } from 'rxjs';
|
||||
import { Observable, merge, of, Subscription } from 'rxjs';
|
||||
import { LanguageService } from '../../services/language.service';
|
||||
import { EnterpriseService } from '../../services/enterprise.service';
|
||||
import { NavigationService } from '../../services/navigation.service';
|
||||
@ -14,7 +14,7 @@ import { ApiService } from '../../services/api.service';
|
||||
templateUrl: './master-page.component.html',
|
||||
styleUrls: ['./master-page.component.scss'],
|
||||
})
|
||||
export class MasterPageComponent implements OnInit {
|
||||
export class MasterPageComponent implements OnInit, OnDestroy {
|
||||
@Input() headerVisible = true;
|
||||
@Input() footerVisibleOverride: boolean | null = null;
|
||||
|
||||
@ -32,6 +32,9 @@ export class MasterPageComponent implements OnInit {
|
||||
user: any = undefined;
|
||||
servicesEnabled = false;
|
||||
menuOpen = false;
|
||||
|
||||
enterpriseInfo: any;
|
||||
enterpriseInfo$: Subscription;
|
||||
|
||||
@ViewChild(MenuComponent)
|
||||
public menuComponent!: MenuComponent;
|
||||
@ -64,6 +67,9 @@ export class MasterPageComponent implements OnInit {
|
||||
this.footerVisible = this.footerVisibleOverride;
|
||||
}
|
||||
});
|
||||
this.enterpriseInfo$ = this.enterpriseService.info$.subscribe(info => {
|
||||
this.enterpriseInfo = info;
|
||||
});
|
||||
|
||||
this.servicesEnabled = this.officialMempoolSpace && this.stateService.env.ACCELERATOR === true && this.stateService.network === '';
|
||||
this.refreshAuth();
|
||||
@ -72,6 +78,12 @@ export class MasterPageComponent implements OnInit {
|
||||
this.menuOpen = isServicesPage && !this.isSmallScreen();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (this.enterpriseInfo$) {
|
||||
this.enterpriseInfo$.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
collapse(): void {
|
||||
this.navCollapsed = !this.navCollapsed;
|
||||
}
|
||||
|
@ -70,6 +70,26 @@
|
||||
<app-tx-features [tx]="tx"></app-tx-features>
|
||||
</td>
|
||||
</tr>
|
||||
<tr *ngIf="network === ''">
|
||||
<td class="td-width" i18n="transaction.mining">Mining</td>
|
||||
<td *ngIf="pool" class="wrap-cell">
|
||||
<a placement="bottom" [routerLink]="['/mining/pool' | relativeUrl, pool.slug]" class="badge mr-1"
|
||||
[class]="pool.name === 'Unknown' ? 'badge-secondary' : 'badge-primary'">
|
||||
{{ pool.name }}
|
||||
</a>
|
||||
<ng-container *ngIf="auditStatus">
|
||||
<span *ngIf="auditStatus.coinbase; else expected" class="badge badge-primary mr-1" i18n="tx-features.tag.coinbase|Coinbase">Coinbase</span>
|
||||
<ng-template #expected><span *ngIf="auditStatus.expected; else seen" class="badge badge-success mr-1" i18n-ngbTooltip="Expected in block tooltip" ngbTooltip="This transaction was projected to be included in the block" placement="bottom" i18n="tx-features.tag.expected|Expected in Block">Expected in Block</span></ng-template>
|
||||
<ng-template #seen><span *ngIf="auditStatus.seen; else notSeen" class="badge badge-success mr-1" i18n-ngbTooltip="Seen in mempool tooltip" ngbTooltip="This transaction was seen in the mempool prior to mining" placement="bottom" i18n="tx-features.tag.seen|Seen in Mempool">Seen in Mempool</span></ng-template>
|
||||
<ng-template #notSeen><span class="badge badge-warning mr-1" i18n-ngbTooltip="Not seen in mempool tooltip" ngbTooltip="This transaction was missing from our mempool prior to mining" placement="bottom" i18n="tx-features.tag.not-seen|Not seen in Mempool">Not seen in Mempool</span></ng-template>
|
||||
<span *ngIf="auditStatus.added" class="badge badge-primary mr-1" i18n-ngbTooltip="Added transaction tooltip" ngbTooltip="This transaction may have been added or prioritized out-of-band" placement="bottom" i18n="tx-features.tag.added|Added">Added</span>
|
||||
<span *ngIf="auditStatus.conflict" class="badge badge-warning mr-1" i18n-ngbTooltip="Conflict in mempool tooltip" ngbTooltip="This transaction conflicted with another version in our mempool" placement="bottom" i18n="tx-features.tag.conflict|Conflict">Conflict</span>
|
||||
</ng-container>
|
||||
</td>
|
||||
<td *ngIf="!pool">
|
||||
<span class="skeleton-loader"></span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@ -509,7 +529,7 @@
|
||||
<ng-template #feeTable>
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr *ngIf="isMobile && (network === 'liquid' || network === 'liquidtestnet' || !featuresEnabled)"></tr>
|
||||
<tr *ngIf="isMobile && (network === 'liquid' || network === 'liquidtestnet' || !featuresEnabled || network === '')"></tr>
|
||||
<tr>
|
||||
<td class="td-width" i18n="transaction.fee|Transaction fee">Fee</td>
|
||||
<td>{{ tx.fee | number }} <span class="symbol" i18n="shared.sat|sat">sat</span> <span class="fiat"><app-fiat [blockConversion]="blockConversion" [value]="tx.fee"></app-fiat></span></td>
|
||||
|
@ -149,6 +149,10 @@
|
||||
.btn {
|
||||
display: block;
|
||||
}
|
||||
|
||||
&.wrap-cell {
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,10 +8,11 @@ import {
|
||||
retryWhen,
|
||||
delay,
|
||||
mergeMap,
|
||||
tap
|
||||
tap,
|
||||
map
|
||||
} from 'rxjs/operators';
|
||||
import { Transaction } from '../../interfaces/electrs.interface';
|
||||
import { of, merge, Subscription, Observable, Subject, from, throwError } from 'rxjs';
|
||||
import { of, merge, Subscription, Observable, Subject, from, throwError, combineLatest } from 'rxjs';
|
||||
import { StateService } from '../../services/state.service';
|
||||
import { CacheService } from '../../services/cache.service';
|
||||
import { WebsocketService } from '../../services/websocket.service';
|
||||
@ -28,6 +29,22 @@ import { isFeatureActive } from '../../bitcoin.utils';
|
||||
import { ServicesApiServices } from '../../services/services-api.service';
|
||||
import { EnterpriseService } from '../../services/enterprise.service';
|
||||
|
||||
interface Pool {
|
||||
id: number;
|
||||
name: string;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
interface AuditStatus {
|
||||
seen?: boolean;
|
||||
expected?: boolean;
|
||||
added?: boolean;
|
||||
delayed?: number;
|
||||
accelerated?: boolean;
|
||||
conflict?: boolean;
|
||||
coinbase?: boolean;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-transaction',
|
||||
templateUrl: './transaction.component.html',
|
||||
@ -58,6 +75,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
urlFragmentSubscription: Subscription;
|
||||
mempoolBlocksSubscription: Subscription;
|
||||
blocksSubscription: Subscription;
|
||||
miningSubscription: Subscription;
|
||||
fragmentParams: URLSearchParams;
|
||||
rbfTransaction: undefined | Transaction;
|
||||
replaced: boolean = false;
|
||||
@ -67,11 +85,14 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
accelerationInfo: Acceleration | null = null;
|
||||
sigops: number | null;
|
||||
adjustedVsize: number | null;
|
||||
pool: Pool | null;
|
||||
auditStatus: AuditStatus | null;
|
||||
showCpfpDetails = false;
|
||||
fetchCpfp$ = new Subject<string>();
|
||||
fetchRbfHistory$ = new Subject<string>();
|
||||
fetchCachedTx$ = new Subject<string>();
|
||||
fetchAcceleration$ = new Subject<string>();
|
||||
fetchMiningInfo$ = new Subject<{ hash: string, height: number, txid: string }>();
|
||||
isCached: boolean = false;
|
||||
now = Date.now();
|
||||
da$: Observable<DifficultyAdjustment>;
|
||||
@ -100,6 +121,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
acceleratorAvailable: boolean = this.stateService.env.OFFICIAL_MEMPOOL_SPACE && this.stateService.env.ACCELERATOR && this.stateService.network === '';
|
||||
showAccelerationSummary = false;
|
||||
scrollIntoAccelPreview = false;
|
||||
auditEnabled: boolean = this.stateService.env.AUDIT && this.stateService.env.BASE_MODULE === 'mempool' && this.stateService.env.MINING_DASHBOARD === true;
|
||||
|
||||
@ViewChild('graphContainer')
|
||||
graphContainer: ElementRef;
|
||||
@ -266,6 +288,54 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
}
|
||||
});
|
||||
|
||||
this.miningSubscription = this.fetchMiningInfo$.pipe(
|
||||
filter((target) => target.txid === this.txId),
|
||||
tap(() => {
|
||||
this.pool = null;
|
||||
this.auditStatus = null;
|
||||
}),
|
||||
switchMap(({ hash, height, txid }) => {
|
||||
const foundBlock = this.cacheService.getCachedBlock(height) || null;
|
||||
const auditAvailable = this.isAuditAvailable(height);
|
||||
const isCoinbase = this.tx.vin.some(v => v.is_coinbase);
|
||||
const fetchAudit = auditAvailable && !isCoinbase;
|
||||
return combineLatest([
|
||||
foundBlock ? of(foundBlock.extras.pool) : this.apiService.getBlock$(hash).pipe(
|
||||
map(block => {
|
||||
return block.extras.pool;
|
||||
}),
|
||||
catchError(() => {
|
||||
return of(null);
|
||||
})
|
||||
),
|
||||
fetchAudit ? this.apiService.getBlockAudit$(hash).pipe(
|
||||
map(audit => {
|
||||
const isAdded = audit.addedTxs.includes(txid);
|
||||
const isAccelerated = audit.acceleratedTxs.includes(txid);
|
||||
const isConflict = audit.fullrbfTxs.includes(txid);
|
||||
const isExpected = audit.template.some(tx => tx.txid === txid);
|
||||
return {
|
||||
seen: isExpected || !(isAdded || isConflict),
|
||||
expected: isExpected,
|
||||
added: isAdded,
|
||||
conflict: isConflict,
|
||||
accelerated: isAccelerated,
|
||||
};
|
||||
}),
|
||||
catchError(() => {
|
||||
return of(null);
|
||||
})
|
||||
) : of(isCoinbase ? { coinbase: true } : null)
|
||||
]);
|
||||
}),
|
||||
catchError(() => {
|
||||
return of(null);
|
||||
})
|
||||
).subscribe(([pool, auditStatus]) => {
|
||||
this.pool = pool;
|
||||
this.auditStatus = auditStatus;
|
||||
});
|
||||
|
||||
this.mempoolPositionSubscription = this.stateService.mempoolTxPosition$.subscribe(txPosition => {
|
||||
this.now = Date.now();
|
||||
if (txPosition && txPosition.txid === this.txId && txPosition.position) {
|
||||
@ -396,6 +466,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
}
|
||||
} else {
|
||||
this.fetchAcceleration$.next(tx.status.block_hash);
|
||||
this.fetchMiningInfo$.next({ hash: tx.status.block_hash, height: tx.status.block_height, txid: tx.txid });
|
||||
this.transactionTime = 0;
|
||||
}
|
||||
|
||||
@ -453,6 +524,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
this.audioService.playSound('magic');
|
||||
}
|
||||
this.fetchAcceleration$.next(block.id);
|
||||
this.fetchMiningInfo$.next({ hash: block.id, height: block.height, txid: this.tx.txid });
|
||||
}
|
||||
});
|
||||
|
||||
@ -606,6 +678,29 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
this.featuresEnabled = this.segwitEnabled || this.taprootEnabled || this.rbfEnabled;
|
||||
}
|
||||
|
||||
isAuditAvailable(blockHeight: number): boolean {
|
||||
if (!this.auditEnabled) {
|
||||
return false;
|
||||
}
|
||||
switch (this.stateService.network) {
|
||||
case 'testnet':
|
||||
if (blockHeight < this.stateService.env.TESTNET_BLOCK_AUDIT_START_HEIGHT) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'signet':
|
||||
if (blockHeight < this.stateService.env.SIGNET_BLOCK_AUDIT_START_HEIGHT) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (blockHeight < this.stateService.env.MAINNET_BLOCK_AUDIT_START_HEIGHT) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
resetTransaction() {
|
||||
this.error = undefined;
|
||||
this.tx = null;
|
||||
@ -625,6 +720,8 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
this.accelerationInfo = null;
|
||||
this.txInBlockIndex = null;
|
||||
this.mempoolPosition = null;
|
||||
this.pool = null;
|
||||
this.auditStatus = null;
|
||||
document.body.scrollTo(0, 0);
|
||||
this.leaveTransaction();
|
||||
}
|
||||
@ -712,6 +809,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
this.mempoolPositionSubscription.unsubscribe();
|
||||
this.mempoolBlocksSubscription.unsubscribe();
|
||||
this.blocksSubscription.unsubscribe();
|
||||
this.miningSubscription?.unsubscribe();
|
||||
this.leaveTransaction();
|
||||
}
|
||||
}
|
||||
|
@ -236,7 +236,7 @@ export class DashboardComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
acc.unshift(stats);
|
||||
acc = acc.slice(0, 120);
|
||||
return acc;
|
||||
}, mempoolStats)
|
||||
}, (mempoolStats || []))
|
||||
),
|
||||
of(mempoolStats)
|
||||
);
|
||||
|
@ -9878,16 +9878,179 @@ export const restApiDocsData = [
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
category: "accelerator",
|
||||
fragment: "accelerator",
|
||||
title: "Accelerator",
|
||||
category: "accelerator-public",
|
||||
fragment: "accelerator-public",
|
||||
title: "Accelerator (Public)",
|
||||
showConditions: [""],
|
||||
options: { officialOnly: true },
|
||||
},
|
||||
{
|
||||
options: { officialOnly: true },
|
||||
type: "endpoint",
|
||||
category: "accelerator",
|
||||
category: "accelerator-public",
|
||||
httpRequestMethod: "POST",
|
||||
fragment: "accelerator-estimate",
|
||||
title: "POST Calculate Estimated Costs",
|
||||
description: {
|
||||
default: "<p>Returns estimated costs to accelerate a transaction. Optionally set the <code>api_key</code> header to get customized estimation.</p>"
|
||||
},
|
||||
urlString: "/v1/services/accelerator/estimate",
|
||||
showConditions: [""],
|
||||
showJsExamples: showJsExamplesDefaultFalse,
|
||||
codeExample: {
|
||||
default: {
|
||||
codeTemplate: {
|
||||
curl: `%{1}" "[[hostname]][[baseNetworkUrl]]/api/v1/services/accelerator/estimate`, //custom interpolation technique handled in replaceCurlPlaceholder()
|
||||
commonJS: ``,
|
||||
esModule: ``
|
||||
},
|
||||
codeSampleMainnet: {
|
||||
esModule: [],
|
||||
commonJS: [],
|
||||
curl: ["txInput=ee13ebb99632377c15c94980357f674d285ac413452050031ea6dcd3e9b2dc29"],
|
||||
headers: "api_key: stacksats",
|
||||
response: `{
|
||||
"txSummary": {
|
||||
"txid": "ee13ebb99632377c15c94980357f674d285ac413452050031ea6dcd3e9b2dc29",
|
||||
"effectiveVsize": 154,
|
||||
"effectiveFee": 154,
|
||||
"ancestorCount": 1
|
||||
},
|
||||
"cost": 3850,
|
||||
"targetFeeRate": 26,
|
||||
"nextBlockFee": 4004,
|
||||
"userBalance": 99900000,
|
||||
"mempoolBaseFee": 40000,
|
||||
"vsizeFee": 50000,
|
||||
"hasAccess": true
|
||||
}`,
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
options: { officialOnly: true },
|
||||
type: "endpoint",
|
||||
category: "accelerator-public",
|
||||
httpRequestMethod: "GET",
|
||||
fragment: "accelerator-pending",
|
||||
title: "GET Pending Accelerations",
|
||||
description: {
|
||||
default: "<p>Returns all transactions currently being accelerated.</p>"
|
||||
},
|
||||
urlString: "/v1/services/accelerator/accelerations",
|
||||
showConditions: [""],
|
||||
showJsExamples: showJsExamplesDefaultFalse,
|
||||
codeExample: {
|
||||
default: {
|
||||
codeTemplate: {
|
||||
curl: `/api/v1/services/accelerator/accelerations`,
|
||||
commonJS: ``,
|
||||
esModule: ``
|
||||
},
|
||||
codeSampleMainnet: {
|
||||
esModule: [],
|
||||
commonJS: [],
|
||||
curl: [],
|
||||
headers: '',
|
||||
response: `[
|
||||
{
|
||||
"txid": "8a183c8ae929a2afb857e7f2acd440aaefdf2797f8f7eab1c5f95ff8602abc81",
|
||||
"added": 1707558316,
|
||||
"feeDelta": 3500,
|
||||
"effectiveVsize": 111,
|
||||
"effectiveFee": 1671,
|
||||
"pools": [
|
||||
111
|
||||
]
|
||||
},
|
||||
{
|
||||
"txid": "6097f295e21bdd8d725bd8d9ad4dd72b05bd795dc648bfef52150a9b2b7f7a45",
|
||||
"added": 1707560464,
|
||||
"feeDelta": 60000,
|
||||
"effectiveVsize": 812,
|
||||
"effectiveFee": 7790,
|
||||
"pools": [
|
||||
111
|
||||
]
|
||||
}
|
||||
]`,
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
options: { officialOnly: true },
|
||||
type: "endpoint",
|
||||
category: "accelerator-public",
|
||||
httpRequestMethod: "GET",
|
||||
fragment: "accelerator-public-history",
|
||||
title: "GET Acceleration History",
|
||||
description: {
|
||||
default: `<p>Returns all past accelerated transactions.
|
||||
Filters can be applied:<ul>
|
||||
<li><code>status</code>: <code>all</code>, <code>requested</code>, <code>accelerating</code>, <code>mined</code>, <code>completed</code>, <code>failed</code></li>
|
||||
<li><code>timeframe</code>: <code>24h</code>, <code>3d</code>, <code>1w</code>, <code>1m</code>, <code>3m</code>, <code>6m</code>, <code>1y</code>, <code>2y</code>, <code>3y</code>, <code>4y</code>, <code>all</code></li>
|
||||
<li><code>poolUniqueId</code>: any id from <a target="_blank" href="https://github.com/mempool/mining-pools/blob/master/pools-v2.json">https://github.com/mempool/mining-pools/blob/master/pools-v2.json</a>. <i>Note: This will return all acceleration requests accepted by the pool but the the listed transactions may have been mined by another pool.</i>
|
||||
<li><code>blockHash</code>: a block hash</a>
|
||||
<li><code>blockHeight</code>: a block height</a>
|
||||
<li><code>page</code>: the requested page number if using pagination <i>(min: 1)</i></a>
|
||||
<li><code>pageLength</code>: the page lenght if using pagination <i>(min: 1, max: 50)</i></a>
|
||||
</ul></p>`
|
||||
},
|
||||
urlString: "/v1/services/accelerator/accelerations/history",
|
||||
showConditions: [""],
|
||||
showJsExamples: showJsExamplesDefaultFalse,
|
||||
codeExample: {
|
||||
default: {
|
||||
codeTemplate: {
|
||||
curl: `/api/v1/services/accelerator/accelerations/history?blockHash=00000000000000000000482f0746d62141694b9210a813b97eb8445780a32003`,
|
||||
commonJS: ``,
|
||||
esModule: ``
|
||||
},
|
||||
codeSampleMainnet: {
|
||||
esModule: [],
|
||||
commonJS: [],
|
||||
curl: [],
|
||||
headers: '',
|
||||
response: `[
|
||||
{
|
||||
"txid": "d7e1796d8eb4a09d4e6c174e36cfd852f1e6e6c9f7df4496339933cd32cbdd1d",
|
||||
"status": "completed",
|
||||
"feePaid": 53239,
|
||||
"added": 1707421053,
|
||||
"lastUpdated": 1707422952,
|
||||
"baseFee": 50000,
|
||||
"vsizeFee": 0,
|
||||
"effectiveFee": 146,
|
||||
"effectiveVsize": 141,
|
||||
"feeDelta": 14000,
|
||||
"blockHash": "00000000000000000000482f0746d62141694b9210a813b97eb8445780a32003",
|
||||
"blockHeight": 829559,
|
||||
"pools": [
|
||||
{
|
||||
"pool_unique_id": 111,
|
||||
"username": "foundryusa"
|
||||
}
|
||||
]
|
||||
}
|
||||
]`,
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
category: "accelerator-private",
|
||||
fragment: "accelerator-private",
|
||||
title: "Accelerator (Authenticated)",
|
||||
showConditions: [""],
|
||||
options: { officialOnly: true },
|
||||
},
|
||||
{
|
||||
options: { officialOnly: true },
|
||||
type: "endpoint",
|
||||
category: "accelerator-private",
|
||||
httpRequestMethod: "GET",
|
||||
fragment: "accelerator-deposit-history",
|
||||
title: "GET Deposit History",
|
||||
@ -9935,7 +10098,7 @@ export const restApiDocsData = [
|
||||
{
|
||||
options: { officialOnly: true },
|
||||
type: "endpoint",
|
||||
category: "accelerator",
|
||||
category: "accelerator-private",
|
||||
httpRequestMethod: "GET",
|
||||
fragment: "accelerator-balance",
|
||||
title: "GET Available Balance",
|
||||
@ -9969,51 +10132,7 @@ export const restApiDocsData = [
|
||||
{
|
||||
options: { officialOnly: true },
|
||||
type: "endpoint",
|
||||
category: "accelerator",
|
||||
httpRequestMethod: "POST",
|
||||
fragment: "accelerator-estimate",
|
||||
title: "POST Calculate Estimated Costs",
|
||||
description: {
|
||||
default: "<p>Returns estimated costs to accelerate a transaction.</p>"
|
||||
},
|
||||
urlString: "/v1/services/accelerator/estimate",
|
||||
showConditions: [""],
|
||||
showJsExamples: showJsExamplesDefaultFalse,
|
||||
codeExample: {
|
||||
default: {
|
||||
codeTemplate: {
|
||||
curl: `%{1}" "[[hostname]][[baseNetworkUrl]]/api/v1/services/accelerator/estimate`, //custom interpolation technique handled in replaceCurlPlaceholder()
|
||||
commonJS: ``,
|
||||
esModule: ``
|
||||
},
|
||||
codeSampleMainnet: {
|
||||
esModule: [],
|
||||
commonJS: [],
|
||||
curl: ["txInput=ee13ebb99632377c15c94980357f674d285ac413452050031ea6dcd3e9b2dc29"],
|
||||
headers: "api_key: stacksats",
|
||||
response: `{
|
||||
"txSummary": {
|
||||
"txid": "ee13ebb99632377c15c94980357f674d285ac413452050031ea6dcd3e9b2dc29",
|
||||
"effectiveVsize": 154,
|
||||
"effectiveFee": 154,
|
||||
"ancestorCount": 1
|
||||
},
|
||||
"cost": 3850,
|
||||
"targetFeeRate": 26,
|
||||
"nextBlockFee": 4004,
|
||||
"userBalance": 99900000,
|
||||
"mempoolBaseFee": 40000,
|
||||
"vsizeFee": 50000,
|
||||
"hasAccess": true
|
||||
}`,
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
options: { officialOnly: true },
|
||||
type: "endpoint",
|
||||
category: "accelerator",
|
||||
category: "accelerator-private",
|
||||
httpRequestMethod: "POST",
|
||||
fragment: "accelerator-accelerate",
|
||||
title: "POST Accelerate A Transaction",
|
||||
@ -10043,10 +10162,10 @@ export const restApiDocsData = [
|
||||
{
|
||||
options: { officialOnly: true },
|
||||
type: "endpoint",
|
||||
category: "accelerator",
|
||||
category: "accelerator-private",
|
||||
httpRequestMethod: "GET",
|
||||
fragment: "accelerator-history",
|
||||
title: "GET Private Acceleration History",
|
||||
title: "GET Acceleration History",
|
||||
description: {
|
||||
default: "<p>Returns the user's past acceleration requests.</p><p>Pass one of the following for <code>:status</code>: <code>all</code>, <code>requested</code>, <code>accelerating</code>, <code>mined</code>, <code>completed</code>, <code>failed</code>. Pass <code>true</code> in <code>:details</code> to get a detailed <code>history</code> of the acceleration request.</p>"
|
||||
},
|
||||
@ -10153,117 +10272,6 @@ export const restApiDocsData = [
|
||||
}
|
||||
]
|
||||
}
|
||||
]`,
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
options: { officialOnly: true },
|
||||
type: "endpoint",
|
||||
category: "accelerator",
|
||||
httpRequestMethod: "GET",
|
||||
fragment: "accelerator-pending",
|
||||
title: "GET Pending Accelerations",
|
||||
description: {
|
||||
default: "<p>Returns all transactions currently being accelerated.</p>"
|
||||
},
|
||||
urlString: "/v1/services/accelerator/accelerations",
|
||||
showConditions: [""],
|
||||
showJsExamples: showJsExamplesDefaultFalse,
|
||||
codeExample: {
|
||||
default: {
|
||||
codeTemplate: {
|
||||
curl: `/api/v1/services/accelerator/accelerations`,
|
||||
commonJS: ``,
|
||||
esModule: ``
|
||||
},
|
||||
codeSampleMainnet: {
|
||||
esModule: [],
|
||||
commonJS: [],
|
||||
curl: [],
|
||||
headers: '',
|
||||
response: `[
|
||||
{
|
||||
"txid": "8a183c8ae929a2afb857e7f2acd440aaefdf2797f8f7eab1c5f95ff8602abc81",
|
||||
"added": 1707558316,
|
||||
"feeDelta": 3500,
|
||||
"effectiveVsize": 111,
|
||||
"effectiveFee": 1671,
|
||||
"pools": [
|
||||
111
|
||||
]
|
||||
},
|
||||
{
|
||||
"txid": "6097f295e21bdd8d725bd8d9ad4dd72b05bd795dc648bfef52150a9b2b7f7a45",
|
||||
"added": 1707560464,
|
||||
"feeDelta": 60000,
|
||||
"effectiveVsize": 812,
|
||||
"effectiveFee": 7790,
|
||||
"pools": [
|
||||
111
|
||||
]
|
||||
}
|
||||
]`,
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
options: { officialOnly: true },
|
||||
type: "endpoint",
|
||||
category: "accelerator",
|
||||
httpRequestMethod: "GET",
|
||||
fragment: "accelerator-public-history",
|
||||
title: "GET Public Acceleration History",
|
||||
description: {
|
||||
default: `<p>Returns all past accelerated transactions.
|
||||
Filters can be applied:<ul>
|
||||
<li><code>status</code>: <code>all</code>, <code>requested</code>, <code>accelerating</code>, <code>mined</code>, <code>completed</code>, <code>failed</code></li>
|
||||
<li><code>timeframe</code>: <code>24h</code>, <code>3d</code>, <code>1w</code>, <code>1m</code>, <code>3m</code>, <code>6m</code>, <code>1y</code>, <code>2y</code>, <code>3y</code>, <code>all</code></li>
|
||||
<li><code>poolUniqueId</code>: any id from <a target="_blank" href="https://github.com/mempool/mining-pools/blob/master/pools-v2.json">https://github.com/mempool/mining-pools/blob/master/pools-v2.json</a>
|
||||
<li><code>blockHash</code>: a block hash</a>
|
||||
<li><code>blockHeight</code>: a block height</a>
|
||||
<li><code>page</code>: the requested page number if using pagination</a>
|
||||
<li><code>pageLength</code>: the page lenght if using pagination</a>
|
||||
</ul></p>`
|
||||
},
|
||||
urlString: "/v1/services/accelerator/accelerations/history",
|
||||
showConditions: [""],
|
||||
showJsExamples: showJsExamplesDefaultFalse,
|
||||
codeExample: {
|
||||
default: {
|
||||
codeTemplate: {
|
||||
curl: `/api/v1/services/accelerator/accelerations/history?blockHash=00000000000000000000482f0746d62141694b9210a813b97eb8445780a32003`,
|
||||
commonJS: ``,
|
||||
esModule: ``
|
||||
},
|
||||
codeSampleMainnet: {
|
||||
esModule: [],
|
||||
commonJS: [],
|
||||
curl: [],
|
||||
headers: '',
|
||||
response: `[
|
||||
{
|
||||
"txid": "d7e1796d8eb4a09d4e6c174e36cfd852f1e6e6c9f7df4496339933cd32cbdd1d",
|
||||
"status": "completed",
|
||||
"feePaid": 53239,
|
||||
"added": 1707421053,
|
||||
"lastUpdated": 1707422952,
|
||||
"baseFee": 50000,
|
||||
"vsizeFee": 0,
|
||||
"effectiveFee": 146,
|
||||
"effectiveVsize": 141,
|
||||
"feeDelta": 14000,
|
||||
"blockHash": "00000000000000000000482f0746d62141694b9210a813b97eb8445780a32003",
|
||||
"blockHeight": 829559,
|
||||
"pools": [
|
||||
{
|
||||
"pool_unique_id": 111,
|
||||
"username": "foundryusa"
|
||||
}
|
||||
]
|
||||
}
|
||||
]`,
|
||||
},
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
<div id="enterprise-cta-desktop">
|
||||
<div id="enterprise-cta-desktop" *ngIf="officialMempoolInstance">
|
||||
<p>Get higher API limits with Mempool Enterprise®</p>
|
||||
<a class="btn btn-small btn-purple" href="/enterprise">More Info <fa-icon [icon]="['fas', 'angle-right']" [styles]="{'font-size': '12px'}"></fa-icon></a>
|
||||
</div>
|
||||
|
@ -39,7 +39,7 @@
|
||||
|
||||
<div class="doc-content">
|
||||
|
||||
<div id="enterprise-cta-mobile" *ngIf="showMobileEnterpriseUpsell">
|
||||
<div id="enterprise-cta-mobile" *ngIf="officialMempoolInstance && showMobileEnterpriseUpsell">
|
||||
<p>Get higher API limits with <span class="no-line-break">Mempool Enterprise®</span></p>
|
||||
<div class="button-group">
|
||||
<a class="btn btn-small btn-secondary" (click)="showMobileEnterpriseUpsell = false">No Thanks</a>
|
||||
|
@ -403,4 +403,18 @@ export interface AccelerationHistoryParams {
|
||||
blockHeight?: number;
|
||||
page?: number;
|
||||
pageLength?: number;
|
||||
}
|
||||
|
||||
export interface AccelerationInfo {
|
||||
txid: string,
|
||||
height: number,
|
||||
pool: {
|
||||
id: number,
|
||||
slug: string,
|
||||
name: string,
|
||||
},
|
||||
effective_vsize: number,
|
||||
effective_fee: number,
|
||||
boost_rate: number,
|
||||
boost_cost: number,
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient, HttpParams, HttpResponse } from '@angular/common/http';
|
||||
import { CpfpInfo, OptimizedMempoolStats, AddressInformation, LiquidPegs, ITranslators,
|
||||
PoolStat, BlockExtended, TransactionStripped, RewardStats, AuditScore, BlockSizesAndWeights, RbfTree, BlockAudit, Acceleration, AccelerationHistoryParams, CurrentPegs, AuditStatus, FederationAddress, FederationUtxo, RecentPeg, PegsVolume } from '../interfaces/node-api.interface';
|
||||
PoolStat, BlockExtended, TransactionStripped, RewardStats, AuditScore, BlockSizesAndWeights, RbfTree, BlockAudit, Acceleration, AccelerationHistoryParams, CurrentPegs, AuditStatus, FederationAddress, FederationUtxo, RecentPeg, PegsVolume, AccelerationInfo } from '../interfaces/node-api.interface';
|
||||
import { BehaviorSubject, Observable, catchError, filter, of, shareReplay, take, tap } from 'rxjs';
|
||||
import { StateService } from './state.service';
|
||||
import { Transaction } from '../interfaces/electrs.interface';
|
||||
@ -459,4 +459,22 @@ export class ApiService {
|
||||
(queryParams.length > 0 ? `?${queryParams.join('&')}` : '')
|
||||
);
|
||||
}
|
||||
|
||||
getAccelerationsByPool$(slug: string): Observable<AccelerationInfo[]> {
|
||||
return this.httpClient.get<AccelerationInfo[]>(
|
||||
this.apiBaseUrl + this.apiBasePath + `/api/v1/accelerations/pool/${slug}`
|
||||
);
|
||||
}
|
||||
|
||||
getAccelerationsByHeight$(height: number): Observable<AccelerationInfo[]> {
|
||||
return this.httpClient.get<AccelerationInfo[]>(
|
||||
this.apiBaseUrl + this.apiBasePath + `/api/v1/accelerations/block/${height}`
|
||||
);
|
||||
}
|
||||
|
||||
getRecentAccelerations$(interval: string | undefined): Observable<AccelerationInfo[]> {
|
||||
return this.httpClient.get<AccelerationInfo[]>(
|
||||
this.apiBaseUrl + this.apiBasePath + '/api/v1/accelerations/interval' + (interval !== undefined ? `/${interval}` : '')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import { ApiService } from './api.service';
|
||||
import { SeoService } from './seo.service';
|
||||
import { StateService } from './state.service';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
@ -11,9 +12,9 @@ import { ActivatedRoute } from '@angular/router';
|
||||
export class EnterpriseService {
|
||||
exclusiveHostName = '.mempool.space';
|
||||
subdomain: string | null = null;
|
||||
info: object = {};
|
||||
statsUrl: string;
|
||||
siteId: number;
|
||||
info$: BehaviorSubject<object> = new BehaviorSubject(null);
|
||||
|
||||
constructor(
|
||||
@Inject(DOCUMENT) private document: Document,
|
||||
@ -47,9 +48,9 @@ export class EnterpriseService {
|
||||
|
||||
fetchSubdomainInfo(): void {
|
||||
this.apiService.getEnterpriseInfo$(this.subdomain).subscribe((info) => {
|
||||
this.info = info;
|
||||
this.insertMatomo(info.site_id);
|
||||
this.seoService.setEnterpriseTitle(info.title);
|
||||
this.info$.next(info);
|
||||
},
|
||||
(error) => {
|
||||
if (error.status === 404) {
|
||||
|
@ -145,8 +145,8 @@ export class ServicesApiServices {
|
||||
return this.httpClient.get<Acceleration[]>(`${SERVICES_API_PREFIX}/accelerator/accelerations`);
|
||||
}
|
||||
|
||||
getAggregatedAccelerationHistory$(params: AccelerationHistoryParams): Observable<Acceleration[]> {
|
||||
return this.httpClient.get<Acceleration[]>(`${SERVICES_API_PREFIX}/accelerator/accelerations/history/aggregated`, { params: { ...params } });
|
||||
getAggregatedAccelerationHistory$(params: AccelerationHistoryParams): Observable<any> {
|
||||
return this.httpClient.get<any>(`${SERVICES_API_PREFIX}/accelerator/accelerations/history/aggregated`, { params: { ...params }, observe: 'response' });
|
||||
}
|
||||
|
||||
getAccelerationHistory$(params: AccelerationHistoryParams): Observable<Acceleration[]> {
|
||||
|
@ -116,7 +116,7 @@ export class WebsocketService {
|
||||
this.startMultiTrackTransaction(this.trackingTxId);
|
||||
}
|
||||
if (this.isTrackingMempoolBlock) {
|
||||
this.startTrackMempoolBlock(this.trackingMempoolBlock);
|
||||
this.startTrackMempoolBlock(this.trackingMempoolBlock, true);
|
||||
}
|
||||
if (this.isTrackingRbf) {
|
||||
this.startTrackRbf(this.isTrackingRbf);
|
||||
@ -197,9 +197,9 @@ export class WebsocketService {
|
||||
this.websocketSubject.next({ 'track-asset': 'stop' });
|
||||
}
|
||||
|
||||
startTrackMempoolBlock(block: number) {
|
||||
startTrackMempoolBlock(block: number, force: boolean = false) {
|
||||
// skip duplicate tracking requests
|
||||
if (this.trackingMempoolBlock !== block) {
|
||||
if (force || this.trackingMempoolBlock !== block) {
|
||||
this.websocketSubject.next({ 'track-mempool-block': block });
|
||||
this.isTrackingMempoolBlock = true;
|
||||
this.trackingMempoolBlock = block;
|
||||
|
Loading…
x
Reference in New Issue
Block a user