Resolve conflicts in dashboard component
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
<div class="block-filters" [class.filters-active]="activeFilters.length > 0" [class.menu-open]="menuOpen" [class.small]="cssWidth < 500" [class.vsmall]="cssWidth < 400" [class.tiny]="cssWidth < 200">
|
||||
<div class="block-filters" [class.filters-active]="activeFilters.length > 0" [class.any-mode]="filterMode === 'or'" [class.menu-open]="menuOpen" [class.small]="cssWidth < 500" [class.vsmall]="cssWidth < 400" [class.tiny]="cssWidth < 200">
|
||||
<a *ngIf="menuOpen" [routerLink]="['/docs/faq' | relativeUrl]" fragment="how-do-mempool-goggles-work" class="info-badges" i18n-ngbTooltip="Mempool Goggles tooltip" ngbTooltip="select filter categories to highlight matching transactions">
|
||||
<span class="badge badge-pill badge-warning beta" i18n="beta">beta</span>
|
||||
<fa-icon [icon]="['fas', 'info-circle']" [fixedWidth]="true" size="lg"></fa-icon>
|
||||
@@ -14,6 +14,15 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="filter-menu" *ngIf="menuOpen && cssWidth > 280">
|
||||
<h5>Match</h5>
|
||||
<div class="btn-group btn-group-toggle">
|
||||
<label class="btn btn-xs blue mode-toggle" [class.active]="filterMode === 'and'">
|
||||
<input type="radio" [value]="'all'" fragment="all" (click)="setFilterMode('and')">All
|
||||
</label>
|
||||
<label class="btn btn-xs green mode-toggle" [class.active]="filterMode === 'or'">
|
||||
<input type="radio" [value]="'any'" fragment="any" (click)="setFilterMode('or')">Any
|
||||
</label>
|
||||
</div>
|
||||
<ng-container *ngFor="let group of filterGroups;">
|
||||
<h5>{{ group.label }}</h5>
|
||||
<div class="filter-group">
|
||||
|
||||
@@ -77,6 +77,49 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.any-mode {
|
||||
.filter-tag {
|
||||
border: solid 1px #1a9436;
|
||||
&.selected {
|
||||
background-color: #1a9436;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
font-size: 0.9em;
|
||||
margin-right: 0.25em;
|
||||
}
|
||||
|
||||
.mode-toggle {
|
||||
padding: 0.2em 0.5em;
|
||||
pointer-events: all;
|
||||
line-height: 1.5;
|
||||
background: #181b2daf;
|
||||
|
||||
&:first-child {
|
||||
border-top-left-radius: 0.2rem;
|
||||
border-bottom-left-radius: 0.2rem;
|
||||
}
|
||||
&:last-child {
|
||||
border-top-right-radius: 0.2rem;
|
||||
border-bottom-right-radius: 0.2rem;
|
||||
}
|
||||
|
||||
&.blue {
|
||||
border: solid 1px #105fb0;
|
||||
&.active {
|
||||
background: #105fb0;
|
||||
}
|
||||
}
|
||||
&.green {
|
||||
border: solid 1px #1a9436;
|
||||
&.active {
|
||||
background: #1a9436;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:host-context(.block-overview-graph:hover) &, &:hover, &:active {
|
||||
.menu-toggle {
|
||||
opacity: 0.5;
|
||||
@@ -132,6 +175,11 @@
|
||||
.filter-tag {
|
||||
font-size: 0.7em;
|
||||
}
|
||||
.mode-toggle {
|
||||
font-size: 0.7em;
|
||||
margin-bottom: 5px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
&.tiny {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Component, EventEmitter, Output, HostListener, Input, ChangeDetectorRef, OnChanges, SimpleChanges, OnInit, OnDestroy } from '@angular/core';
|
||||
import { FilterGroups, TransactionFilters } from '../../shared/filters.utils';
|
||||
import { ActiveFilter, FilterGroups, FilterMode, TransactionFilters } from '../../shared/filters.utils';
|
||||
import { StateService } from '../../services/state.service';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
@@ -12,7 +12,7 @@ import { Subscription } from 'rxjs';
|
||||
export class BlockFiltersComponent implements OnInit, OnChanges, OnDestroy {
|
||||
@Input() cssWidth: number = 800;
|
||||
@Input() excludeFilters: string[] = [];
|
||||
@Output() onFilterChanged: EventEmitter<bigint | null> = new EventEmitter();
|
||||
@Output() onFilterChanged: EventEmitter<ActiveFilter | null> = new EventEmitter();
|
||||
|
||||
filterSubscription: Subscription;
|
||||
|
||||
@@ -21,6 +21,7 @@ export class BlockFiltersComponent implements OnInit, OnChanges, OnDestroy {
|
||||
disabledFilters: { [key: string]: boolean } = {};
|
||||
activeFilters: string[] = [];
|
||||
filterFlags: { [key: string]: boolean } = {};
|
||||
filterMode: FilterMode = 'and';
|
||||
menuOpen: boolean = false;
|
||||
|
||||
constructor(
|
||||
@@ -29,15 +30,16 @@ export class BlockFiltersComponent implements OnInit, OnChanges, OnDestroy {
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.filterSubscription = this.stateService.activeGoggles$.subscribe((activeFilters: string[]) => {
|
||||
this.filterSubscription = this.stateService.activeGoggles$.subscribe((active: ActiveFilter) => {
|
||||
this.filterMode = active.mode;
|
||||
for (const key of Object.keys(this.filterFlags)) {
|
||||
this.filterFlags[key] = false;
|
||||
}
|
||||
for (const key of activeFilters) {
|
||||
for (const key of active.filters) {
|
||||
this.filterFlags[key] = !this.disabledFilters[key];
|
||||
}
|
||||
this.activeFilters = [...activeFilters.filter(key => !this.disabledFilters[key])];
|
||||
this.onFilterChanged.emit(this.getBooleanFlags());
|
||||
this.activeFilters = [...active.filters.filter(key => !this.disabledFilters[key])];
|
||||
this.onFilterChanged.emit({ mode: active.mode, filters: this.activeFilters });
|
||||
});
|
||||
}
|
||||
|
||||
@@ -53,6 +55,12 @@ export class BlockFiltersComponent implements OnInit, OnChanges, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
setFilterMode(mode): void {
|
||||
this.filterMode = mode;
|
||||
this.onFilterChanged.emit({ mode: this.filterMode, filters: this.activeFilters });
|
||||
this.stateService.activeGoggles$.next({ mode: this.filterMode, filters: [...this.activeFilters] });
|
||||
}
|
||||
|
||||
toggleFilter(key): void {
|
||||
const filter = this.filters[key];
|
||||
this.filterFlags[key] = !this.filterFlags[key];
|
||||
@@ -73,8 +81,8 @@ export class BlockFiltersComponent implements OnInit, OnChanges, OnDestroy {
|
||||
this.activeFilters = this.activeFilters.filter(f => f != key);
|
||||
}
|
||||
const booleanFlags = this.getBooleanFlags();
|
||||
this.onFilterChanged.emit(booleanFlags);
|
||||
this.stateService.activeGoggles$.next([...this.activeFilters]);
|
||||
this.onFilterChanged.emit({ mode: this.filterMode, filters: this.activeFilters });
|
||||
this.stateService.activeGoggles$.next({ mode: this.filterMode, filters: [...this.activeFilters] });
|
||||
}
|
||||
|
||||
getBooleanFlags(): bigint | null {
|
||||
@@ -90,7 +98,7 @@ export class BlockFiltersComponent implements OnInit, OnChanges, OnDestroy {
|
||||
@HostListener('document:click', ['$event'])
|
||||
onClick(event): boolean {
|
||||
// click away from menu
|
||||
if (!event.target.closest('button')) {
|
||||
if (!event.target.closest('button') && !event.target.closest('label')) {
|
||||
this.menuOpen = false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
[auditEnabled]="auditHighlighting"
|
||||
[blockConversion]="blockConversion"
|
||||
></app-block-overview-tooltip>
|
||||
<app-block-filters *ngIf="showFilters && filtersAvailable" [excludeFilters]="excludeFilters" [cssWidth]="cssWidth" (onFilterChanged)="setFilterFlags($event)"></app-block-filters>
|
||||
<app-block-filters *ngIf="webGlEnabled && showFilters && filtersAvailable" [excludeFilters]="excludeFilters" [cssWidth]="cssWidth" (onFilterChanged)="setFilterFlags($event)"></app-block-filters>
|
||||
<div *ngIf="!webGlEnabled" class="placeholder">
|
||||
<span i18n="webgl-disabled">Your browser does not support this feature.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -7,6 +7,19 @@
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
grid-column: 1/-1;
|
||||
|
||||
.placeholder {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.grid-align {
|
||||
|
||||
@@ -9,6 +9,8 @@ import { Price } from '../../services/price.service';
|
||||
import { StateService } from '../../services/state.service';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { defaultColorFunction, setOpacity, defaultFeeColors, defaultAuditFeeColors, defaultMarginalFeeColors, defaultAuditColors } from './utils';
|
||||
import { ActiveFilter, FilterMode, toFlags } from '../../shared/filters.utils';
|
||||
import { detectWebGL } from '../../shared/graphs.utils';
|
||||
|
||||
const unmatchedOpacity = 0.2;
|
||||
const unmatchedFeeColors = defaultFeeColors.map(c => setOpacity(c, unmatchedOpacity));
|
||||
@@ -42,7 +44,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On
|
||||
@Input() showFilters: boolean = false;
|
||||
@Input() excludeFilters: string[] = [];
|
||||
@Input() filterFlags: bigint | null = null;
|
||||
@Input() filterMode: 'and' | 'or' = 'and';
|
||||
@Input() filterMode: FilterMode = 'and';
|
||||
@Input() blockConversion: Price;
|
||||
@Input() overrideColors: ((tx: TxView) => Color) | null = null;
|
||||
@Output() txClickEvent = new EventEmitter<{ tx: TransactionStripped, keyModifier: boolean}>();
|
||||
@@ -76,11 +78,14 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On
|
||||
filtersAvailable: boolean = true;
|
||||
activeFilterFlags: bigint | null = null;
|
||||
|
||||
webGlEnabled = true;
|
||||
|
||||
constructor(
|
||||
readonly ngZone: NgZone,
|
||||
readonly elRef: ElementRef,
|
||||
private stateService: StateService,
|
||||
) {
|
||||
this.webGlEnabled = detectWebGL();
|
||||
this.vertexArray = new FastVertexArray(512, TxSprite.dataSize);
|
||||
this.searchSubscription = this.stateService.searchText$.subscribe((text) => {
|
||||
this.searchText = text;
|
||||
@@ -119,10 +124,11 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On
|
||||
}
|
||||
}
|
||||
|
||||
setFilterFlags(flags?: bigint | null): void {
|
||||
this.activeFilterFlags = this.filterFlags || flags || null;
|
||||
setFilterFlags(goggle?: ActiveFilter): void {
|
||||
this.filterMode = goggle?.mode || this.filterMode;
|
||||
this.activeFilterFlags = goggle?.filters ? toFlags(goggle.filters) : this.filterFlags;
|
||||
if (this.scene) {
|
||||
if (this.activeFilterFlags != null) {
|
||||
if (this.activeFilterFlags != null && this.filtersAvailable) {
|
||||
this.scene.setColorFunction(this.getFilterColorFunction(this.activeFilterFlags));
|
||||
} else {
|
||||
this.scene.setColorFunction(this.overrideColors);
|
||||
@@ -157,7 +163,11 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On
|
||||
|
||||
// initialize the scene without any entry transition
|
||||
setup(transactions: TransactionStripped[]): void {
|
||||
this.filtersAvailable = transactions.reduce((flagSet, tx) => flagSet || tx.flags > 0, false);
|
||||
const filtersAvailable = transactions.reduce((flagSet, tx) => flagSet || tx.flags > 0, false);
|
||||
if (filtersAvailable !== this.filtersAvailable) {
|
||||
this.setFilterFlags();
|
||||
}
|
||||
this.filtersAvailable = filtersAvailable;
|
||||
if (this.scene) {
|
||||
this.scene.setup(transactions);
|
||||
this.readyNextFrame = true;
|
||||
@@ -500,11 +510,13 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On
|
||||
}
|
||||
|
||||
onTxClick(cssX: number, cssY: number, keyModifier: boolean = false) {
|
||||
const x = cssX * window.devicePixelRatio;
|
||||
const y = cssY * window.devicePixelRatio;
|
||||
const selected = this.scene.getTxAt({ x, y });
|
||||
if (selected && selected.txid) {
|
||||
this.txClickEvent.emit({ tx: selected, keyModifier });
|
||||
if (this.scene) {
|
||||
const x = cssX * window.devicePixelRatio;
|
||||
const y = cssY * window.devicePixelRatio;
|
||||
const selected = this.scene.getTxAt({ x, y });
|
||||
if (selected && selected.txid) {
|
||||
this.txClickEvent.emit({ tx: selected, keyModifier });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -524,7 +536,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On
|
||||
|
||||
getFilterColorFunction(flags: bigint): ((tx: TxView) => Color) {
|
||||
return (tx: TxView) => {
|
||||
if ((this.filterMode === 'and' && (tx.bigintFlags & flags) === flags) || (this.filterMode === 'or' && (tx.bigintFlags & flags) > 0n)) {
|
||||
if ((this.filterMode === 'and' && (tx.bigintFlags & flags) === flags) || (this.filterMode === 'or' && (flags === 0n || (tx.bigintFlags & flags) > 0n))) {
|
||||
return defaultColorFunction(tx);
|
||||
} else {
|
||||
return defaultColorFunction(
|
||||
|
||||
@@ -10,6 +10,7 @@ import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pi
|
||||
import { Router } from '@angular/router';
|
||||
import { Color } from '../block-overview-graph/sprite-types';
|
||||
import TxView from '../block-overview-graph/tx-view';
|
||||
import { FilterMode } from '../../shared/filters.utils';
|
||||
|
||||
@Component({
|
||||
selector: 'app-mempool-block-overview',
|
||||
@@ -22,7 +23,7 @@ export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChang
|
||||
@Input() showFilters: boolean = false;
|
||||
@Input() overrideColors: ((tx: TxView) => Color) | null = null;
|
||||
@Input() filterFlags: bigint | undefined = undefined;
|
||||
@Input() filterMode: 'and' | 'or' = 'and';
|
||||
@Input() filterMode: FilterMode = 'and';
|
||||
@Output() txPreviewEvent = new EventEmitter<TransactionStripped | void>();
|
||||
|
||||
@ViewChild('blockGraph') blockGraph: BlockOverviewGraphComponent;
|
||||
@@ -99,7 +100,7 @@ export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChang
|
||||
const inOldBlock = {};
|
||||
const inNewBlock = {};
|
||||
const added: TransactionStripped[] = [];
|
||||
const changed: { txid: string, rate: number | undefined, acc: boolean | undefined }[] = [];
|
||||
const changed: { txid: string, rate: number | undefined, flags: number, acc: boolean | undefined }[] = [];
|
||||
const removed: string[] = [];
|
||||
for (const tx of transactionsStripped) {
|
||||
inNewBlock[tx.txid] = true;
|
||||
@@ -117,6 +118,7 @@ export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChang
|
||||
changed.push({
|
||||
txid: tx.txid,
|
||||
rate: tx.rate,
|
||||
flags: tx.flags,
|
||||
acc: tx.acc
|
||||
});
|
||||
}
|
||||
|
||||
@@ -85,6 +85,6 @@
|
||||
background-color: #f1c40f;
|
||||
}
|
||||
|
||||
.badge-platinium {
|
||||
.badge-platinum {
|
||||
background-color: #653b9c;
|
||||
}
|
||||
@@ -25,7 +25,7 @@
|
||||
<div class="quick-filter">
|
||||
<div class="btn-group btn-group-toggle">
|
||||
<label class="btn btn-primary btn-xs" [class.active]="filter.index === goggleIndex" *ngFor="let filter of goggleCycle">
|
||||
<input type="radio" [value]="'3m'" fragment="3m" (click)="goggleIndex = filter.index" [attr.data-cy]="'3m'"> {{ filter.name }}
|
||||
<input type="radio" [value]="'3m'" fragment="3m" (click)="setFilter(filter.index)" [attr.data-cy]="'3m'"> {{ filter.name }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@@ -33,8 +33,8 @@
|
||||
<app-mempool-block-overview
|
||||
[index]="0"
|
||||
[resolution]="goggleResolution"
|
||||
[filterFlags]="goggleCycle[goggleIndex].flag"
|
||||
filterMode="or"
|
||||
[filterFlags]="goggleFlags"
|
||||
[filterMode]="goggleMode"
|
||||
></app-mempool-block-overview>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -7,6 +7,7 @@ import { ApiService } from '../services/api.service';
|
||||
import { StateService } from '../services/state.service';
|
||||
import { WebsocketService } from '../services/websocket.service';
|
||||
import { SeoService } from '../services/seo.service';
|
||||
import { ActiveFilter, FilterMode, toFlags } from '../shared/filters.utils';
|
||||
|
||||
interface MempoolBlocksData {
|
||||
blocks: number;
|
||||
@@ -58,6 +59,7 @@ export class DashboardComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
federationUtxosNumber$: Observable<number>;
|
||||
fullHistory$: Observable<any>;
|
||||
isLoad: boolean = true;
|
||||
filterSubscription: Subscription;
|
||||
mempoolInfoSubscription: Subscription;
|
||||
currencySubscription: Subscription;
|
||||
currency: string;
|
||||
@@ -68,13 +70,15 @@ export class DashboardComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
private lastReservesBlockUpdate: number = 0;
|
||||
|
||||
goggleResolution = 82;
|
||||
goggleCycle = [
|
||||
{ index: 0, name: 'All' },
|
||||
{ index: 1, name: 'Consolidations', flag: 0b00000010_00000000_00000000_00000000_00000000n },
|
||||
{ index: 2, name: 'Coinjoin', flag: 0b00000001_00000000_00000000_00000000_00000000n },
|
||||
{ index: 3, name: '💩', flag: 0b00000100_00000000_00000000_00000000n | 0b00000010_00000000_00000000_00000000n | 0b00000001_00000000_00000000_00000000n },
|
||||
goggleCycle: { index: number, name: string, mode: FilterMode, filters: string[] }[] = [
|
||||
{ index: 0, name: 'All', mode: 'and', filters: [] },
|
||||
{ index: 1, name: 'Consolidation', mode: 'and', filters: ['consolidation'] },
|
||||
{ index: 2, name: 'Coinjoin', mode: 'and', filters: ['coinjoin'] },
|
||||
{ index: 3, name: 'Data', mode: 'or', filters: ['inscription', 'fake_pubkey', 'op_return'] },
|
||||
];
|
||||
goggleIndex = 0; // Math.floor(Math.random() * this.goggleCycle.length);
|
||||
goggleFlags = 0n;
|
||||
goggleMode: FilterMode = 'and';
|
||||
goggleIndex = 0;
|
||||
|
||||
private destroy$ = new Subject();
|
||||
|
||||
@@ -90,6 +94,7 @@ export class DashboardComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.filterSubscription.unsubscribe();
|
||||
this.mempoolInfoSubscription.unsubscribe();
|
||||
this.currencySubscription.unsubscribe();
|
||||
this.websocketService.stopTrackRbfSummary();
|
||||
@@ -110,6 +115,30 @@ export class DashboardComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
map((indicators) => indicators.mempool !== undefined ? indicators.mempool : 100)
|
||||
);
|
||||
|
||||
this.filterSubscription = this.stateService.activeGoggles$.subscribe((active: ActiveFilter) => {
|
||||
const activeFilters = active.filters.sort().join(',');
|
||||
for (const goggle of this.goggleCycle) {
|
||||
if (goggle.mode === active.mode) {
|
||||
const goggleFilters = goggle.filters.sort().join(',');
|
||||
if (goggleFilters === activeFilters) {
|
||||
this.goggleIndex = goggle.index;
|
||||
this.goggleFlags = toFlags(goggle.filters);
|
||||
this.goggleMode = goggle.mode;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.goggleCycle.push({
|
||||
index: this.goggleCycle.length,
|
||||
name: 'Custom',
|
||||
mode: active.mode,
|
||||
filters: active.filters,
|
||||
});
|
||||
this.goggleIndex = this.goggleCycle.length - 1;
|
||||
this.goggleFlags = toFlags(active.filters);
|
||||
this.goggleMode = active.mode;
|
||||
});
|
||||
|
||||
this.mempoolInfoData$ = combineLatest([
|
||||
this.stateService.mempoolInfo$,
|
||||
this.stateService.vbytesPerSecond$
|
||||
@@ -392,6 +421,11 @@ export class DashboardComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
getArrayFromNumber(num: number): number[] {
|
||||
return Array.from({ length: num }, (_, i) => i + 1);
|
||||
}
|
||||
|
||||
setFilter(index): void {
|
||||
const selected = this.goggleCycle[index];
|
||||
this.stateService.activeGoggles$.next(selected);
|
||||
}
|
||||
|
||||
@HostListener('window:resize', ['$event'])
|
||||
onResize(): void {
|
||||
|
||||
@@ -9871,7 +9871,403 @@ export const restApiDocsData = [
|
||||
codeSampleBisq: emptyCodeSample,
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
category: "accelerator",
|
||||
fragment: "accelerator",
|
||||
title: "Accelerator",
|
||||
showConditions: [""],
|
||||
options: { officialOnly: true },
|
||||
},
|
||||
{
|
||||
options: { officialOnly: true },
|
||||
type: "endpoint",
|
||||
category: "accelerator",
|
||||
httpRequestMethod: "GET",
|
||||
fragment: "accelerator-deposit-history",
|
||||
title: "GET Deposit History",
|
||||
description: {
|
||||
default: "<p>Returns a list of deposits the user has made as prepayment for the accelerator service.</p>"
|
||||
},
|
||||
urlString: "/v1/services/accelerator/deposit-history",
|
||||
showConditions: [""],
|
||||
showJsExamples: showJsExamplesDefaultFalse,
|
||||
codeExample: {
|
||||
default: {
|
||||
codeTemplate: {
|
||||
curl: `/api/v1/services/accelerator/deposit-history`,
|
||||
commonJS: ``,
|
||||
esModule: ``
|
||||
},
|
||||
codeSampleMainnet: {
|
||||
esModule: [],
|
||||
commonJS: [],
|
||||
curl: [],
|
||||
headers: "api_key: stacksats",
|
||||
response: `[
|
||||
{
|
||||
"type": "Bitcoin",
|
||||
"invoiceId": "CCunucVyNw7jUiUz64mmHz",
|
||||
"amount": 10311031,
|
||||
"status": "pending",
|
||||
"date": 1706372653000,
|
||||
"link": "/payment/bitcoin/CCunucVyNw7jUiUz64mmHz"
|
||||
},
|
||||
{
|
||||
"type": "Bitcoin",
|
||||
"invoiceId": "SG1U27R9PdWi3gH3jB9tm9",
|
||||
"amount": 21000000,
|
||||
"status": "paid",
|
||||
"date": 1706372582000,
|
||||
"link": null
|
||||
},
|
||||
...
|
||||
]`,
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
options: { officialOnly: true },
|
||||
type: "endpoint",
|
||||
category: "accelerator",
|
||||
httpRequestMethod: "GET",
|
||||
fragment: "accelerator-balance",
|
||||
title: "GET Available Balance",
|
||||
description: {
|
||||
default: "<p>Returns the user's currently available balance, currently locked funds, and total fees paid so far.</p>"
|
||||
},
|
||||
urlString: "/v1/services/accelerator/balance",
|
||||
showConditions: [""],
|
||||
showJsExamples: showJsExamplesDefaultFalse,
|
||||
codeExample: {
|
||||
default: {
|
||||
codeTemplate: {
|
||||
curl: `/api/v1/services/accelerator/balance`,
|
||||
commonJS: ``,
|
||||
esModule: ``
|
||||
},
|
||||
codeSampleMainnet: {
|
||||
esModule: [],
|
||||
commonJS: [],
|
||||
curl: [],
|
||||
headers: "api_key: stacksats",
|
||||
response: `{
|
||||
"balance": 99900000,
|
||||
"hold": 101829,
|
||||
"feesPaid": 133721
|
||||
}`,
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
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",
|
||||
httpRequestMethod: "POST",
|
||||
fragment: "accelerator-accelerate",
|
||||
title: "POST Accelerate A Transaction",
|
||||
description: {
|
||||
default: "<p>Sends a request to accelerate a transaction.</p>"
|
||||
},
|
||||
urlString: "/v1/services/accelerator/accelerate",
|
||||
showConditions: [""],
|
||||
showJsExamples: showJsExamplesDefaultFalse,
|
||||
codeExample: {
|
||||
default: {
|
||||
codeTemplate: {
|
||||
curl: `%{1}" "[[hostname]][[baseNetworkUrl]]/api/v1/services/accelerator/accelerate`, //custom interpolation technique handled in replaceCurlPlaceholder()
|
||||
commonJS: ``,
|
||||
esModule: ``
|
||||
},
|
||||
codeSampleMainnet: {
|
||||
esModule: [],
|
||||
commonJS: [],
|
||||
curl: ["txInput=ee13ebb99632377c15c94980357f674d285ac413452050031ea6dcd3e9b2dc29&userBid=21000000"],
|
||||
headers: "api_key: stacksats",
|
||||
response: `HTTP/1.1 200 OK`,
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
options: { officialOnly: true },
|
||||
type: "endpoint",
|
||||
category: "accelerator",
|
||||
httpRequestMethod: "GET",
|
||||
fragment: "accelerator-history",
|
||||
title: "GET Private 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>"
|
||||
},
|
||||
urlString: "/v1/services/accelerator/history?status=:status&details=:details",
|
||||
showConditions: [""],
|
||||
showJsExamples: showJsExamplesDefaultFalse,
|
||||
codeExample: {
|
||||
default: {
|
||||
codeTemplate: {
|
||||
curl: `/api/v1/services/accelerator/history?status=all&details=true`,
|
||||
commonJS: ``,
|
||||
esModule: ``
|
||||
},
|
||||
codeSampleMainnet: {
|
||||
esModule: [],
|
||||
commonJS: [],
|
||||
curl: [],
|
||||
headers: "api_key: stacksats",
|
||||
response: `[
|
||||
{
|
||||
"id": 89,
|
||||
"user_id": 1,
|
||||
"txid": "ae2639469ec000ed1d14e2550cbb01794e1cd288a00cdc7cce18398ba3cc2ffe",
|
||||
"status": "failed",
|
||||
"estimated_fee": 247,
|
||||
"fee_paid": 0,
|
||||
"added": 1706378712,
|
||||
"last_updated": 1706378712,
|
||||
"confirmations": 4,
|
||||
"base_fee": 0,
|
||||
"vsize_fee": 0,
|
||||
"max_bid": 7000,
|
||||
"effective_vsize": 135,
|
||||
"effective_fee": 3128,
|
||||
"history": [
|
||||
{
|
||||
"event": "user-requested-acceleration",
|
||||
"timestamp": 1706378712
|
||||
},
|
||||
{
|
||||
"event": "accepted_test-api-key",
|
||||
"timestamp": 1706378712
|
||||
},
|
||||
{
|
||||
"event": "failed-at-block-827672",
|
||||
"timestamp": 1706380261
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 88,
|
||||
"user_id": 1,
|
||||
"txid": "c5840e89173331760e959a190b24e2a289121277ed7f8a095fe289b37cee9fde",
|
||||
"status": "completed",
|
||||
"estimated_fee": 223,
|
||||
"fee_paid": 140019,
|
||||
"added": 1706378704,
|
||||
"last_updated": 1706380231,
|
||||
"confirmations": 6,
|
||||
"base_fee": 40000,
|
||||
"vsize_fee": 100000,
|
||||
"max_bid": 14000,
|
||||
"effective_vsize": 135,
|
||||
"effective_fee": 3152,
|
||||
"history": [
|
||||
{
|
||||
"event": "user-requested-acceleration",
|
||||
"timestamp": 1706378704
|
||||
},
|
||||
{
|
||||
"event": "accepted_test-api-key",
|
||||
"timestamp": 1706378704
|
||||
},
|
||||
{
|
||||
"event": "complete-at-block-827670",
|
||||
"timestamp": 1706380231
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 87,
|
||||
"user_id": 1,
|
||||
"txid": "178b5b9b310f0d667d7ea563a2cdcc17bc8cd15261b58b1653860a724ca83458",
|
||||
"status": "completed",
|
||||
"estimated_fee": 115,
|
||||
"fee_paid": 90062,
|
||||
"added": 1706378684,
|
||||
"last_updated": 1706380231,
|
||||
"confirmations": 6,
|
||||
"base_fee": 40000,
|
||||
"vsize_fee": 50000,
|
||||
"max_bid": 14000,
|
||||
"effective_vsize": 135,
|
||||
"effective_fee": 3260,
|
||||
"history": [
|
||||
{
|
||||
"event": "user-requested-acceleration",
|
||||
"timestamp": 1706378684
|
||||
},
|
||||
{
|
||||
"event": "accepted_test-api-key",
|
||||
"timestamp": 1706378684
|
||||
},
|
||||
{
|
||||
"event": "complete-at-block-827670",
|
||||
"timestamp": 1706380231
|
||||
}
|
||||
]
|
||||
}
|
||||
]`,
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]`,
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
export const faqData = [
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div *ngFor="let item of tabData">
|
||||
<p *ngIf="( item.type === 'category' ) && ( item.showConditions.indexOf(network.val) > -1 )">{{ item.title }}</p>
|
||||
<p *ngIf="( item.type === 'category' ) && ( item.showConditions.indexOf(network.val) > -1 ) && ( !item.hasOwnProperty('options') || ( item.hasOwnProperty('options') && item.options.hasOwnProperty('officialOnly') && item.options.officialOnly && officialMempoolInstance ))">{{ item.title }}</p>
|
||||
<a *ngIf="( item.type !== 'category' ) && ( item.showConditions.indexOf(network.val) > -1 ) && ( !item.hasOwnProperty('options') || ( item.hasOwnProperty('options') && item.options.hasOwnProperty('officialOnly') && item.options.officialOnly && officialMempoolInstance ) || ( item.hasOwnProperty('options') && item.options.hasOwnProperty('auditOnly') && item.options.auditOnly && auditEnabled ) )" [routerLink]="['./']" fragment="{{ item.fragment }}" (click)="navLinkClick($event)">{{ item.title }}</a>
|
||||
</div>
|
||||
|
||||
@@ -43,54 +43,56 @@
|
||||
<p class="doc-welcome-note api-note" *ngIf="officialMempoolInstance">Note that we enforce rate limits. If you exceed these limits, you will get an HTTP 429 error. If you repeatedly exceed the limits, you may be banned from accessing the service altogether. Consider an <a href="https://mempool.space/enterprise">enterprise sponsorship</a> if you need higher API limits.</p>
|
||||
|
||||
<div class="doc-item-container" *ngFor="let item of restDocs">
|
||||
<h3 *ngIf="( item.type === 'category' ) && ( item.showConditions.indexOf(network.val) > -1 )">{{ item.title }}</h3>
|
||||
<div *ngIf="( item.type !== 'category' ) && ( item.showConditions.indexOf(network.val) > -1 )" class="endpoint-container" id="{{ item.fragment }}">
|
||||
<a id="{{ item.fragment + '-tab-header' }}" class="section-header" (click)="anchorLinkClick( $event )" [routerLink]="['./']" fragment="{{ item.fragment }}">{{ item.title }} <span>{{ item.category }}</span></a>
|
||||
<div class="endpoint-content">
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<ng-container *ngIf="item.httpRequestMethod === 'GET' && network.val === 'bisq' && item.codeExample.hasOwnProperty('bisq');else liquid_link_example" #bisq_link_example>
|
||||
<a [href]="wrapUrl(network.val, item.codeExample.bisq)" target="_blank" rel="nofollow">{{ item.httpRequestMethod }} {{ baseNetworkUrl }}/api{{ item.urlString }}</a>
|
||||
<div *ngIf="!item.hasOwnProperty('options') || ( item.hasOwnProperty('options') && item.options.hasOwnProperty('officialOnly') && item.options.officialOnly && officialMempoolInstance )">
|
||||
<h3 *ngIf="( item.type === 'category' ) && ( item.showConditions.indexOf(network.val) > -1 )">{{ item.title }}</h3>
|
||||
<div *ngIf="( item.type !== 'category' ) && ( item.showConditions.indexOf(network.val) > -1 )" class="endpoint-container" id="{{ item.fragment }}">
|
||||
<a id="{{ item.fragment + '-tab-header' }}" class="section-header" (click)="anchorLinkClick( $event )" [routerLink]="['./']" fragment="{{ item.fragment }}">{{ item.title }} <span>{{ item.category }}</span></a>
|
||||
<div class="endpoint-content">
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<ng-container *ngIf="item.httpRequestMethod === 'GET' && network.val === 'bisq' && item.codeExample.hasOwnProperty('bisq');else liquid_link_example" #bisq_link_example>
|
||||
<a [href]="wrapUrl(network.val, item.codeExample.bisq)" target="_blank" rel="nofollow">{{ item.httpRequestMethod }} {{ baseNetworkUrl }}/api{{ item.urlString }}</a>
|
||||
</ng-container>
|
||||
<ng-template #liquid_link_example>
|
||||
<ng-container *ngIf="item.httpRequestMethod === 'GET' && network.val === 'liquid' && item.codeExample.hasOwnProperty('liquid');else default_link_example">
|
||||
<a [href]="wrapUrl(network.val, item.codeExample.liquid)" target="_blank" rel="nofollow" *ngIf="item.fragment !== 'get-cpfp'">{{ item.httpRequestMethod }} {{ baseNetworkUrl }}/api{{ item.urlString }}</a>
|
||||
<p *ngIf="item.fragment === 'get-cpfp'">{{ item.httpRequestMethod }} {{ baseNetworkUrl }}/api{{ item.urlString }}</p>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
<ng-template #default_link_example>
|
||||
<ng-container *ngIf="item.httpRequestMethod === 'GET'">
|
||||
<a [href]="wrapUrl(network.val, item.codeExample.default)" target="_blank" rel="nofollow" *ngIf="item.fragment !== 'get-cpfp'">{{ item.httpRequestMethod }} {{ baseNetworkUrl }}/api{{ item.urlString }}</a>
|
||||
<p *ngIf="item.fragment === 'get-cpfp'">{{ item.httpRequestMethod }} {{ baseNetworkUrl }}/api{{ item.urlString }}</p>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
<div *ngIf="item.httpRequestMethod === 'POST'">{{ item.httpRequestMethod }} {{ item.urlString }}</div>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<ng-container *ngIf="network.val === 'bisq' && item.description.hasOwnProperty('bisq');else liquid_description" #bisq_description>
|
||||
<div [innerHTML]="item.description.bisq" i18n></div>
|
||||
</ng-container>
|
||||
<ng-template #liquid_description>
|
||||
<ng-container *ngIf="network.val === 'liquid' && item.description.hasOwnProperty('liquid');else default_description">
|
||||
<div [innerHTML]="item.description.liquid" i18n></div>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
<ng-template #default_description>
|
||||
<div [innerHTML]="item.description.default" i18n></div>
|
||||
</ng-template>
|
||||
</div>
|
||||
<ng-container *ngIf="network.val === 'bisq' && item.codeExample.hasOwnProperty('bisq');else liquid_code_example" #bisq_code_example>
|
||||
<app-code-template [hostname]="hostname" [baseNetworkUrl]="baseNetworkUrl" [method]="item.httpRequestMethod" [code]="item.codeExample.bisq" [network]="network.val" [showCodeExample]="item.showJsExamples"></app-code-template>
|
||||
</ng-container>
|
||||
<ng-template #liquid_link_example>
|
||||
<ng-container *ngIf="item.httpRequestMethod === 'GET' && network.val === 'liquid' && item.codeExample.hasOwnProperty('liquid');else default_link_example">
|
||||
<a [href]="wrapUrl(network.val, item.codeExample.liquid)" target="_blank" rel="nofollow" *ngIf="item.fragment !== 'get-cpfp'">{{ item.httpRequestMethod }} {{ baseNetworkUrl }}/api{{ item.urlString }}</a>
|
||||
<p *ngIf="item.fragment === 'get-cpfp'">{{ item.httpRequestMethod }} {{ baseNetworkUrl }}/api{{ item.urlString }}</p>
|
||||
<ng-template #liquid_code_example>
|
||||
<ng-container *ngIf="network.val === 'liquid' && item.codeExample.hasOwnProperty('liquid');else default_code_example">
|
||||
<app-code-template [hostname]="hostname" [baseNetworkUrl]="baseNetworkUrl" [method]="item.httpRequestMethod" [code]="item.codeExample.liquid" [network]="network.val" [showCodeExample]="item.showJsExamples"></app-code-template>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
<ng-template #default_link_example>
|
||||
<ng-container *ngIf="item.httpRequestMethod === 'GET'">
|
||||
<a [href]="wrapUrl(network.val, item.codeExample.default)" target="_blank" rel="nofollow" *ngIf="item.fragment !== 'get-cpfp'">{{ item.httpRequestMethod }} {{ baseNetworkUrl }}/api{{ item.urlString }}</a>
|
||||
<p *ngIf="item.fragment === 'get-cpfp'">{{ item.httpRequestMethod }} {{ baseNetworkUrl }}/api{{ item.urlString }}</p>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
<div *ngIf="item.httpRequestMethod === 'POST'">{{ item.httpRequestMethod }} {{ item.urlString }}</div>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<ng-container *ngIf="network.val === 'bisq' && item.description.hasOwnProperty('bisq');else liquid_description" #bisq_description>
|
||||
<div [innerHTML]="item.description.bisq" i18n></div>
|
||||
</ng-container>
|
||||
<ng-template #liquid_description>
|
||||
<ng-container *ngIf="network.val === 'liquid' && item.description.hasOwnProperty('liquid');else default_description">
|
||||
<div [innerHTML]="item.description.liquid" i18n></div>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
<ng-template #default_description>
|
||||
<div [innerHTML]="item.description.default" i18n></div>
|
||||
<ng-template #default_code_example>
|
||||
<app-code-template [hostname]="hostname" [baseNetworkUrl]="baseNetworkUrl" [method]="item.httpRequestMethod" [code]="item.codeExample.default" [network]="network.val" [showCodeExample]="item.showJsExamples"></app-code-template>
|
||||
</ng-template>
|
||||
</div>
|
||||
<ng-container *ngIf="network.val === 'bisq' && item.codeExample.hasOwnProperty('bisq');else liquid_code_example" #bisq_code_example>
|
||||
<app-code-template [hostname]="hostname" [baseNetworkUrl]="baseNetworkUrl" [method]="item.httpRequestMethod" [code]="item.codeExample.bisq" [network]="network.val" [showCodeExample]="item.showJsExamples"></app-code-template>
|
||||
</ng-container>
|
||||
<ng-template #liquid_code_example>
|
||||
<ng-container *ngIf="network.val === 'liquid' && item.codeExample.hasOwnProperty('liquid');else default_code_example">
|
||||
<app-code-template [hostname]="hostname" [baseNetworkUrl]="baseNetworkUrl" [method]="item.httpRequestMethod" [code]="item.codeExample.liquid" [network]="network.val" [showCodeExample]="item.showJsExamples"></app-code-template>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
<ng-template #default_code_example>
|
||||
<app-code-template [hostname]="hostname" [baseNetworkUrl]="baseNetworkUrl" [method]="item.httpRequestMethod" [code]="item.codeExample.default" [network]="network.val" [showCodeExample]="item.showJsExamples"></app-code-template>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -311,27 +311,29 @@ yarn add @mempool/liquid.js`;
|
||||
text = text.replace('%{' + indexNumber + '}', textReplace);
|
||||
}
|
||||
|
||||
const headersString = code.headers ? ` -H "${code.headers}"` : ``;
|
||||
|
||||
if (this.env.BASE_MODULE === 'mempool') {
|
||||
if (this.network === 'main' || this.network === '') {
|
||||
if (this.method === 'POST') {
|
||||
return `curl -X POST -sSLd "${text}"`;
|
||||
return `curl${headersString} -X POST -sSLd "${text}"`;
|
||||
}
|
||||
return `curl -sSL "${this.hostname}${text}"`;
|
||||
return `curl${headersString} -sSL "${this.hostname}${text}"`;
|
||||
}
|
||||
if (this.method === 'POST') {
|
||||
return `curl -X POST -sSLd "${text}"`;
|
||||
return `curl${headersString} -X POST -sSLd "${text}"`;
|
||||
}
|
||||
return `curl -sSL "${this.hostname}/${this.network}${text}"`;
|
||||
return `curl${headersString} -sSL "${this.hostname}/${this.network}${text}"`;
|
||||
} else if (this.env.BASE_MODULE === 'liquid') {
|
||||
if (this.method === 'POST') {
|
||||
if (this.network !== 'liquid') {
|
||||
text = text.replace('/api', `/${this.network}/api`);
|
||||
}
|
||||
return `curl -X POST -sSLd "${text}"`;
|
||||
return `curl${headersString} -X POST -sSLd "${text}"`;
|
||||
}
|
||||
return ( this.network === 'liquid' ? `curl -sSL "${this.hostname}${text}"` : `curl -sSL "${this.hostname}/${this.network}${text}"` );
|
||||
return ( this.network === 'liquid' ? `curl${headersString} -sSL "${this.hostname}${text}"` : `curl${headersString} -sSL "${this.hostname}/${this.network}${text}"` );
|
||||
} else {
|
||||
return `curl -sSL "${this.hostname}${text}"`;
|
||||
return `curl${headersString} -sSL "${this.hostname}${text}"`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -70,9 +70,15 @@ export interface MempoolBlockWithTransactions extends MempoolBlock {
|
||||
}
|
||||
|
||||
export interface MempoolBlockDelta {
|
||||
added: TransactionStripped[],
|
||||
removed: string[],
|
||||
changed?: { txid: string, rate: number | undefined, acc: boolean | undefined }[];
|
||||
added: TransactionStripped[];
|
||||
removed: string[];
|
||||
changed: { txid: string, rate: number, flags: number, acc: boolean }[];
|
||||
}
|
||||
|
||||
export interface MempoolBlockDeltaCompressed {
|
||||
added: TransactionCompressed[];
|
||||
removed: string[];
|
||||
changed: MempoolDeltaChange[];
|
||||
}
|
||||
|
||||
export interface MempoolInfo {
|
||||
@@ -97,6 +103,11 @@ export interface TransactionStripped {
|
||||
context?: 'projected' | 'actual';
|
||||
}
|
||||
|
||||
// [txid, fee, vsize, value, rate, flags, acceleration?]
|
||||
export type TransactionCompressed = [string, number, number, number, number, number, 1?];
|
||||
// [txid, rate, flags, acceleration?]
|
||||
export type MempoolDeltaChange = [string, number, number, (1|0)];
|
||||
|
||||
export interface IBackendInfo {
|
||||
hostname?: string;
|
||||
gitCommit: string;
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { Router, NavigationStart } from '@angular/router';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||
import { StateService } from './state.service';
|
||||
import { StorageService } from './storage.service';
|
||||
import { MenuGroup } from '../interfaces/services.interface';
|
||||
import { Observable, of, ReplaySubject, tap, catchError, share } from 'rxjs';
|
||||
import { Observable, of, ReplaySubject, tap, catchError, share, filter, switchMap } from 'rxjs';
|
||||
import { IBackendInfo } from '../interfaces/websocket.interface';
|
||||
import { Acceleration, AccelerationHistoryParams } from '../interfaces/node-api.interface';
|
||||
|
||||
@@ -30,16 +31,20 @@ const SERVICES_API_PREFIX = `/api/v1/services`;
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ServicesApiServices {
|
||||
private apiBaseUrl: string; // base URL is protocol, hostname, and port
|
||||
private apiBasePath: string; // network path is /testnet, etc. or '' for mainnet
|
||||
apiBaseUrl: string; // base URL is protocol, hostname, and port
|
||||
apiBasePath: string; // network path is /testnet, etc. or '' for mainnet
|
||||
|
||||
userSubject$ = new ReplaySubject<IUser | null>(1);
|
||||
currentAuth = null;
|
||||
|
||||
constructor(
|
||||
private httpClient: HttpClient,
|
||||
private stateService: StateService,
|
||||
private storageService: StorageService
|
||||
private storageService: StorageService,
|
||||
private router: Router,
|
||||
) {
|
||||
this.currentAuth = localStorage.getItem('auth');
|
||||
|
||||
this.apiBaseUrl = ''; // use relative URL by default
|
||||
if (!stateService.isBrowser) { // except when inside AU SSR process
|
||||
this.apiBaseUrl = this.stateService.env.NGINX_PROTOCOL + '://' + this.stateService.env.NGINX_HOSTNAME + ':' + this.stateService.env.NGINX_PORT;
|
||||
@@ -59,6 +64,10 @@ export class ServicesApiServices {
|
||||
}
|
||||
|
||||
this.getUserInfo$().subscribe();
|
||||
this.router.events.pipe(
|
||||
filter((event) => event instanceof NavigationStart && this.currentAuth !== localStorage.getItem('auth')),
|
||||
switchMap(() => this.getUserInfo$()),
|
||||
).subscribe();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Inject, Injectable, PLATFORM_ID, LOCALE_ID } from '@angular/core';
|
||||
import { ReplaySubject, BehaviorSubject, Subject, fromEvent, Observable, merge } from 'rxjs';
|
||||
import { Transaction } from '../interfaces/electrs.interface';
|
||||
import { IBackendInfo, MempoolBlock, MempoolBlockDelta, MempoolInfo, Recommendedfees, ReplacedTransaction, ReplacementInfo, TransactionStripped } from '../interfaces/websocket.interface';
|
||||
import { IBackendInfo, MempoolBlock, MempoolBlockDelta, MempoolInfo, Recommendedfees, ReplacedTransaction, ReplacementInfo, TransactionCompressed, TransactionStripped } from '../interfaces/websocket.interface';
|
||||
import { BlockExtended, CpfpInfo, DifficultyAdjustment, MempoolPosition, OptimizedMempoolStats, RbfTree } from '../interfaces/node-api.interface';
|
||||
import { Router, NavigationStart } from '@angular/router';
|
||||
import { isPlatformBrowser } from '@angular/common';
|
||||
@@ -9,6 +9,7 @@ import { filter, map, scan, shareReplay } from 'rxjs/operators';
|
||||
import { StorageService } from './storage.service';
|
||||
import { hasTouchScreen } from '../shared/pipes/bytes-pipe/utils';
|
||||
import { ApiService } from './api.service';
|
||||
import { ActiveFilter } from '../shared/filters.utils';
|
||||
|
||||
export interface MarkBlockState {
|
||||
blockHeight?: number;
|
||||
@@ -150,7 +151,7 @@ export class StateService {
|
||||
searchFocus$: Subject<boolean> = new Subject<boolean>();
|
||||
menuOpen$: BehaviorSubject<boolean> = new BehaviorSubject(false);
|
||||
|
||||
activeGoggles$: BehaviorSubject<string[]> = new BehaviorSubject([]);
|
||||
activeGoggles$: BehaviorSubject<ActiveFilter> = new BehaviorSubject({ mode: 'and', filters: [] });
|
||||
|
||||
constructor(
|
||||
@Inject(PLATFORM_ID) private platformId: any,
|
||||
|
||||
@@ -8,6 +8,7 @@ import { ApiService } from './api.service';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { TransferState, makeStateKey } from '@angular/platform-browser';
|
||||
import { CacheService } from './cache.service';
|
||||
import { uncompressDeltaChange, uncompressTx } from '../shared/common.utils';
|
||||
|
||||
const OFFLINE_RETRY_AFTER_MS = 2000;
|
||||
const OFFLINE_PING_CHECK_AFTER_MS = 30000;
|
||||
@@ -382,9 +383,9 @@ export class WebsocketService {
|
||||
if (response['projected-block-transactions']) {
|
||||
if (response['projected-block-transactions'].index == this.trackingMempoolBlock) {
|
||||
if (response['projected-block-transactions'].blockTransactions) {
|
||||
this.stateService.mempoolBlockTransactions$.next(response['projected-block-transactions'].blockTransactions);
|
||||
this.stateService.mempoolBlockTransactions$.next(response['projected-block-transactions'].blockTransactions.map(uncompressTx));
|
||||
} else if (response['projected-block-transactions'].delta) {
|
||||
this.stateService.mempoolBlockDelta$.next(response['projected-block-transactions'].delta);
|
||||
this.stateService.mempoolBlockDelta$.next(uncompressDeltaChange(response['projected-block-transactions'].delta));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { MempoolBlockDelta, MempoolBlockDeltaCompressed, MempoolDeltaChange, TransactionCompressed, TransactionStripped } from "../interfaces/websocket.interface";
|
||||
|
||||
export function isMobile(): boolean {
|
||||
return (window.innerWidth <= 767.98);
|
||||
}
|
||||
@@ -152,4 +154,29 @@ export function seoDescriptionNetwork(network: string): string {
|
||||
return ' ' + network.charAt(0).toUpperCase() + network.slice(1);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
export function uncompressTx(tx: TransactionCompressed): TransactionStripped {
|
||||
return {
|
||||
txid: tx[0],
|
||||
fee: tx[1],
|
||||
vsize: tx[2],
|
||||
value: tx[3],
|
||||
rate: tx[4],
|
||||
flags: tx[5],
|
||||
acc: !!tx[6],
|
||||
};
|
||||
}
|
||||
|
||||
export function uncompressDeltaChange(delta: MempoolBlockDeltaCompressed): MempoolBlockDelta {
|
||||
return {
|
||||
added: delta.added.map(uncompressTx),
|
||||
removed: delta.removed,
|
||||
changed: delta.changed.map(tx => ({
|
||||
txid: tx[0],
|
||||
rate: tx[1],
|
||||
flags: tx[2],
|
||||
acc: !!tx[3],
|
||||
}))
|
||||
};
|
||||
}
|
||||
@@ -7,6 +7,13 @@ export interface Filter {
|
||||
important?: boolean,
|
||||
}
|
||||
|
||||
export type FilterMode = 'and' | 'or';
|
||||
|
||||
export interface ActiveFilter {
|
||||
mode: FilterMode,
|
||||
filters: string[],
|
||||
}
|
||||
|
||||
// binary flags for transaction classification
|
||||
export const TransactionFlags = {
|
||||
// features
|
||||
@@ -43,6 +50,14 @@ export const TransactionFlags = {
|
||||
sighash_acp: 0b00010000_00000000_00000000_00000000_00000000_00000000n,
|
||||
};
|
||||
|
||||
export function toFlags(filters: string[]): bigint {
|
||||
let flag = 0n;
|
||||
for (const filter of filters) {
|
||||
flag |= TransactionFlags[filter];
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
export const TransactionFilters: { [key: string]: Filter } = {
|
||||
/* features */
|
||||
rbf: { key: 'rbf', label: 'RBF enabled', flag: TransactionFlags.rbf, toggle: 'rbf', important: true },
|
||||
|
||||
Reference in New Issue
Block a user