Adding "mempool block" details. Work in progress!
This commit is contained in:
		
							parent
							
								
									3e6f382c4d
								
							
						
					
					
						commit
						72658c19f6
					
				@ -62,6 +62,7 @@ class MempoolBlocks {
 | 
			
		||||
      blockSize: blockSize,
 | 
			
		||||
      blockVSize: blockVSize,
 | 
			
		||||
      nTx: transactions.length,
 | 
			
		||||
      totalFees: transactions.reduce((acc, cur) => acc + cur.fee, 0),
 | 
			
		||||
      medianFee: this.median(transactions.map((tx) => tx.feePerVsize)),
 | 
			
		||||
      feeRange: this.getFeesInRange(transactions, rangeLength),
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
@ -12,6 +12,7 @@ export interface MempoolBlock {
 | 
			
		||||
  blockVSize: number;
 | 
			
		||||
  nTx: number;
 | 
			
		||||
  medianFee: number;
 | 
			
		||||
  totalFees: number;
 | 
			
		||||
  feeRange: number[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -8,6 +8,7 @@ import { MasterPageComponent } from './components/master-page/master-page.compon
 | 
			
		||||
import { AboutComponent } from './components/about/about.component';
 | 
			
		||||
import { TelevisionComponent } from './components/television/television.component';
 | 
			
		||||
import { StatisticsComponent } from './components/statistics/statistics.component';
 | 
			
		||||
import { MempoolBlockComponent } from './components/mempool-block/mempool-block.component';
 | 
			
		||||
 | 
			
		||||
const routes: Routes = [
 | 
			
		||||
  {
 | 
			
		||||
@ -36,6 +37,11 @@ const routes: Routes = [
 | 
			
		||||
        children: [],
 | 
			
		||||
        component: BlockComponent
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        path: 'mempool-block/:id',
 | 
			
		||||
        children: [],
 | 
			
		||||
        component: MempoolBlockComponent
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        path: 'address/:id',
 | 
			
		||||
        children: [],
 | 
			
		||||
 | 
			
		||||
@ -40,6 +40,8 @@ import { BlockchainComponent } from './components/blockchain/blockchain.componen
 | 
			
		||||
import { FooterComponent } from './components/footer/footer.component';
 | 
			
		||||
import { AudioService } from './services/audio.service';
 | 
			
		||||
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({
 | 
			
		||||
  declarations: [
 | 
			
		||||
@ -71,6 +73,8 @@ import { FiatComponent } from './fiat/fiat.component';
 | 
			
		||||
    ChartistComponent,
 | 
			
		||||
    FooterComponent,
 | 
			
		||||
    FiatComponent,
 | 
			
		||||
    MempoolBlockComponent,
 | 
			
		||||
    FeeDistributionGraphComponent,
 | 
			
		||||
  ],
 | 
			
		||||
  imports: [
 | 
			
		||||
    BrowserModule,
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
<div class="text-center" class="blockchain-wrapper">
 | 
			
		||||
  <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>
 | 
			
		||||
 | 
			
		||||
    <div id="divider" *ngIf="!isLoading; else loadingTmpl"></div>
 | 
			
		||||
 | 
			
		||||
@ -12,6 +12,7 @@ export class BlockchainComponent implements OnInit, OnDestroy {
 | 
			
		||||
  @Input() position: 'middle' | 'top' = 'middle';
 | 
			
		||||
  @Input() markHeight: number;
 | 
			
		||||
  @Input() txFeePerVSize: number;
 | 
			
		||||
  @Input() markMempoolBlockIndex = -1;
 | 
			
		||||
 | 
			
		||||
  txTrackingSubscription: 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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.progress-text {
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  text-align: center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@media (min-width: 768px) {
 | 
			
		||||
  .d-md-block {
 | 
			
		||||
      display: table-cell !important;
 | 
			
		||||
@ -16,9 +22,3 @@
 | 
			
		||||
      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 *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)">
 | 
			
		||||
        <a [routerLink]="['/mempool-block/', i]" class="blockLink"> </a>
 | 
			
		||||
        <div class="block-body" *ngIf="mempoolBlocks?.length">
 | 
			
		||||
          <div class="fees">
 | 
			
		||||
            <span class="yellow-color">~{{ projectedBlock.medianFee | ceil }} sats/vB</span>
 | 
			
		||||
 | 
			
		||||
@ -104,3 +104,11 @@
 | 
			
		||||
  border-right: 35px solid transparent;
 | 
			
		||||
  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;
 | 
			
		||||
 | 
			
		||||
  @Input() txFeePerVSize: number;
 | 
			
		||||
  @Input() markIndex: number;
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private stateService: StateService,
 | 
			
		||||
@ -42,7 +43,6 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  ngOnChanges() {
 | 
			
		||||
    this.calculateTransactionPosition();
 | 
			
		||||
  }
 | 
			
		||||
@ -91,13 +91,18 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  calculateTransactionPosition() {
 | 
			
		||||
    if (!this.txFeePerVSize || !this.mempoolBlocks) {
 | 
			
		||||
    if ((!this.txFeePerVSize && this.markIndex === -1) || !this.mempoolBlocks) {
 | 
			
		||||
      this.arrowVisible = false;
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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 (let i = 0; i < block.feeRange.length - 1; i++) {
 | 
			
		||||
        if (this.txFeePerVSize < block.feeRange[i + 1] && this.txFeePerVSize >= block.feeRange[i]) {
 | 
			
		||||
 | 
			
		||||
@ -19,6 +19,7 @@ export interface MempoolBlock {
 | 
			
		||||
  blockVSize: number;
 | 
			
		||||
  nTx: number;
 | 
			
		||||
  medianFee: number;
 | 
			
		||||
  totalFees: number;
 | 
			
		||||
  feeRange: number[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user