Accelerator fee diagram concept
This commit is contained in:
parent
c44276027c
commit
c753a8e92a
@ -0,0 +1,21 @@
|
|||||||
|
<div class="fee-graph" *ngIf="tx && estimate">
|
||||||
|
<div class="column">
|
||||||
|
<ng-container *ngFor="let bar of bars">
|
||||||
|
<div class="bar {{ bar.class }}" [class.active]="bar.active" [style]="bar.style" (click)="onClick($event, bar);">
|
||||||
|
<div class="fill"></div>
|
||||||
|
<div class="line">
|
||||||
|
<p class="fee-rate">
|
||||||
|
<span class="label">{{ bar.label }}</span>
|
||||||
|
<span class="rate">
|
||||||
|
<app-fee-rate [fee]="bar.rate"></app-fee-rate>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<span class="fee">{{ bar.class === 'tx' ? '' : '+' }} {{ bar.fee | number }} <span class="symbol" i18n="shared.sat|sat">sat</span></span>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
<div class="vsize">
|
||||||
|
<span [innerHTML]="'‎' + (estimate.txSummary.effectiveVsize | vbytes: 2)"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,147 @@
|
|||||||
|
.fee-graph {
|
||||||
|
height: 100%;
|
||||||
|
width: 20%;
|
||||||
|
min-width: 100px;
|
||||||
|
max-width: 150px;
|
||||||
|
max-height: 100vh;
|
||||||
|
margin-left: 4em;
|
||||||
|
margin-right: 1.5em;
|
||||||
|
padding-bottom: 63px;
|
||||||
|
|
||||||
|
.column {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
background: #181b2d;
|
||||||
|
|
||||||
|
.bar {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.fill {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
opacity: 0.75;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fee {
|
||||||
|
position: absolute;
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.line {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
left: -4.5em;
|
||||||
|
border-top: dashed white 1.5px;
|
||||||
|
|
||||||
|
.fee-rate {
|
||||||
|
width: 100%;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0.2em;
|
||||||
|
font-size: 0.8em;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
.label {
|
||||||
|
margin-right: .2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rate .symbol {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.tx {
|
||||||
|
.fill {
|
||||||
|
background: #105fb0;
|
||||||
|
}
|
||||||
|
.line {
|
||||||
|
.fee-rate {
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.fee {
|
||||||
|
opacity: 1;
|
||||||
|
z-index: 11;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.target {
|
||||||
|
.fill {
|
||||||
|
background: #3bcc49;
|
||||||
|
}
|
||||||
|
.fee {
|
||||||
|
opacity: 1;
|
||||||
|
z-index: 11;
|
||||||
|
}
|
||||||
|
.line .fee-rate {
|
||||||
|
bottom: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.max {
|
||||||
|
cursor: pointer;
|
||||||
|
.line .fee-rate {
|
||||||
|
.label {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
bottom: 2px;
|
||||||
|
}
|
||||||
|
&.active, &:hover {
|
||||||
|
.fill {
|
||||||
|
background: #653b9c;
|
||||||
|
}
|
||||||
|
.line {
|
||||||
|
.fee-rate .label {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.fill {
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
.line {
|
||||||
|
z-index: 11;
|
||||||
|
}
|
||||||
|
.fee {
|
||||||
|
opacity: 1;
|
||||||
|
z-index: 12;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover > .bar:not(:hover) {
|
||||||
|
&.target, &.max {
|
||||||
|
.fee {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
.line .fee-rate .label {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsize {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,96 @@
|
|||||||
|
import { Component, OnInit, Input, Output, OnChanges, EventEmitter, HostListener, Inject, LOCALE_ID } from '@angular/core';
|
||||||
|
import { StateService } from '../../services/state.service';
|
||||||
|
import { Outspend, Transaction, Vin, Vout } from '../../interfaces/electrs.interface';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { ReplaySubject, merge, Subscription, of } from 'rxjs';
|
||||||
|
import { tap, switchMap } from 'rxjs/operators';
|
||||||
|
import { ApiService } from '../../services/api.service';
|
||||||
|
import { AccelerationEstimate, RateOption } from './accelerate-preview.component';
|
||||||
|
|
||||||
|
interface GraphBar {
|
||||||
|
rate: number;
|
||||||
|
style: any;
|
||||||
|
class: 'tx' | 'target' | 'max';
|
||||||
|
label: string;
|
||||||
|
active?: boolean;
|
||||||
|
rateIndex?: number;
|
||||||
|
fee?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-accelerate-fee-graph',
|
||||||
|
templateUrl: './accelerate-fee-graph.component.html',
|
||||||
|
styleUrls: ['./accelerate-fee-graph.component.scss'],
|
||||||
|
})
|
||||||
|
export class AccelerateFeeGraphComponent implements OnInit, OnChanges {
|
||||||
|
@Input() tx: Transaction;
|
||||||
|
@Input() estimate: AccelerationEstimate;
|
||||||
|
@Input() maxRateOptions: RateOption[] = [];
|
||||||
|
@Input() maxRateIndex: number = 0;
|
||||||
|
@Output() setUserBid = new EventEmitter<{ fee: number, index: number }>();
|
||||||
|
|
||||||
|
bars: GraphBar[] = [];
|
||||||
|
tooltipPosition = { x: 0, y: 0 };
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.initGraph();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnChanges(): void {
|
||||||
|
this.initGraph();
|
||||||
|
}
|
||||||
|
|
||||||
|
initGraph(): void {
|
||||||
|
if (!this.tx || !this.estimate) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const maxRate = Math.max(...this.maxRateOptions.map(option => option.rate));
|
||||||
|
const baseRate = this.estimate.txSummary.effectiveFee / this.estimate.txSummary.effectiveVsize;
|
||||||
|
const baseHeight = baseRate / maxRate;
|
||||||
|
const bars: GraphBar[] = this.maxRateOptions.slice().reverse().map(option => {
|
||||||
|
return {
|
||||||
|
rate: option.rate,
|
||||||
|
style: this.getStyle(option.rate, maxRate, baseHeight),
|
||||||
|
class: 'max',
|
||||||
|
label: 'max',
|
||||||
|
active: option.index === this.maxRateIndex,
|
||||||
|
rateIndex: option.index,
|
||||||
|
fee: option.fee,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
bars.push({
|
||||||
|
rate: this.estimate.targetFeeRate,
|
||||||
|
style: this.getStyle(this.estimate.targetFeeRate, maxRate, baseHeight),
|
||||||
|
class: 'target',
|
||||||
|
label: 'expected',
|
||||||
|
fee: this.estimate.nextBlockFee - this.estimate.txSummary.effectiveFee
|
||||||
|
});
|
||||||
|
bars.push({
|
||||||
|
rate: baseRate,
|
||||||
|
style: this.getStyle(baseRate, maxRate, 0),
|
||||||
|
class: 'tx',
|
||||||
|
label: 'paid',
|
||||||
|
fee: this.estimate.txSummary.effectiveFee,
|
||||||
|
});
|
||||||
|
this.bars = bars;
|
||||||
|
}
|
||||||
|
|
||||||
|
getStyle(rate, maxRate, base) {
|
||||||
|
const top = (rate / maxRate);
|
||||||
|
return {
|
||||||
|
height: `${(top - base) * 100}%`,
|
||||||
|
bottom: base ? `${base * 100}%` : '0',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onClick(event, bar): void {
|
||||||
|
if (bar.rateIndex != null) {
|
||||||
|
this.setUserBid.emit({ fee: bar.fee, index: bar.rateIndex });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@HostListener('pointermove', ['$event'])
|
||||||
|
onPointerMove(event) {
|
||||||
|
this.tooltipPosition = { x: event.offsetX, y: event.offsetY };
|
||||||
|
}
|
||||||
|
}
|
@ -20,7 +20,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ng-container *ngIf="estimate">
|
<div class="accelerate-cols">
|
||||||
|
<app-accelerate-fee-graph
|
||||||
|
[tx]="tx"
|
||||||
|
[estimate]="estimate"
|
||||||
|
[maxRateOptions]="maxRateOptions"
|
||||||
|
[maxRateIndex]="selectFeeRateIndex"
|
||||||
|
(setUserBid)="setUserBid($event)"
|
||||||
|
></app-accelerate-fee-graph>
|
||||||
|
|
||||||
|
<ng-container *ngIf="estimate">
|
||||||
<div [class]="{estimateDisabled: error}">
|
<div [class]="{estimateDisabled: error}">
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
@ -97,10 +106,11 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="fee-card">
|
<div class="fee-card">
|
||||||
<div class="d-flex mb-2">
|
<div class="d-flex mb-2">
|
||||||
<button type="button" class="btn btn-primary flex-grow-1 btn-border btn-sm feerate" [class]="{active: selectFeeRateIndex === 1}" (click)="setUserBid(2, 1)">{{ (estimate.txSummary.effectiveFee + minExtraCost * 2) / estimate.txSummary.effectiveVsize | number : '1.0-0' }} <span class="symbol" i18n="shared.sat-vbyte|sat/vB">sat/vB</span></button>
|
<ng-container *ngFor="let option of maxRateOptions">
|
||||||
<button type="button" class="btn btn-primary flex-grow-1 btn-border btn-sm feerate" [class]="{active: selectFeeRateIndex === 2}" (click)="setUserBid(5, 2)">{{ (estimate.txSummary.effectiveFee + minExtraCost * 5) / estimate.txSummary.effectiveVsize | number : '1.0-0' }} <span class="symbol" i18n="shared.sat-vbyte|sat/vB">sat/vB</span></button>
|
<button type="button" class="btn btn-primary flex-grow-1 btn-border btn-sm feerate" [class]="{active: selectFeeRateIndex === option.index}" (click)="setUserBid(option)">
|
||||||
<button type="button" class="btn btn-primary flex-grow-1 btn-border btn-sm feerate" [class]="{active: selectFeeRateIndex === 3}" (click)="setUserBid(10, 3)">{{ (estimate.txSummary.effectiveFee + minExtraCost * 10) / estimate.txSummary.effectiveVsize | number : '1.0-0' }} <span class="symbol" i18n="shared.sat-vbyte|sat/vB">sat/vB</span></button>
|
{{ option.rate | number : '1.0-0' }} <span class="symbol" i18n="shared.sat-vbyte|sat/vB">sat/vB</span>
|
||||||
<button type="button" class="btn btn-primary flex-grow-1 btn-border btn-sm feerate" [class]="{active: selectFeeRateIndex === 4}" (click)="setUserBid(20, 4)">{{ (estimate.txSummary.effectiveFee + minExtraCost * 20) / estimate.txSummary.effectiveVsize | number : '1.0-0' }} <span class="symbol" i18n="shared.sat-vbyte|sat/vB">sat/vB</span></button>
|
</button>
|
||||||
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -228,4 +238,5 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
</div>
|
@ -25,3 +25,10 @@
|
|||||||
text-wrap: wrap;
|
text-wrap: wrap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.accelerate-cols {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: stretch;
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
@ -2,6 +2,7 @@ import { Component, OnInit, Input, OnDestroy, OnChanges, SimpleChanges } from '@
|
|||||||
import { ApiService } from '../../services/api.service';
|
import { ApiService } from '../../services/api.service';
|
||||||
import { Subscription, catchError, of, tap } from 'rxjs';
|
import { Subscription, catchError, of, tap } from 'rxjs';
|
||||||
import { StorageService } from '../../services/storage.service';
|
import { StorageService } from '../../services/storage.service';
|
||||||
|
import { Transaction } from '../../interfaces/electrs.interface';
|
||||||
|
|
||||||
export type AccelerationEstimate = {
|
export type AccelerationEstimate = {
|
||||||
txSummary: TxSummary;
|
txSummary: TxSummary;
|
||||||
@ -20,17 +21,24 @@ export type TxSummary = {
|
|||||||
ancestorCount: number; // Number of ancestors
|
ancestorCount: number; // Number of ancestors
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface RateOption {
|
||||||
|
fee: number;
|
||||||
|
rate: number;
|
||||||
|
index: number;
|
||||||
|
}
|
||||||
|
|
||||||
export const DEFAULT_BID_RATIO = 5;
|
export const DEFAULT_BID_RATIO = 5;
|
||||||
export const MIN_BID_RATIO = 2;
|
export const MIN_BID_RATIO = 2;
|
||||||
export const MAX_BID_RATIO = 20;
|
export const MAX_BID_RATIO = 20;
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-accelerate-preview',
|
selector: 'app-accelerate-preview',
|
||||||
templateUrl: 'accelerate-preview.component.html',
|
templateUrl: 'accelerate-preview.component.html',
|
||||||
styleUrls: ['accelerate-preview.component.scss']
|
styleUrls: ['accelerate-preview.component.scss']
|
||||||
})
|
})
|
||||||
export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges {
|
export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges {
|
||||||
@Input() txid: string | undefined;
|
@Input() tx: Transaction | undefined;
|
||||||
@Input() scrollEvent: boolean;
|
@Input() scrollEvent: boolean;
|
||||||
|
|
||||||
math = Math;
|
math = Math;
|
||||||
@ -47,6 +55,8 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges
|
|||||||
userBid = 0;
|
userBid = 0;
|
||||||
selectFeeRateIndex = 2;
|
selectFeeRateIndex = 2;
|
||||||
|
|
||||||
|
maxRateOptions: RateOption[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private apiService: ApiService,
|
private apiService: ApiService,
|
||||||
private storageService: StorageService
|
private storageService: StorageService
|
||||||
@ -65,7 +75,7 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.estimateSubscription = this.apiService.estimate$(this.txid).pipe(
|
this.estimateSubscription = this.apiService.estimate$(this.tx.txid).pipe(
|
||||||
tap((response) => {
|
tap((response) => {
|
||||||
if (response.status === 204) {
|
if (response.status === 204) {
|
||||||
this.estimate = undefined;
|
this.estimate = undefined;
|
||||||
@ -88,8 +98,15 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Make min extra fee at least 50% of the current tx fee
|
// Make min extra fee at least 50% of the current tx fee
|
||||||
this.minExtraCost = Math.max(this.estimate.cost, this.estimate.txSummary.effectiveFee / 2);
|
this.minExtraCost = Math.round(Math.max(this.estimate.cost, this.estimate.txSummary.effectiveFee / 2));
|
||||||
this.minExtraCost = Math.round(this.minExtraCost);
|
|
||||||
|
this.maxRateOptions = [2, 5, 10, 20].map((multiplier, index) => {
|
||||||
|
return {
|
||||||
|
fee: this.minExtraCost * multiplier,
|
||||||
|
rate: (this.estimate.txSummary.effectiveFee + (this.minExtraCost * multiplier)) / this.estimate.txSummary.effectiveVsize,
|
||||||
|
index,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
this.minBidAllowed = this.minExtraCost * MIN_BID_RATIO;
|
this.minBidAllowed = this.minExtraCost * MIN_BID_RATIO;
|
||||||
this.maxBidAllowed = this.minExtraCost * MAX_BID_RATIO;
|
this.maxBidAllowed = this.minExtraCost * MAX_BID_RATIO;
|
||||||
@ -121,10 +138,10 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges
|
|||||||
/**
|
/**
|
||||||
* User changed his bid
|
* User changed his bid
|
||||||
*/
|
*/
|
||||||
setUserBid(multiplier: number, index: number) {
|
setUserBid({ fee, index }: { fee: number, index: number}) {
|
||||||
if (this.estimate) {
|
if (this.estimate) {
|
||||||
this.selectFeeRateIndex = index;
|
this.selectFeeRateIndex = index;
|
||||||
this.userBid = Math.max(0, this.minExtraCost * multiplier);
|
this.userBid = Math.max(0, fee);
|
||||||
this.maxCost = this.userBid + this.estimate.mempoolBaseFee + this.estimate.vsizeFee;
|
this.maxCost = this.userBid + this.estimate.mempoolBaseFee + this.estimate.vsizeFee;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -156,7 +173,7 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges
|
|||||||
this.accelerationSubscription.unsubscribe();
|
this.accelerationSubscription.unsubscribe();
|
||||||
}
|
}
|
||||||
this.accelerationSubscription = this.apiService.accelerate$(
|
this.accelerationSubscription = this.apiService.accelerate$(
|
||||||
this.txid,
|
this.tx.txid,
|
||||||
this.userBid
|
this.userBid
|
||||||
).subscribe({
|
).subscribe({
|
||||||
next: () => {
|
next: () => {
|
||||||
|
@ -84,7 +84,7 @@
|
|||||||
<h2>Accelerate</h2>
|
<h2>Accelerate</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<app-accelerate-preview [txid]="txId" [scrollEvent]="scrollIntoAccelPreview"></app-accelerate-preview>
|
<app-accelerate-preview [tx]="tx" [scrollEvent]="scrollIntoAccelPreview"></app-accelerate-preview>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
@ -94,6 +94,7 @@ import { GeolocationComponent } from '../shared/components/geolocation/geolocati
|
|||||||
import { TestnetAlertComponent } from './components/testnet-alert/testnet-alert.component';
|
import { TestnetAlertComponent } from './components/testnet-alert/testnet-alert.component';
|
||||||
import { GlobalFooterComponent } from './components/global-footer/global-footer.component';
|
import { GlobalFooterComponent } from './components/global-footer/global-footer.component';
|
||||||
import { AcceleratePreviewComponent } from '../components/accelerate-preview/accelerate-preview.component';
|
import { AcceleratePreviewComponent } from '../components/accelerate-preview/accelerate-preview.component';
|
||||||
|
import { AccelerateFeeGraphComponent } from '../components/accelerate-preview/accelerate-fee-graph.component';
|
||||||
import { MempoolErrorComponent } from './components/mempool-error/mempool-error.component';
|
import { MempoolErrorComponent } from './components/mempool-error/mempool-error.component';
|
||||||
|
|
||||||
import { MempoolBlockOverviewComponent } from '../components/mempool-block-overview/mempool-block-overview.component';
|
import { MempoolBlockOverviewComponent } from '../components/mempool-block-overview/mempool-block-overview.component';
|
||||||
@ -192,6 +193,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir
|
|||||||
TestnetAlertComponent,
|
TestnetAlertComponent,
|
||||||
GlobalFooterComponent,
|
GlobalFooterComponent,
|
||||||
AcceleratePreviewComponent,
|
AcceleratePreviewComponent,
|
||||||
|
AccelerateFeeGraphComponent,
|
||||||
CalculatorComponent,
|
CalculatorComponent,
|
||||||
BitcoinsatoshisPipe,
|
BitcoinsatoshisPipe,
|
||||||
MempoolBlockOverviewComponent,
|
MempoolBlockOverviewComponent,
|
||||||
@ -315,6 +317,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir
|
|||||||
PreviewTitleComponent,
|
PreviewTitleComponent,
|
||||||
GlobalFooterComponent,
|
GlobalFooterComponent,
|
||||||
AcceleratePreviewComponent,
|
AcceleratePreviewComponent,
|
||||||
|
AccelerateFeeGraphComponent,
|
||||||
MempoolErrorComponent,
|
MempoolErrorComponent,
|
||||||
|
|
||||||
MempoolBlockOverviewComponent,
|
MempoolBlockOverviewComponent,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user