Adding "mempool block" details. Work in progress!
This commit is contained in:
parent
3e6f382c4d
commit
72658c19f6
@ -62,6 +62,7 @@ class MempoolBlocks {
|
|||||||
blockSize: blockSize,
|
blockSize: blockSize,
|
||||||
blockVSize: blockVSize,
|
blockVSize: blockVSize,
|
||||||
nTx: transactions.length,
|
nTx: transactions.length,
|
||||||
|
totalFees: transactions.reduce((acc, cur) => acc + cur.fee, 0),
|
||||||
medianFee: this.median(transactions.map((tx) => tx.feePerVsize)),
|
medianFee: this.median(transactions.map((tx) => tx.feePerVsize)),
|
||||||
feeRange: this.getFeesInRange(transactions, rangeLength),
|
feeRange: this.getFeesInRange(transactions, rangeLength),
|
||||||
};
|
};
|
||||||
|
@ -12,6 +12,7 @@ export interface MempoolBlock {
|
|||||||
blockVSize: number;
|
blockVSize: number;
|
||||||
nTx: number;
|
nTx: number;
|
||||||
medianFee: number;
|
medianFee: number;
|
||||||
|
totalFees: number;
|
||||||
feeRange: number[];
|
feeRange: number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ import { MasterPageComponent } from './components/master-page/master-page.compon
|
|||||||
import { AboutComponent } from './components/about/about.component';
|
import { AboutComponent } from './components/about/about.component';
|
||||||
import { TelevisionComponent } from './components/television/television.component';
|
import { TelevisionComponent } from './components/television/television.component';
|
||||||
import { StatisticsComponent } from './components/statistics/statistics.component';
|
import { StatisticsComponent } from './components/statistics/statistics.component';
|
||||||
|
import { MempoolBlockComponent } from './components/mempool-block/mempool-block.component';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
@ -36,6 +37,11 @@ const routes: Routes = [
|
|||||||
children: [],
|
children: [],
|
||||||
component: BlockComponent
|
component: BlockComponent
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'mempool-block/:id',
|
||||||
|
children: [],
|
||||||
|
component: MempoolBlockComponent
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'address/:id',
|
path: 'address/:id',
|
||||||
children: [],
|
children: [],
|
||||||
|
@ -40,6 +40,8 @@ import { BlockchainComponent } from './components/blockchain/blockchain.componen
|
|||||||
import { FooterComponent } from './components/footer/footer.component';
|
import { FooterComponent } from './components/footer/footer.component';
|
||||||
import { AudioService } from './services/audio.service';
|
import { AudioService } from './services/audio.service';
|
||||||
import { FiatComponent } from './fiat/fiat.component';
|
import { FiatComponent } from './fiat/fiat.component';
|
||||||
|
import { MempoolBlockComponent } from './components/mempool-block/mempool-block.component';
|
||||||
|
import { FeeDistributionGraphComponent } from './components/fee-distribution-graph/fee-distribution-graph.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@ -71,6 +73,8 @@ import { FiatComponent } from './fiat/fiat.component';
|
|||||||
ChartistComponent,
|
ChartistComponent,
|
||||||
FooterComponent,
|
FooterComponent,
|
||||||
FiatComponent,
|
FiatComponent,
|
||||||
|
MempoolBlockComponent,
|
||||||
|
FeeDistributionGraphComponent,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<div class="text-center" class="blockchain-wrapper">
|
<div class="text-center" class="blockchain-wrapper">
|
||||||
<div class="position-container">
|
<div class="position-container">
|
||||||
<app-mempool-blocks [txFeePerVSize]="markHeight ? 0 : txFeePerVSize"></app-mempool-blocks>
|
<app-mempool-blocks [markIndex]="markMempoolBlockIndex" [txFeePerVSize]="markHeight ? 0 : txFeePerVSize"></app-mempool-blocks>
|
||||||
<app-blockchain-blocks [markHeight]="markHeight"></app-blockchain-blocks>
|
<app-blockchain-blocks [markHeight]="markHeight"></app-blockchain-blocks>
|
||||||
|
|
||||||
<div id="divider" *ngIf="!isLoading; else loadingTmpl"></div>
|
<div id="divider" *ngIf="!isLoading; else loadingTmpl"></div>
|
||||||
|
@ -12,6 +12,7 @@ export class BlockchainComponent implements OnInit, OnDestroy {
|
|||||||
@Input() position: 'middle' | 'top' = 'middle';
|
@Input() position: 'middle' | 'top' = 'middle';
|
||||||
@Input() markHeight: number;
|
@Input() markHeight: number;
|
||||||
@Input() txFeePerVSize: number;
|
@Input() txFeePerVSize: number;
|
||||||
|
@Input() markMempoolBlockIndex = -1;
|
||||||
|
|
||||||
txTrackingSubscription: Subscription;
|
txTrackingSubscription: Subscription;
|
||||||
blocksSubscription: Subscription;
|
blocksSubscription: Subscription;
|
||||||
|
@ -0,0 +1,13 @@
|
|||||||
|
<div style="height: 400px;" *ngIf="mempoolVsizeFeesData; else loadingFees">
|
||||||
|
<app-chartist
|
||||||
|
[data]="mempoolVsizeFeesData"
|
||||||
|
[type]="'Line'"
|
||||||
|
[options]="mempoolVsizeFeesOptions">
|
||||||
|
</app-chartist>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ng-template #loadingFees>
|
||||||
|
<div class="text-center">
|
||||||
|
<div class="spinner-border text-light"></div>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
@ -0,0 +1,66 @@
|
|||||||
|
import { Component, OnInit, Input, OnChanges } from '@angular/core';
|
||||||
|
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||||
|
import * as Chartist from 'chartist';
|
||||||
|
import { VbytesPipe } from 'src/app/pipes/bytes-pipe/vbytes.pipe';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-fee-distribution-graph',
|
||||||
|
templateUrl: './fee-distribution-graph.component.html',
|
||||||
|
styleUrls: ['./fee-distribution-graph.component.scss']
|
||||||
|
})
|
||||||
|
export class FeeDistributionGraphComponent implements OnChanges {
|
||||||
|
@Input() feeRange;
|
||||||
|
|
||||||
|
mempoolVsizeFeesData: any;
|
||||||
|
mempoolVsizeFeesOptions: any;
|
||||||
|
|
||||||
|
mempoolVsizeFeesPieData: any;
|
||||||
|
mempoolVsizeFeesPieOptions: any;
|
||||||
|
|
||||||
|
feeLevels = [1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 30, 40, 50, 60, 70, 80, 90, 100, 125, 150, 175, 200,
|
||||||
|
250, 300, 350, 400, 500];
|
||||||
|
|
||||||
|
radioGroupForm: FormGroup;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private vbytesPipe: VbytesPipe,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
ngOnChanges() {
|
||||||
|
this.mempoolVsizeFeesOptions = {
|
||||||
|
showArea: true,
|
||||||
|
showLine: true,
|
||||||
|
fullWidth: true,
|
||||||
|
showPoint: false,
|
||||||
|
low: 0,
|
||||||
|
axisY: {
|
||||||
|
labelInterpolationFnc: (value: number): any => {
|
||||||
|
return this.vbytesPipe.transform(value, 2);
|
||||||
|
},
|
||||||
|
offset: 60
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const fees = this.feeRange;
|
||||||
|
const series = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < this.feeLevels.length; i++) {
|
||||||
|
let total = 0;
|
||||||
|
for (let j = 0; j < fees.length; j++) {
|
||||||
|
if (i === this.feeLevels.length - 1) {
|
||||||
|
if (fees[j] >= this.feeLevels[i]) {
|
||||||
|
total += 1;
|
||||||
|
}
|
||||||
|
} else if (fees[j] >= this.feeLevels[i] && fees[j] < this.feeLevels[i + 1]) {
|
||||||
|
total += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
series.push(total);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mempoolVsizeFeesData = {
|
||||||
|
series: [fees]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -6,6 +6,12 @@
|
|||||||
background-color: #2d3348;
|
background-color: #2d3348;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.progress-text {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
.d-md-block {
|
.d-md-block {
|
||||||
display: table-cell !important;
|
display: table-cell !important;
|
||||||
@ -16,9 +22,3 @@
|
|||||||
display: table-cell !important;
|
display: table-cell !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress-text {
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
@ -0,0 +1,54 @@
|
|||||||
|
<div class="container-xl" *ngIf="mempoolBlock">
|
||||||
|
|
||||||
|
<div style="position: relative;">
|
||||||
|
<app-blockchain position="top" [markMempoolBlockIndex]="mempoolBlockIndex"></app-blockchain>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="title-block">
|
||||||
|
<h1>Mempool block</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm">
|
||||||
|
<table class="table table-borderless table-striped">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Median fee</td>
|
||||||
|
<td>~{{ mempoolBlock.medianFee | ceil }} sats/vB (<app-fiat [value]="mempoolBlock.medianFee * 250" digitsInfo="1.2-2"></app-fiat>)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Fee span</td>
|
||||||
|
<td><span class="yellow-color">{{ mempoolBlock.feeRange[0] | ceil }} - {{ mempoolBlock.feeRange[mempoolBlock.feeRange.length - 1] | ceil }} sat/vB</span></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Total fees</td>
|
||||||
|
<td>{{ mempoolBlock.totalFees / 100000000 | number : '1.2-2' }} BTC (<app-fiat [value]="mempoolBlock.totalFees" digitsInfo="1.0-0"></app-fiat>)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Transactions</td>
|
||||||
|
<td>{{ mempoolBlock.nTx }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Filled</td>
|
||||||
|
<td>
|
||||||
|
<div class="progress position-relative">
|
||||||
|
<div class="progress-bar progress-mempool" role="progressbar" [ngStyle]="{'width': (mempoolBlock.blockVSize / 1000000) * 100 + '%' }"></div>
|
||||||
|
<div class="progress-text">{{ mempoolBlock.blockSize | bytes: 2 }}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm">
|
||||||
|
<app-fee-distribution-graph [feeRange]="mempoolBlock.feeRange"></app-fee-distribution-graph>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
</div>
|
@ -0,0 +1,25 @@
|
|||||||
|
.progress-mempool {
|
||||||
|
background: repeating-linear-gradient(to right, #2d3348, #2d3348 0%, #105fb0 0%, #9339f4 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress {
|
||||||
|
background-color: #2d3348;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-text {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-block {
|
||||||
|
color: #FFF;
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-top: 20px;
|
||||||
|
padding-bottom: 3px;
|
||||||
|
border-top: 5px solid #FFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-block > h1 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { MempoolBlockComponent } from './mempool-block.component';
|
||||||
|
|
||||||
|
describe('MempoolBlockComponent', () => {
|
||||||
|
let component: MempoolBlockComponent;
|
||||||
|
let fixture: ComponentFixture<MempoolBlockComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ MempoolBlockComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(MempoolBlockComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,40 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { StateService } from 'src/app/services/state.service';
|
||||||
|
import { ActivatedRoute, ParamMap } from '@angular/router';
|
||||||
|
import { switchMap, map } from 'rxjs/operators';
|
||||||
|
import { MempoolBlock } from 'src/app/interfaces/websocket.interface';
|
||||||
|
import { WebsocketService } from 'src/app/services/websocket.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-mempool-block',
|
||||||
|
templateUrl: './mempool-block.component.html',
|
||||||
|
styleUrls: ['./mempool-block.component.scss']
|
||||||
|
})
|
||||||
|
export class MempoolBlockComponent implements OnInit {
|
||||||
|
mempoolBlockIndex: number;
|
||||||
|
mempoolBlock: MempoolBlock;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private stateService: StateService,
|
||||||
|
private websocketService: WebsocketService,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.websocketService.want(['blocks', 'stats', 'mempool-blocks']);
|
||||||
|
|
||||||
|
this.route.paramMap.pipe(
|
||||||
|
switchMap((params: ParamMap) => {
|
||||||
|
this.mempoolBlockIndex = parseInt(params.get('id'), 10) || 0;
|
||||||
|
return this.stateService.mempoolBlocks$
|
||||||
|
.pipe(
|
||||||
|
map((mempoolBlocks) => mempoolBlocks[this.mempoolBlockIndex])
|
||||||
|
);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.subscribe((mempoolBlock) => {
|
||||||
|
this.mempoolBlock = mempoolBlock;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -2,6 +2,7 @@
|
|||||||
<div class="flashing">
|
<div class="flashing">
|
||||||
<div *ngFor="let projectedBlock of mempoolBlocks; let i = index; trackBy: trackByFn">
|
<div *ngFor="let projectedBlock of mempoolBlocks; let i = index; trackBy: trackByFn">
|
||||||
<div class="bitcoin-block text-center mempool-block" id="mempool-block-{{ i }}" [ngStyle]="getStyleForMempoolBlockAtIndex(i)">
|
<div class="bitcoin-block text-center mempool-block" id="mempool-block-{{ i }}" [ngStyle]="getStyleForMempoolBlockAtIndex(i)">
|
||||||
|
<a [routerLink]="['/mempool-block/', i]" class="blockLink"> </a>
|
||||||
<div class="block-body" *ngIf="mempoolBlocks?.length">
|
<div class="block-body" *ngIf="mempoolBlocks?.length">
|
||||||
<div class="fees">
|
<div class="fees">
|
||||||
<span class="yellow-color">~{{ projectedBlock.medianFee | ceil }} sats/vB</span>
|
<span class="yellow-color">~{{ projectedBlock.medianFee | ceil }} sats/vB</span>
|
||||||
|
@ -104,3 +104,11 @@
|
|||||||
border-right: 35px solid transparent;
|
border-right: 35px solid transparent;
|
||||||
border-bottom: 35px solid #FFF;
|
border-bottom: 35px solid #FFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.blockLink {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
@ -20,6 +20,7 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
rightPosition = 0;
|
rightPosition = 0;
|
||||||
|
|
||||||
@Input() txFeePerVSize: number;
|
@Input() txFeePerVSize: number;
|
||||||
|
@Input() markIndex: number;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private stateService: StateService,
|
private stateService: StateService,
|
||||||
@ -42,7 +43,6 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ngOnChanges() {
|
ngOnChanges() {
|
||||||
this.calculateTransactionPosition();
|
this.calculateTransactionPosition();
|
||||||
}
|
}
|
||||||
@ -91,13 +91,18 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
calculateTransactionPosition() {
|
calculateTransactionPosition() {
|
||||||
if (!this.txFeePerVSize || !this.mempoolBlocks) {
|
if ((!this.txFeePerVSize && this.markIndex === -1) || !this.mempoolBlocks) {
|
||||||
this.arrowVisible = false;
|
this.arrowVisible = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.arrowVisible = true;
|
this.arrowVisible = true;
|
||||||
|
|
||||||
|
if (this.markIndex > -1) {
|
||||||
|
this.rightPosition = this.markIndex * (this.blockWidth + this.blockPadding) + 0.5 * this.blockWidth;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (const block of this.mempoolBlocks) {
|
for (const block of this.mempoolBlocks) {
|
||||||
for (let i = 0; i < block.feeRange.length - 1; i++) {
|
for (let i = 0; i < block.feeRange.length - 1; i++) {
|
||||||
if (this.txFeePerVSize < block.feeRange[i + 1] && this.txFeePerVSize >= block.feeRange[i]) {
|
if (this.txFeePerVSize < block.feeRange[i + 1] && this.txFeePerVSize >= block.feeRange[i]) {
|
||||||
|
@ -19,6 +19,7 @@ export interface MempoolBlock {
|
|||||||
blockVSize: number;
|
blockVSize: number;
|
||||||
nTx: number;
|
nTx: number;
|
||||||
medianFee: number;
|
medianFee: number;
|
||||||
|
totalFees: number;
|
||||||
feeRange: number[];
|
feeRange: number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user