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=="
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||||
},
|
},
|
||||||
"node_modules/ip": {
|
"node_modules/ip": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ip/-/ip-2.0.1.tgz",
|
||||||
"integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ=="
|
"integrity": "sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ=="
|
||||||
},
|
},
|
||||||
"node_modules/ipaddr.js": {
|
"node_modules/ipaddr.js": {
|
||||||
"version": "1.9.1",
|
"version": "1.9.1",
|
||||||
@ -10683,9 +10683,9 @@
|
|||||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||||
},
|
},
|
||||||
"ip": {
|
"ip": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ip/-/ip-2.0.1.tgz",
|
||||||
"integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ=="
|
"integrity": "sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ=="
|
||||||
},
|
},
|
||||||
"ipaddr.js": {
|
"ipaddr.js": {
|
||||||
"version": "1.9.1",
|
"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[]> {
|
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);
|
interval = Common.getSqlInterval(interval);
|
||||||
|
|
||||||
if (!config.MEMPOOL_SERVICES.ACCELERATIONS || (interval == null && poolSlug == null && height == null)) {
|
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": {
|
"node_modules/es5-ext": {
|
||||||
"version": "0.10.53",
|
"version": "0.10.64",
|
||||||
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz",
|
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz",
|
||||||
"integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==",
|
"integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==",
|
||||||
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"es6-iterator": "~2.0.3",
|
"es6-iterator": "^2.0.3",
|
||||||
"es6-symbol": "~3.1.3",
|
"es6-symbol": "^3.1.3",
|
||||||
"next-tick": "~1.0.0"
|
"esniff": "^2.0.1",
|
||||||
|
"next-tick": "^1.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/es6-iterator": {
|
"node_modules/es6-iterator": {
|
||||||
@ -8624,6 +8629,25 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"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": {
|
"node_modules/espree": {
|
||||||
"version": "9.4.1",
|
"version": "9.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz",
|
||||||
@ -10397,9 +10421,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ip": {
|
"node_modules/ip": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ip/-/ip-2.0.1.tgz",
|
||||||
"integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ=="
|
"integrity": "sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ=="
|
||||||
},
|
},
|
||||||
"node_modules/ipaddr.js": {
|
"node_modules/ipaddr.js": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
@ -12508,9 +12532,9 @@
|
|||||||
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
|
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
|
||||||
},
|
},
|
||||||
"node_modules/next-tick": {
|
"node_modules/next-tick": {
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
|
||||||
"integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw="
|
"integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ=="
|
||||||
},
|
},
|
||||||
"node_modules/ngx-echarts": {
|
"node_modules/ngx-echarts": {
|
||||||
"version": "16.2.0",
|
"version": "16.2.0",
|
||||||
@ -23055,13 +23079,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"es5-ext": {
|
"es5-ext": {
|
||||||
"version": "0.10.53",
|
"version": "0.10.64",
|
||||||
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz",
|
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz",
|
||||||
"integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==",
|
"integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"es6-iterator": "~2.0.3",
|
"es6-iterator": "^2.0.3",
|
||||||
"es6-symbol": "~3.1.3",
|
"es6-symbol": "^3.1.3",
|
||||||
"next-tick": "~1.0.0"
|
"esniff": "^2.0.1",
|
||||||
|
"next-tick": "^1.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"es6-iterator": {
|
"es6-iterator": {
|
||||||
@ -23472,6 +23497,24 @@
|
|||||||
"integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==",
|
"integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==",
|
||||||
"dev": true
|
"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": {
|
"espree": {
|
||||||
"version": "9.4.1",
|
"version": "9.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz",
|
||||||
@ -24802,9 +24845,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ip": {
|
"ip": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ip/-/ip-2.0.1.tgz",
|
||||||
"integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ=="
|
"integrity": "sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ=="
|
||||||
},
|
},
|
||||||
"ipaddr.js": {
|
"ipaddr.js": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
@ -26374,9 +26417,9 @@
|
|||||||
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
|
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
|
||||||
},
|
},
|
||||||
"next-tick": {
|
"next-tick": {
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
|
||||||
"integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw="
|
"integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ=="
|
||||||
},
|
},
|
||||||
"ngx-echarts": {
|
"ngx-echarts": {
|
||||||
"version": "16.2.0",
|
"version": "16.2.0",
|
||||||
|
@ -9,23 +9,38 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</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}">
|
<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'">
|
<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
|
<input type="radio" [value]="'24h'" fragment="24h" [routerLink]="['/graphs/acceleration/fees' | relativeUrl]" formControlName="dateSpan"> 24H
|
||||||
</label>
|
</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
|
<input type="radio" [value]="'3d'" fragment="3d" [routerLink]="['/graphs/acceleration/fees' | relativeUrl]" formControlName="dateSpan"> 3D
|
||||||
</label>
|
</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
|
<input type="radio" [value]="'1w'" fragment="1w" [routerLink]="['/graphs/acceleration/fees' | relativeUrl]" formControlName="dateSpan"> 1W
|
||||||
</label>
|
</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
|
<input type="radio" [value]="'1m'" fragment="1m" [routerLink]="['/graphs/acceleration/fees' | relativeUrl]" formControlName="dateSpan"> 1M
|
||||||
</label>
|
</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
|
<input type="radio" [value]="'3m'" fragment="3m" [routerLink]="['/graphs/acceleration/fees' | relativeUrl]" formControlName="dateSpan"> 3M
|
||||||
</label>
|
</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>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Input, LOCALE_ID, OnDestroy, OnInit } from '@angular/core';
|
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Input, LOCALE_ID, OnDestroy, OnInit } from '@angular/core';
|
||||||
import { EChartsOption } from 'echarts';
|
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 { startWith, switchMap, tap } from 'rxjs/operators';
|
||||||
import { SeoService } from '../../../services/seo.service';
|
import { SeoService } from '../../../services/seo.service';
|
||||||
import { formatNumber } from '@angular/common';
|
import { formatNumber } from '@angular/common';
|
||||||
@ -41,8 +41,7 @@ export class AccelerationFeesGraphComponent implements OnInit, OnDestroy {
|
|||||||
renderer: 'svg',
|
renderer: 'svg',
|
||||||
};
|
};
|
||||||
|
|
||||||
hrStatsObservable$: Observable<any>;
|
aggregatedHistory$: Observable<any>;
|
||||||
statsObservable$: Observable<any>;
|
|
||||||
statsSubscription: Subscription;
|
statsSubscription: Subscription;
|
||||||
isLoading = true;
|
isLoading = true;
|
||||||
formatNumber = formatNumber;
|
formatNumber = formatNumber;
|
||||||
@ -50,6 +49,7 @@ export class AccelerationFeesGraphComponent implements OnInit, OnDestroy {
|
|||||||
chartInstance: any = undefined;
|
chartInstance: any = undefined;
|
||||||
|
|
||||||
currency: string;
|
currency: string;
|
||||||
|
daysAvailable: number = 0;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(LOCALE_ID) public locale: string,
|
@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.radioGroupForm.controls.dateSpan.setValue(fragment, { emitEvent: false });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.statsObservable$ = combineLatest([
|
this.aggregatedHistory$ = combineLatest([
|
||||||
this.radioGroupForm.get('dateSpan').valueChanges.pipe(
|
this.radioGroupForm.get('dateSpan').valueChanges.pipe(
|
||||||
startWith(this.radioGroupForm.controls.dateSpan.value),
|
startWith(this.radioGroupForm.controls.dateSpan.value),
|
||||||
switchMap((timespan) => {
|
switchMap((timespan) => {
|
||||||
@ -95,14 +95,17 @@ export class AccelerationFeesGraphComponent implements OnInit, OnDestroy {
|
|||||||
),
|
),
|
||||||
fromEvent(window, 'resize').pipe(startWith(null)),
|
fromEvent(window, 'resize').pipe(startWith(null)),
|
||||||
]).pipe(
|
]).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.isLoading = false;
|
||||||
this.prepareChartOptions(history);
|
this.prepareChartOptions(history);
|
||||||
this.cd.markForCheck();
|
this.cd.markForCheck();
|
||||||
})
|
}),
|
||||||
|
share(),
|
||||||
);
|
);
|
||||||
|
|
||||||
this.statsObservable$.subscribe();
|
this.aggregatedHistory$.subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareChartOptions(data) {
|
prepareChartOptions(data) {
|
||||||
@ -247,6 +250,7 @@ export class AccelerationFeesGraphComponent implements OnInit, OnDestroy {
|
|||||||
type: 'bar',
|
type: 'bar',
|
||||||
barWidth: '90%',
|
barWidth: '90%',
|
||||||
large: true,
|
large: true,
|
||||||
|
barMinHeight: 1,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
dataZoom: (this.widget || data.length === 0 )? undefined : [{
|
dataZoom: (this.widget || data.length === 0 )? undefined : [{
|
||||||
|
@ -44,7 +44,7 @@ export class AccelerationsListComponent implements OnInit {
|
|||||||
|
|
||||||
this.accelerationList$ = this.pageSubject.pipe(
|
this.accelerationList$ = this.pageSubject.pipe(
|
||||||
switchMap((page) => {
|
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(
|
return accelerationObservable$.pipe(
|
||||||
switchMap(response => {
|
switchMap(response => {
|
||||||
let accelerations = response;
|
let accelerations = response;
|
||||||
|
@ -60,7 +60,7 @@ export class AcceleratorDashboardComponent implements OnInit {
|
|||||||
this.accelerations$ = this.stateService.chainTip$.pipe(
|
this.accelerations$ = this.stateService.chainTip$.pipe(
|
||||||
distinctUntilChanged(),
|
distinctUntilChanged(),
|
||||||
switchMap(() => {
|
switchMap(() => {
|
||||||
return this.serviceApiServices.getAccelerationHistory$({ timeframe: '3m', page: 1, pageLength: 100}).pipe(
|
return this.serviceApiServices.getAccelerationHistory$({}).pipe(
|
||||||
catchError(() => {
|
catchError(() => {
|
||||||
return of([]);
|
return of([]);
|
||||||
}),
|
}),
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<div class="block-overview-graph">
|
<div class="block-overview-graph">
|
||||||
<canvas class="block-overview-canvas" [class.clickable]="!!hoverTx" #blockCanvas></canvas>
|
<canvas class="block-overview-canvas" [class.clickable]="!!hoverTx" #blockCanvas></canvas>
|
||||||
<div class="loader-wrapper" [class.hidden]="(!isLoading || disableSpinner) && !unavailable">
|
<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 *ngIf="!isLoading && unavailable" class="ml-3" i18n="block.not-available">not available</div>
|
||||||
</div>
|
</div>
|
||||||
<app-block-overview-tooltip
|
<app-block-overview-tooltip
|
||||||
|
@ -60,6 +60,5 @@
|
|||||||
|
|
||||||
&.hidden {
|
&.hidden {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity 500ms;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -433,6 +433,9 @@
|
|||||||
<td i18n="block.total-fees|Total fees in a block">Total fees</td>
|
<td i18n="block.total-fees|Total fees in a block">Total fees</td>
|
||||||
<td>
|
<td>
|
||||||
<app-amount [satoshis]="block.extras.totalFees" digitsInfo="1.2-3" [noFiat]="true"></app-amount>
|
<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">
|
<span *ngIf="blockAudit.feeDelta" class="difference" [class.positive]="blockAudit.feeDelta <= 0" [class.negative]="blockAudit.feeDelta > 0">
|
||||||
{{ blockAudit.feeDelta < 0 ? '+' : '' }}{{ (-blockAudit.feeDelta * 100) | amountShortener: 2 }}%
|
{{ blockAudit.feeDelta < 0 ? '+' : '' }}{{ (-blockAudit.feeDelta * 100) | amountShortener: 2 }}%
|
||||||
</span>
|
</span>
|
||||||
|
@ -288,6 +288,10 @@ h1 {
|
|||||||
@media (max-width: 767.98px) {
|
@media (max-width: 767.98px) {
|
||||||
margin-top: 0.75rem;
|
margin-top: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.oobFees {
|
||||||
|
color: #653b9c;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.graph-col {
|
.graph-col {
|
||||||
|
@ -9,7 +9,7 @@ import { StateService } from '../../services/state.service';
|
|||||||
import { SeoService } from '../../services/seo.service';
|
import { SeoService } from '../../services/seo.service';
|
||||||
import { WebsocketService } from '../../services/websocket.service';
|
import { WebsocketService } from '../../services/websocket.service';
|
||||||
import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe';
|
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 { ApiService } from '../../services/api.service';
|
||||||
import { BlockOverviewGraphComponent } from '../../components/block-overview-graph/block-overview-graph.component';
|
import { BlockOverviewGraphComponent } from '../../components/block-overview-graph/block-overview-graph.component';
|
||||||
import { detectWebGL } from '../../shared/graphs.utils';
|
import { detectWebGL } from '../../shared/graphs.utils';
|
||||||
@ -43,6 +43,7 @@ export class BlockComponent implements OnInit, OnDestroy {
|
|||||||
latestBlock: BlockExtended;
|
latestBlock: BlockExtended;
|
||||||
latestBlocks: BlockExtended[] = [];
|
latestBlocks: BlockExtended[] = [];
|
||||||
transactions: Transaction[];
|
transactions: Transaction[];
|
||||||
|
oobFees: number = 0;
|
||||||
isLoadingTransactions = true;
|
isLoadingTransactions = true;
|
||||||
strippedTransactions: TransactionStripped[];
|
strippedTransactions: TransactionStripped[];
|
||||||
overviewTransitionDirection: string;
|
overviewTransitionDirection: string;
|
||||||
@ -85,6 +86,7 @@ export class BlockComponent implements OnInit, OnDestroy {
|
|||||||
timeLtr: boolean;
|
timeLtr: boolean;
|
||||||
childChangeSubscription: Subscription;
|
childChangeSubscription: Subscription;
|
||||||
auditPrefSubscription: Subscription;
|
auditPrefSubscription: Subscription;
|
||||||
|
oobSubscription: Subscription;
|
||||||
|
|
||||||
priceSubscription: Subscription;
|
priceSubscription: Subscription;
|
||||||
blockConversion: Price;
|
blockConversion: Price;
|
||||||
@ -168,6 +170,7 @@ export class BlockComponent implements OnInit, OnDestroy {
|
|||||||
this.page = 1;
|
this.page = 1;
|
||||||
this.error = undefined;
|
this.error = undefined;
|
||||||
this.fees = undefined;
|
this.fees = undefined;
|
||||||
|
this.oobFees = 0;
|
||||||
|
|
||||||
if (history.state.data && history.state.data.blockHeight) {
|
if (history.state.data && history.state.data.blockHeight) {
|
||||||
this.blockHeight = history.state.data.blockHeight;
|
this.blockHeight = history.state.data.blockHeight;
|
||||||
@ -446,7 +449,7 @@ export class BlockComponent implements OnInit, OnDestroy {
|
|||||||
inBlock[tx.txid] = true;
|
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.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;
|
blockAudit.txDelta = blockAudit.template.length > 0 ? (blockAudit.template.length - this.block.tx_count) / blockAudit.template.length : 0;
|
||||||
this.blockAudit = blockAudit;
|
this.blockAudit = blockAudit;
|
||||||
@ -462,6 +465,32 @@ export class BlockComponent implements OnInit, OnDestroy {
|
|||||||
this.setupBlockGraphs();
|
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$
|
this.networkChangedSubscription = this.stateService.networkChanged$
|
||||||
.subscribe((network) => this.network = network);
|
.subscribe((network) => this.network = network);
|
||||||
|
|
||||||
@ -529,6 +558,7 @@ export class BlockComponent implements OnInit, OnDestroy {
|
|||||||
this.unsubscribeNextBlockSubscriptions();
|
this.unsubscribeNextBlockSubscriptions();
|
||||||
this.childChangeSubscription?.unsubscribe();
|
this.childChangeSubscription?.unsubscribe();
|
||||||
this.priceSubscription?.unsubscribe();
|
this.priceSubscription?.unsubscribe();
|
||||||
|
this.oobSubscription?.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
unsubscribeNextBlockSubscriptions() {
|
unsubscribeNextBlockSubscriptions() {
|
||||||
|
@ -56,7 +56,7 @@
|
|||||||
<app-time kind="since" [time]="block.timestamp" [fastRender]="true" [precision]="1" minUnit="minute"></app-time></div>
|
<app-time kind="since" [time]="block.timestamp" [fastRender]="true" [precision]="1" minUnit="minute"></app-time></div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</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"
|
<a [attr.data-cy]="'bitcoin-block-' + offset + '-index-' + i + '-pool'" class="badge badge-primary"
|
||||||
[routerLink]="[('/mining/pool/' + block.extras.pool.slug) | relativeUrl]">
|
[routerLink]="[('/mining/pool/' + block.extras.pool.slug) | relativeUrl]">
|
||||||
{{ block.extras.pool.name}}</a>
|
{{ block.extras.pool.name}}</a>
|
||||||
|
@ -27,6 +27,7 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
@Input() minimal: boolean = false;
|
@Input() minimal: boolean = false;
|
||||||
@Input() blockWidth: number = 125;
|
@Input() blockWidth: number = 125;
|
||||||
@Input() spotlight: number = 0;
|
@Input() spotlight: number = 0;
|
||||||
|
@Input() showPools: boolean = true;
|
||||||
@Input() getHref?: (index, block) => string = (index, block) => `/block/${block.id}`;
|
@Input() getHref?: (index, block) => string = (index, block) => `/block/${block.id}`;
|
||||||
|
|
||||||
specialBlocks = specialBlocks;
|
specialBlocks = specialBlocks;
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
[minimal]="true"
|
[minimal]="true"
|
||||||
[count]="blockchainBlocks"
|
[count]="blockchainBlocks"
|
||||||
[blockWidth]="blockWidth"
|
[blockWidth]="blockWidth"
|
||||||
|
[showPools]="false"
|
||||||
[spotlight]="mode === 'mined' ? -index - 1 : 0"
|
[spotlight]="mode === 'mined' ? -index - 1 : 0"
|
||||||
[getHref]="getMinedUrl"
|
[getHref]="getMinedUrl"
|
||||||
></app-blockchain-blocks>
|
></app-blockchain-blocks>
|
||||||
|
@ -16,9 +16,9 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<a class="navbar-brand" [ngClass]="{'dual-logos': subdomain}" [routerLink]="['/' | relativeUrl]" (click)="brandClick($event)">
|
<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">
|
<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>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<ng-container *ngIf="{ val: connectionState$ | async } as connectionState">
|
<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 { Router } from '@angular/router';
|
||||||
import { Env, StateService } from '../../services/state.service';
|
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 { LanguageService } from '../../services/language.service';
|
||||||
import { EnterpriseService } from '../../services/enterprise.service';
|
import { EnterpriseService } from '../../services/enterprise.service';
|
||||||
import { NavigationService } from '../../services/navigation.service';
|
import { NavigationService } from '../../services/navigation.service';
|
||||||
@ -14,7 +14,7 @@ import { ApiService } from '../../services/api.service';
|
|||||||
templateUrl: './master-page.component.html',
|
templateUrl: './master-page.component.html',
|
||||||
styleUrls: ['./master-page.component.scss'],
|
styleUrls: ['./master-page.component.scss'],
|
||||||
})
|
})
|
||||||
export class MasterPageComponent implements OnInit {
|
export class MasterPageComponent implements OnInit, OnDestroy {
|
||||||
@Input() headerVisible = true;
|
@Input() headerVisible = true;
|
||||||
@Input() footerVisibleOverride: boolean | null = null;
|
@Input() footerVisibleOverride: boolean | null = null;
|
||||||
|
|
||||||
@ -32,6 +32,9 @@ export class MasterPageComponent implements OnInit {
|
|||||||
user: any = undefined;
|
user: any = undefined;
|
||||||
servicesEnabled = false;
|
servicesEnabled = false;
|
||||||
menuOpen = false;
|
menuOpen = false;
|
||||||
|
|
||||||
|
enterpriseInfo: any;
|
||||||
|
enterpriseInfo$: Subscription;
|
||||||
|
|
||||||
@ViewChild(MenuComponent)
|
@ViewChild(MenuComponent)
|
||||||
public menuComponent!: MenuComponent;
|
public menuComponent!: MenuComponent;
|
||||||
@ -64,6 +67,9 @@ export class MasterPageComponent implements OnInit {
|
|||||||
this.footerVisible = this.footerVisibleOverride;
|
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.servicesEnabled = this.officialMempoolSpace && this.stateService.env.ACCELERATOR === true && this.stateService.network === '';
|
||||||
this.refreshAuth();
|
this.refreshAuth();
|
||||||
@ -72,6 +78,12 @@ export class MasterPageComponent implements OnInit {
|
|||||||
this.menuOpen = isServicesPage && !this.isSmallScreen();
|
this.menuOpen = isServicesPage && !this.isSmallScreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
if (this.enterpriseInfo$) {
|
||||||
|
this.enterpriseInfo$.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
collapse(): void {
|
collapse(): void {
|
||||||
this.navCollapsed = !this.navCollapsed;
|
this.navCollapsed = !this.navCollapsed;
|
||||||
}
|
}
|
||||||
|
@ -70,6 +70,26 @@
|
|||||||
<app-tx-features [tx]="tx"></app-tx-features>
|
<app-tx-features [tx]="tx"></app-tx-features>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</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>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
@ -509,7 +529,7 @@
|
|||||||
<ng-template #feeTable>
|
<ng-template #feeTable>
|
||||||
<table class="table table-borderless table-striped">
|
<table class="table table-borderless table-striped">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr *ngIf="isMobile && (network === 'liquid' || network === 'liquidtestnet' || !featuresEnabled)"></tr>
|
<tr *ngIf="isMobile && (network === 'liquid' || network === 'liquidtestnet' || !featuresEnabled || network === '')"></tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="td-width" i18n="transaction.fee|Transaction fee">Fee</td>
|
<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>
|
<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 {
|
.btn {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.wrap-cell {
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,10 +8,11 @@ import {
|
|||||||
retryWhen,
|
retryWhen,
|
||||||
delay,
|
delay,
|
||||||
mergeMap,
|
mergeMap,
|
||||||
tap
|
tap,
|
||||||
|
map
|
||||||
} from 'rxjs/operators';
|
} from 'rxjs/operators';
|
||||||
import { Transaction } from '../../interfaces/electrs.interface';
|
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 { StateService } from '../../services/state.service';
|
||||||
import { CacheService } from '../../services/cache.service';
|
import { CacheService } from '../../services/cache.service';
|
||||||
import { WebsocketService } from '../../services/websocket.service';
|
import { WebsocketService } from '../../services/websocket.service';
|
||||||
@ -28,6 +29,22 @@ import { isFeatureActive } from '../../bitcoin.utils';
|
|||||||
import { ServicesApiServices } from '../../services/services-api.service';
|
import { ServicesApiServices } from '../../services/services-api.service';
|
||||||
import { EnterpriseService } from '../../services/enterprise.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({
|
@Component({
|
||||||
selector: 'app-transaction',
|
selector: 'app-transaction',
|
||||||
templateUrl: './transaction.component.html',
|
templateUrl: './transaction.component.html',
|
||||||
@ -58,6 +75,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
urlFragmentSubscription: Subscription;
|
urlFragmentSubscription: Subscription;
|
||||||
mempoolBlocksSubscription: Subscription;
|
mempoolBlocksSubscription: Subscription;
|
||||||
blocksSubscription: Subscription;
|
blocksSubscription: Subscription;
|
||||||
|
miningSubscription: Subscription;
|
||||||
fragmentParams: URLSearchParams;
|
fragmentParams: URLSearchParams;
|
||||||
rbfTransaction: undefined | Transaction;
|
rbfTransaction: undefined | Transaction;
|
||||||
replaced: boolean = false;
|
replaced: boolean = false;
|
||||||
@ -67,11 +85,14 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
accelerationInfo: Acceleration | null = null;
|
accelerationInfo: Acceleration | null = null;
|
||||||
sigops: number | null;
|
sigops: number | null;
|
||||||
adjustedVsize: number | null;
|
adjustedVsize: number | null;
|
||||||
|
pool: Pool | null;
|
||||||
|
auditStatus: AuditStatus | null;
|
||||||
showCpfpDetails = false;
|
showCpfpDetails = false;
|
||||||
fetchCpfp$ = new Subject<string>();
|
fetchCpfp$ = new Subject<string>();
|
||||||
fetchRbfHistory$ = new Subject<string>();
|
fetchRbfHistory$ = new Subject<string>();
|
||||||
fetchCachedTx$ = new Subject<string>();
|
fetchCachedTx$ = new Subject<string>();
|
||||||
fetchAcceleration$ = new Subject<string>();
|
fetchAcceleration$ = new Subject<string>();
|
||||||
|
fetchMiningInfo$ = new Subject<{ hash: string, height: number, txid: string }>();
|
||||||
isCached: boolean = false;
|
isCached: boolean = false;
|
||||||
now = Date.now();
|
now = Date.now();
|
||||||
da$: Observable<DifficultyAdjustment>;
|
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 === '';
|
acceleratorAvailable: boolean = this.stateService.env.OFFICIAL_MEMPOOL_SPACE && this.stateService.env.ACCELERATOR && this.stateService.network === '';
|
||||||
showAccelerationSummary = false;
|
showAccelerationSummary = false;
|
||||||
scrollIntoAccelPreview = false;
|
scrollIntoAccelPreview = false;
|
||||||
|
auditEnabled: boolean = this.stateService.env.AUDIT && this.stateService.env.BASE_MODULE === 'mempool' && this.stateService.env.MINING_DASHBOARD === true;
|
||||||
|
|
||||||
@ViewChild('graphContainer')
|
@ViewChild('graphContainer')
|
||||||
graphContainer: ElementRef;
|
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.mempoolPositionSubscription = this.stateService.mempoolTxPosition$.subscribe(txPosition => {
|
||||||
this.now = Date.now();
|
this.now = Date.now();
|
||||||
if (txPosition && txPosition.txid === this.txId && txPosition.position) {
|
if (txPosition && txPosition.txid === this.txId && txPosition.position) {
|
||||||
@ -396,6 +466,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.fetchAcceleration$.next(tx.status.block_hash);
|
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;
|
this.transactionTime = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -453,6 +524,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
this.audioService.playSound('magic');
|
this.audioService.playSound('magic');
|
||||||
}
|
}
|
||||||
this.fetchAcceleration$.next(block.id);
|
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;
|
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() {
|
resetTransaction() {
|
||||||
this.error = undefined;
|
this.error = undefined;
|
||||||
this.tx = null;
|
this.tx = null;
|
||||||
@ -625,6 +720,8 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
this.accelerationInfo = null;
|
this.accelerationInfo = null;
|
||||||
this.txInBlockIndex = null;
|
this.txInBlockIndex = null;
|
||||||
this.mempoolPosition = null;
|
this.mempoolPosition = null;
|
||||||
|
this.pool = null;
|
||||||
|
this.auditStatus = null;
|
||||||
document.body.scrollTo(0, 0);
|
document.body.scrollTo(0, 0);
|
||||||
this.leaveTransaction();
|
this.leaveTransaction();
|
||||||
}
|
}
|
||||||
@ -712,6 +809,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
this.mempoolPositionSubscription.unsubscribe();
|
this.mempoolPositionSubscription.unsubscribe();
|
||||||
this.mempoolBlocksSubscription.unsubscribe();
|
this.mempoolBlocksSubscription.unsubscribe();
|
||||||
this.blocksSubscription.unsubscribe();
|
this.blocksSubscription.unsubscribe();
|
||||||
|
this.miningSubscription?.unsubscribe();
|
||||||
this.leaveTransaction();
|
this.leaveTransaction();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -236,7 +236,7 @@ export class DashboardComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||||||
acc.unshift(stats);
|
acc.unshift(stats);
|
||||||
acc = acc.slice(0, 120);
|
acc = acc.slice(0, 120);
|
||||||
return acc;
|
return acc;
|
||||||
}, mempoolStats)
|
}, (mempoolStats || []))
|
||||||
),
|
),
|
||||||
of(mempoolStats)
|
of(mempoolStats)
|
||||||
);
|
);
|
||||||
|
@ -9878,16 +9878,179 @@ export const restApiDocsData = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "category",
|
type: "category",
|
||||||
category: "accelerator",
|
category: "accelerator-public",
|
||||||
fragment: "accelerator",
|
fragment: "accelerator-public",
|
||||||
title: "Accelerator",
|
title: "Accelerator (Public)",
|
||||||
showConditions: [""],
|
showConditions: [""],
|
||||||
options: { officialOnly: true },
|
options: { officialOnly: true },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
options: { officialOnly: true },
|
options: { officialOnly: true },
|
||||||
type: "endpoint",
|
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",
|
httpRequestMethod: "GET",
|
||||||
fragment: "accelerator-deposit-history",
|
fragment: "accelerator-deposit-history",
|
||||||
title: "GET Deposit History",
|
title: "GET Deposit History",
|
||||||
@ -9935,7 +10098,7 @@ export const restApiDocsData = [
|
|||||||
{
|
{
|
||||||
options: { officialOnly: true },
|
options: { officialOnly: true },
|
||||||
type: "endpoint",
|
type: "endpoint",
|
||||||
category: "accelerator",
|
category: "accelerator-private",
|
||||||
httpRequestMethod: "GET",
|
httpRequestMethod: "GET",
|
||||||
fragment: "accelerator-balance",
|
fragment: "accelerator-balance",
|
||||||
title: "GET Available Balance",
|
title: "GET Available Balance",
|
||||||
@ -9969,51 +10132,7 @@ export const restApiDocsData = [
|
|||||||
{
|
{
|
||||||
options: { officialOnly: true },
|
options: { officialOnly: true },
|
||||||
type: "endpoint",
|
type: "endpoint",
|
||||||
category: "accelerator",
|
category: "accelerator-private",
|
||||||
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",
|
|
||||||
httpRequestMethod: "POST",
|
httpRequestMethod: "POST",
|
||||||
fragment: "accelerator-accelerate",
|
fragment: "accelerator-accelerate",
|
||||||
title: "POST Accelerate A Transaction",
|
title: "POST Accelerate A Transaction",
|
||||||
@ -10043,10 +10162,10 @@ export const restApiDocsData = [
|
|||||||
{
|
{
|
||||||
options: { officialOnly: true },
|
options: { officialOnly: true },
|
||||||
type: "endpoint",
|
type: "endpoint",
|
||||||
category: "accelerator",
|
category: "accelerator-private",
|
||||||
httpRequestMethod: "GET",
|
httpRequestMethod: "GET",
|
||||||
fragment: "accelerator-history",
|
fragment: "accelerator-history",
|
||||||
title: "GET Private Acceleration History",
|
title: "GET Acceleration History",
|
||||||
description: {
|
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>"
|
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>
|
<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>
|
<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>
|
</div>
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
|
|
||||||
<div class="doc-content">
|
<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>
|
<p>Get higher API limits with <span class="no-line-break">Mempool Enterprise®</span></p>
|
||||||
<div class="button-group">
|
<div class="button-group">
|
||||||
<a class="btn btn-small btn-secondary" (click)="showMobileEnterpriseUpsell = false">No Thanks</a>
|
<a class="btn btn-small btn-secondary" (click)="showMobileEnterpriseUpsell = false">No Thanks</a>
|
||||||
|
@ -403,4 +403,18 @@ export interface AccelerationHistoryParams {
|
|||||||
blockHeight?: number;
|
blockHeight?: number;
|
||||||
page?: number;
|
page?: number;
|
||||||
pageLength?: 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 { Injectable } from '@angular/core';
|
||||||
import { HttpClient, HttpParams, HttpResponse } from '@angular/common/http';
|
import { HttpClient, HttpParams, HttpResponse } from '@angular/common/http';
|
||||||
import { CpfpInfo, OptimizedMempoolStats, AddressInformation, LiquidPegs, ITranslators,
|
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 { BehaviorSubject, Observable, catchError, filter, of, shareReplay, take, tap } from 'rxjs';
|
||||||
import { StateService } from './state.service';
|
import { StateService } from './state.service';
|
||||||
import { Transaction } from '../interfaces/electrs.interface';
|
import { Transaction } from '../interfaces/electrs.interface';
|
||||||
@ -459,4 +459,22 @@ export class ApiService {
|
|||||||
(queryParams.length > 0 ? `?${queryParams.join('&')}` : '')
|
(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 { SeoService } from './seo.service';
|
||||||
import { StateService } from './state.service';
|
import { StateService } from './state.service';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { BehaviorSubject } from 'rxjs';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@ -11,9 +12,9 @@ import { ActivatedRoute } from '@angular/router';
|
|||||||
export class EnterpriseService {
|
export class EnterpriseService {
|
||||||
exclusiveHostName = '.mempool.space';
|
exclusiveHostName = '.mempool.space';
|
||||||
subdomain: string | null = null;
|
subdomain: string | null = null;
|
||||||
info: object = {};
|
|
||||||
statsUrl: string;
|
statsUrl: string;
|
||||||
siteId: number;
|
siteId: number;
|
||||||
|
info$: BehaviorSubject<object> = new BehaviorSubject(null);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DOCUMENT) private document: Document,
|
@Inject(DOCUMENT) private document: Document,
|
||||||
@ -47,9 +48,9 @@ export class EnterpriseService {
|
|||||||
|
|
||||||
fetchSubdomainInfo(): void {
|
fetchSubdomainInfo(): void {
|
||||||
this.apiService.getEnterpriseInfo$(this.subdomain).subscribe((info) => {
|
this.apiService.getEnterpriseInfo$(this.subdomain).subscribe((info) => {
|
||||||
this.info = info;
|
|
||||||
this.insertMatomo(info.site_id);
|
this.insertMatomo(info.site_id);
|
||||||
this.seoService.setEnterpriseTitle(info.title);
|
this.seoService.setEnterpriseTitle(info.title);
|
||||||
|
this.info$.next(info);
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
if (error.status === 404) {
|
if (error.status === 404) {
|
||||||
|
@ -145,8 +145,8 @@ export class ServicesApiServices {
|
|||||||
return this.httpClient.get<Acceleration[]>(`${SERVICES_API_PREFIX}/accelerator/accelerations`);
|
return this.httpClient.get<Acceleration[]>(`${SERVICES_API_PREFIX}/accelerator/accelerations`);
|
||||||
}
|
}
|
||||||
|
|
||||||
getAggregatedAccelerationHistory$(params: AccelerationHistoryParams): Observable<Acceleration[]> {
|
getAggregatedAccelerationHistory$(params: AccelerationHistoryParams): Observable<any> {
|
||||||
return this.httpClient.get<Acceleration[]>(`${SERVICES_API_PREFIX}/accelerator/accelerations/history/aggregated`, { params: { ...params } });
|
return this.httpClient.get<any>(`${SERVICES_API_PREFIX}/accelerator/accelerations/history/aggregated`, { params: { ...params }, observe: 'response' });
|
||||||
}
|
}
|
||||||
|
|
||||||
getAccelerationHistory$(params: AccelerationHistoryParams): Observable<Acceleration[]> {
|
getAccelerationHistory$(params: AccelerationHistoryParams): Observable<Acceleration[]> {
|
||||||
|
@ -116,7 +116,7 @@ export class WebsocketService {
|
|||||||
this.startMultiTrackTransaction(this.trackingTxId);
|
this.startMultiTrackTransaction(this.trackingTxId);
|
||||||
}
|
}
|
||||||
if (this.isTrackingMempoolBlock) {
|
if (this.isTrackingMempoolBlock) {
|
||||||
this.startTrackMempoolBlock(this.trackingMempoolBlock);
|
this.startTrackMempoolBlock(this.trackingMempoolBlock, true);
|
||||||
}
|
}
|
||||||
if (this.isTrackingRbf) {
|
if (this.isTrackingRbf) {
|
||||||
this.startTrackRbf(this.isTrackingRbf);
|
this.startTrackRbf(this.isTrackingRbf);
|
||||||
@ -197,9 +197,9 @@ export class WebsocketService {
|
|||||||
this.websocketSubject.next({ 'track-asset': 'stop' });
|
this.websocketSubject.next({ 'track-asset': 'stop' });
|
||||||
}
|
}
|
||||||
|
|
||||||
startTrackMempoolBlock(block: number) {
|
startTrackMempoolBlock(block: number, force: boolean = false) {
|
||||||
// skip duplicate tracking requests
|
// skip duplicate tracking requests
|
||||||
if (this.trackingMempoolBlock !== block) {
|
if (force || this.trackingMempoolBlock !== block) {
|
||||||
this.websocketSubject.next({ 'track-mempool-block': block });
|
this.websocketSubject.next({ 'track-mempool-block': block });
|
||||||
this.isTrackingMempoolBlock = true;
|
this.isTrackingMempoolBlock = true;
|
||||||
this.trackingMempoolBlock = block;
|
this.trackingMempoolBlock = block;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user