Improve stability of mempool tx position arrow
This commit is contained in:
		
							parent
							
								
									acc668bdbe
								
							
						
					
					
						commit
						9f32d43907
					
				@ -252,9 +252,17 @@ class MempoolBlocks {
 | 
			
		||||
 | 
			
		||||
  private processBlockTemplates(mempool, blocks, clusters, saveResults): MempoolBlockWithTransactions[] {
 | 
			
		||||
    // update this thread's mempool with the results
 | 
			
		||||
    blocks.forEach(block => {
 | 
			
		||||
    blocks.forEach((block, blockIndex) => {
 | 
			
		||||
      let runningVsize = 0;
 | 
			
		||||
      block.forEach(tx => {
 | 
			
		||||
        if (tx.txid && tx.txid in mempool) {
 | 
			
		||||
          // save position in projected blocks
 | 
			
		||||
          mempool[tx.txid].position = {
 | 
			
		||||
            block: blockIndex,
 | 
			
		||||
            vsize: runningVsize + (mempool[tx.txid].vsize / 2),
 | 
			
		||||
          };
 | 
			
		||||
          runningVsize += mempool[tx.txid].vsize;
 | 
			
		||||
 | 
			
		||||
          if (tx.effectiveFeePerVsize != null) {
 | 
			
		||||
            mempool[tx.txid].effectiveFeePerVsize = tx.effectiveFeePerVsize;
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,6 @@ import config from '../config';
 | 
			
		||||
import logger from '../logger';
 | 
			
		||||
import { ThreadTransaction, MempoolBlockWithTransactions, AuditTransaction } from '../mempool.interfaces';
 | 
			
		||||
import { PairingHeap } from '../utils/pairing-heap';
 | 
			
		||||
import { Common } from './common';
 | 
			
		||||
import { parentPort } from 'worker_threads';
 | 
			
		||||
 | 
			
		||||
let mempool: { [txid: string]: ThreadTransaction } = {};
 | 
			
		||||
@ -72,7 +71,14 @@ function makeBlockTemplates(mempool: { [txid: string]: ThreadTransaction })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Sort by descending ancestor score
 | 
			
		||||
  mempoolArray.sort((a, b) => (b.score || 0) - (a.score || 0));
 | 
			
		||||
  mempoolArray.sort((a, b) => {
 | 
			
		||||
    if (b.score === a.score) {
 | 
			
		||||
      // tie-break by lexicographic txid order for stability
 | 
			
		||||
      return a.txid < b.txid ? -1 : 1;
 | 
			
		||||
    } else {
 | 
			
		||||
      return (b.score || 0) - (a.score || 0);
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  // Build blocks by greedily choosing the highest feerate package
 | 
			
		||||
  // (i.e. the package rooted in the transaction with the best ancestor score)
 | 
			
		||||
@ -80,7 +86,14 @@ function makeBlockTemplates(mempool: { [txid: string]: ThreadTransaction })
 | 
			
		||||
  let blockWeight = 4000;
 | 
			
		||||
  let blockSize = 0;
 | 
			
		||||
  let transactions: AuditTransaction[] = [];
 | 
			
		||||
  const modified: PairingHeap<AuditTransaction> = new PairingHeap((a, b): boolean => (a.score || 0) > (b.score || 0));
 | 
			
		||||
  const modified: PairingHeap<AuditTransaction> = new PairingHeap((a, b): boolean => {
 | 
			
		||||
    if (a.score === b.score) {
 | 
			
		||||
      // tie-break by lexicographic txid order for stability
 | 
			
		||||
      return a.txid > b.txid;
 | 
			
		||||
    } else {
 | 
			
		||||
      return (a.score || 0) > (b.score || 0);
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
  let overflow: AuditTransaction[] = [];
 | 
			
		||||
  let failures = 0;
 | 
			
		||||
  let top = 0;
 | 
			
		||||
 | 
			
		||||
@ -66,9 +66,10 @@ class WebsocketHandler {
 | 
			
		||||
          if (parsedMessage && parsedMessage['track-tx']) {
 | 
			
		||||
            if (/^[a-fA-F0-9]{64}$/.test(parsedMessage['track-tx'])) {
 | 
			
		||||
              client['track-tx'] = parsedMessage['track-tx'];
 | 
			
		||||
              const trackTxid = client['track-tx'];
 | 
			
		||||
              // Client is telling the transaction wasn't found
 | 
			
		||||
              if (parsedMessage['watch-mempool']) {
 | 
			
		||||
                const rbfCacheTxid = rbfCache.getReplacedBy(client['track-tx']);
 | 
			
		||||
                const rbfCacheTxid = rbfCache.getReplacedBy(trackTxid);
 | 
			
		||||
                if (rbfCacheTxid) {
 | 
			
		||||
                  response['txReplaced'] = {
 | 
			
		||||
                    txid: rbfCacheTxid,
 | 
			
		||||
@ -76,7 +77,7 @@ class WebsocketHandler {
 | 
			
		||||
                  client['track-tx'] = null;
 | 
			
		||||
                } else {
 | 
			
		||||
                  // It might have appeared before we had the time to start watching for it
 | 
			
		||||
                  const tx = memPool.getMempool()[client['track-tx']];
 | 
			
		||||
                  const tx = memPool.getMempool()[trackTxid];
 | 
			
		||||
                  if (tx) {
 | 
			
		||||
                    if (config.MEMPOOL.BACKEND === 'esplora') {
 | 
			
		||||
                      response['tx'] = tx;
 | 
			
		||||
@ -100,6 +101,13 @@ class WebsocketHandler {
 | 
			
		||||
                  }
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
              const tx = memPool.getMempool()[trackTxid];
 | 
			
		||||
              if (tx && tx.position) {
 | 
			
		||||
                response['txPosition'] = {
 | 
			
		||||
                  txid: trackTxid,
 | 
			
		||||
                  position: tx.position,
 | 
			
		||||
                };
 | 
			
		||||
              }
 | 
			
		||||
            } else {
 | 
			
		||||
              client['track-tx'] = null;
 | 
			
		||||
            }
 | 
			
		||||
@ -401,9 +409,10 @@ class WebsocketHandler {
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (client['track-tx']) {
 | 
			
		||||
        const trackTxid = client['track-tx'];
 | 
			
		||||
        const outspends: object = {};
 | 
			
		||||
        newTransactions.forEach((tx) => tx.vin.forEach((vin, i) => {
 | 
			
		||||
          if (vin.txid === client['track-tx']) {
 | 
			
		||||
          if (vin.txid === trackTxid) {
 | 
			
		||||
            outspends[vin.vout] = {
 | 
			
		||||
              vin: i,
 | 
			
		||||
              txid: tx.txid,
 | 
			
		||||
@ -426,6 +435,14 @@ class WebsocketHandler {
 | 
			
		||||
        if (rbfChange) {
 | 
			
		||||
          response['rbfInfo'] = rbfChanges.trees[rbfChange];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const mempoolTx = newMempool[trackTxid];
 | 
			
		||||
        if (mempoolTx && mempoolTx.position) {
 | 
			
		||||
          response['txPosition'] = {
 | 
			
		||||
            txid: trackTxid,
 | 
			
		||||
            position: mempoolTx.position,
 | 
			
		||||
          };
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (client['track-mempool-block'] >= 0) {
 | 
			
		||||
@ -556,8 +573,19 @@ class WebsocketHandler {
 | 
			
		||||
        response['mempool-blocks'] = mBlocks;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (client['track-tx'] && txIds.indexOf(client['track-tx']) > -1) {
 | 
			
		||||
        response['txConfirmed'] = true;
 | 
			
		||||
      if (client['track-tx']) {
 | 
			
		||||
        const trackTxid = client['track-tx'];
 | 
			
		||||
        if (txIds.indexOf(trackTxid) > -1) {
 | 
			
		||||
          response['txConfirmed'] = true;
 | 
			
		||||
        } else {
 | 
			
		||||
          const mempoolTx = _memPool[trackTxid];
 | 
			
		||||
          if (mempoolTx && mempoolTx.position) {
 | 
			
		||||
            response['txPosition'] = {
 | 
			
		||||
              txid: trackTxid,
 | 
			
		||||
              position: mempoolTx.position,
 | 
			
		||||
            };
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (client['track-address']) {
 | 
			
		||||
 | 
			
		||||
@ -80,6 +80,10 @@ export interface TransactionExtended extends IEsploraApi.Transaction {
 | 
			
		||||
  bestDescendant?: BestDescendant | null;
 | 
			
		||||
  cpfpChecked?: boolean;
 | 
			
		||||
  deleteAfter?: number;
 | 
			
		||||
  position?: {
 | 
			
		||||
    block: number,
 | 
			
		||||
    vsize: number,
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface AuditTransaction {
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,7 @@ import { feeLevels, mempoolFeeColors } from '../../app.constants';
 | 
			
		||||
import { specialBlocks } from '../../app.constants';
 | 
			
		||||
import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe';
 | 
			
		||||
import { Location } from '@angular/common';
 | 
			
		||||
import { DifficultyAdjustment } from '../../interfaces/node-api.interface';
 | 
			
		||||
import { DifficultyAdjustment, MempoolPosition } from '../../interfaces/node-api.interface';
 | 
			
		||||
import { animate, style, transition, trigger } from '@angular/animations';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
@ -58,6 +58,7 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
 | 
			
		||||
  transition = 'background 2s, right 2s, transform 1s';
 | 
			
		||||
 | 
			
		||||
  markIndex: number;
 | 
			
		||||
  txPosition: MempoolPosition;
 | 
			
		||||
  txFeePerVSize: number;
 | 
			
		||||
 | 
			
		||||
  resetTransitionTimeout: number;
 | 
			
		||||
@ -156,6 +157,9 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
 | 
			
		||||
        if (state.mempoolBlockIndex !== undefined) {
 | 
			
		||||
          this.markIndex = state.mempoolBlockIndex;
 | 
			
		||||
        }
 | 
			
		||||
        if (state.mempoolPosition) {
 | 
			
		||||
          this.txPosition = state.mempoolPosition;
 | 
			
		||||
        }
 | 
			
		||||
        if (state.txFeePerVSize) {
 | 
			
		||||
          this.txFeePerVSize = state.txFeePerVSize;
 | 
			
		||||
        }
 | 
			
		||||
@ -302,7 +306,7 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  calculateTransactionPosition() {
 | 
			
		||||
    if ((!this.txFeePerVSize && (this.markIndex === undefined || this.markIndex === -1)) || !this.mempoolBlocks) {
 | 
			
		||||
    if ((!this.txPosition && !this.txFeePerVSize && (this.markIndex === undefined || this.markIndex === -1)) || !this.mempoolBlocks) {
 | 
			
		||||
      this.arrowVisible = false;
 | 
			
		||||
      return;
 | 
			
		||||
    } else if (this.markIndex > -1) {
 | 
			
		||||
@ -320,33 +324,43 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
 | 
			
		||||
 | 
			
		||||
    this.arrowVisible = true;
 | 
			
		||||
 | 
			
		||||
    let found = false;
 | 
			
		||||
    for (let txInBlockIndex = 0; txInBlockIndex < this.mempoolBlocks.length && !found; txInBlockIndex++) {
 | 
			
		||||
      const block = this.mempoolBlocks[txInBlockIndex];
 | 
			
		||||
      for (let i = 0; i < block.feeRange.length - 1 && !found; i++) {
 | 
			
		||||
        if (this.txFeePerVSize < block.feeRange[i + 1] && this.txFeePerVSize >= block.feeRange[i]) {
 | 
			
		||||
          const feeRangeIndex = i;
 | 
			
		||||
          const feeRangeChunkSize = 1 / (block.feeRange.length - 1);
 | 
			
		||||
    if (this.txPosition) {
 | 
			
		||||
      if (this.txPosition.block >= this.mempoolBlocks.length) {
 | 
			
		||||
        this.rightPosition = ((this.mempoolBlocks.length - 1) * (this.blockWidth + this.blockPadding)) + this.blockWidth;
 | 
			
		||||
      } else {
 | 
			
		||||
        const positionInBlock = Math.min(1, this.txPosition.vsize / this.stateService.blockVSize) * this.blockWidth;
 | 
			
		||||
        const positionOfBlock = this.txPosition.block * (this.blockWidth + this.blockPadding);
 | 
			
		||||
        this.rightPosition = positionOfBlock + positionInBlock;
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      let found = false;
 | 
			
		||||
      for (let txInBlockIndex = 0; txInBlockIndex < this.mempoolBlocks.length && !found; txInBlockIndex++) {
 | 
			
		||||
        const block = this.mempoolBlocks[txInBlockIndex];
 | 
			
		||||
        for (let i = 0; i < block.feeRange.length - 1 && !found; i++) {
 | 
			
		||||
          if (this.txFeePerVSize < block.feeRange[i + 1] && this.txFeePerVSize >= block.feeRange[i]) {
 | 
			
		||||
            const feeRangeIndex = i;
 | 
			
		||||
            const feeRangeChunkSize = 1 / (block.feeRange.length - 1);
 | 
			
		||||
 | 
			
		||||
          const txFee = this.txFeePerVSize - block.feeRange[i];
 | 
			
		||||
          const max = block.feeRange[i + 1] - block.feeRange[i];
 | 
			
		||||
          const blockLocation = txFee / max;
 | 
			
		||||
            const txFee = this.txFeePerVSize - block.feeRange[i];
 | 
			
		||||
            const max = block.feeRange[i + 1] - block.feeRange[i];
 | 
			
		||||
            const blockLocation = txFee / max;
 | 
			
		||||
 | 
			
		||||
          const chunkPositionOffset = blockLocation * feeRangeChunkSize;
 | 
			
		||||
          const feePosition = feeRangeChunkSize * feeRangeIndex + chunkPositionOffset;
 | 
			
		||||
            const chunkPositionOffset = blockLocation * feeRangeChunkSize;
 | 
			
		||||
            const feePosition = feeRangeChunkSize * feeRangeIndex + chunkPositionOffset;
 | 
			
		||||
 | 
			
		||||
          const blockedFilledPercentage = (block.blockVSize > this.stateService.blockVSize ? this.stateService.blockVSize : block.blockVSize) / this.stateService.blockVSize;
 | 
			
		||||
          const arrowRightPosition = txInBlockIndex * (this.blockWidth + this.blockPadding)
 | 
			
		||||
            + ((1 - feePosition) * blockedFilledPercentage * this.blockWidth);
 | 
			
		||||
            const blockedFilledPercentage = (block.blockVSize > this.stateService.blockVSize ? this.stateService.blockVSize : block.blockVSize) / this.stateService.blockVSize;
 | 
			
		||||
            const arrowRightPosition = txInBlockIndex * (this.blockWidth + this.blockPadding)
 | 
			
		||||
              + ((1 - feePosition) * blockedFilledPercentage * this.blockWidth);
 | 
			
		||||
 | 
			
		||||
          this.rightPosition = arrowRightPosition;
 | 
			
		||||
            this.rightPosition = arrowRightPosition;
 | 
			
		||||
            found = true;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        if (this.txFeePerVSize >= block.feeRange[block.feeRange.length - 1]) {
 | 
			
		||||
          this.rightPosition = txInBlockIndex * (this.blockWidth + this.blockPadding);
 | 
			
		||||
          found = true;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      if (this.txFeePerVSize >= block.feeRange[block.feeRange.length - 1]) {
 | 
			
		||||
        this.rightPosition = txInBlockIndex * (this.blockWidth + this.blockPadding);
 | 
			
		||||
        found = true;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -19,7 +19,7 @@ import { WebsocketService } from '../../services/websocket.service';
 | 
			
		||||
import { AudioService } from '../../services/audio.service';
 | 
			
		||||
import { ApiService } from '../../services/api.service';
 | 
			
		||||
import { SeoService } from '../../services/seo.service';
 | 
			
		||||
import { BlockExtended, CpfpInfo, RbfTree } from '../../interfaces/node-api.interface';
 | 
			
		||||
import { BlockExtended, CpfpInfo, RbfTree, MempoolPosition } from '../../interfaces/node-api.interface';
 | 
			
		||||
import { LiquidUnblinding } from './liquid-ublinding';
 | 
			
		||||
import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe';
 | 
			
		||||
import { Price, PriceService } from '../../services/price.service';
 | 
			
		||||
@ -35,6 +35,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
 | 
			
		||||
  tx: Transaction;
 | 
			
		||||
  txId: string;
 | 
			
		||||
  txInBlockIndex: number;
 | 
			
		||||
  mempoolPosition: MempoolPosition;
 | 
			
		||||
  isLoadingTx = true;
 | 
			
		||||
  error: any = undefined;
 | 
			
		||||
  errorUnblinded: any = undefined;
 | 
			
		||||
@ -47,6 +48,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
 | 
			
		||||
  fetchCachedTxSubscription: Subscription;
 | 
			
		||||
  txReplacedSubscription: Subscription;
 | 
			
		||||
  txRbfInfoSubscription: Subscription;
 | 
			
		||||
  mempoolPositionSubscription: Subscription;
 | 
			
		||||
  blocksSubscription: Subscription;
 | 
			
		||||
  queryParamsSubscription: Subscription;
 | 
			
		||||
  urlFragmentSubscription: Subscription;
 | 
			
		||||
@ -174,6 +176,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
 | 
			
		||||
        if (!this.tx?.status?.confirmed) {
 | 
			
		||||
          this.stateService.markBlock$.next({
 | 
			
		||||
            txFeePerVSize: this.tx.effectiveFeePerVsize,
 | 
			
		||||
            mempoolPosition: this.mempoolPosition,
 | 
			
		||||
          });
 | 
			
		||||
        }
 | 
			
		||||
        this.cpfpInfo = cpfpInfo;
 | 
			
		||||
@ -231,6 +234,19 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    this.mempoolPositionSubscription = this.stateService.mempoolTxPosition$.subscribe(txPosition => {
 | 
			
		||||
      if (txPosition && txPosition.txid === this.txId && txPosition.position) {
 | 
			
		||||
        this.mempoolPosition = txPosition.position;
 | 
			
		||||
        if (this.tx && !this.tx.status.confirmed) {
 | 
			
		||||
          this.stateService.markBlock$.next({
 | 
			
		||||
            mempoolPosition: this.mempoolPosition
 | 
			
		||||
          });
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        this.mempoolPosition = null;
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    this.subscription = this.route.paramMap
 | 
			
		||||
      .pipe(
 | 
			
		||||
        switchMap((params: ParamMap) => {
 | 
			
		||||
@ -342,6 +358,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
 | 
			
		||||
            if (tx.cpfpChecked) {
 | 
			
		||||
              this.stateService.markBlock$.next({
 | 
			
		||||
                txFeePerVSize: tx.effectiveFeePerVsize,
 | 
			
		||||
                mempoolPosition: this.mempoolPosition,
 | 
			
		||||
              });
 | 
			
		||||
              this.cpfpInfo = {
 | 
			
		||||
                ancestors: tx.ancestors,
 | 
			
		||||
@ -569,6 +586,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
 | 
			
		||||
    this.flowPrefSubscription.unsubscribe();
 | 
			
		||||
    this.urlFragmentSubscription.unsubscribe();
 | 
			
		||||
    this.mempoolBlocksSubscription.unsubscribe();
 | 
			
		||||
    this.mempoolPositionSubscription.unsubscribe();
 | 
			
		||||
    this.leaveTransaction();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -162,6 +162,10 @@ interface RbfTransaction extends TransactionStripped {
 | 
			
		||||
  rbf?: boolean;
 | 
			
		||||
  mined?: boolean,
 | 
			
		||||
}
 | 
			
		||||
export interface MempoolPosition {
 | 
			
		||||
  block: number,
 | 
			
		||||
  vsize: number,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface RewardStats {
 | 
			
		||||
  startBlock: number;
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,7 @@ import { Inject, Injectable, PLATFORM_ID, LOCALE_ID } from '@angular/core';
 | 
			
		||||
import { ReplaySubject, BehaviorSubject, Subject, fromEvent, Observable } from 'rxjs';
 | 
			
		||||
import { Transaction } from '../interfaces/electrs.interface';
 | 
			
		||||
import { IBackendInfo, MempoolBlock, MempoolBlockWithTransactions, MempoolBlockDelta, MempoolInfo, Recommendedfees, ReplacedTransaction, TransactionStripped } from '../interfaces/websocket.interface';
 | 
			
		||||
import { BlockExtended, DifficultyAdjustment, OptimizedMempoolStats, RbfTree } from '../interfaces/node-api.interface';
 | 
			
		||||
import { BlockExtended, DifficultyAdjustment, MempoolPosition, OptimizedMempoolStats, RbfTree } from '../interfaces/node-api.interface';
 | 
			
		||||
import { Router, NavigationStart } from '@angular/router';
 | 
			
		||||
import { isPlatformBrowser } from '@angular/common';
 | 
			
		||||
import { map, shareReplay } from 'rxjs/operators';
 | 
			
		||||
@ -12,6 +12,7 @@ interface MarkBlockState {
 | 
			
		||||
  blockHeight?: number;
 | 
			
		||||
  mempoolBlockIndex?: number;
 | 
			
		||||
  txFeePerVSize?: number;
 | 
			
		||||
  mempoolPosition?: MempoolPosition;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ILoadingIndicators { [name: string]: number; }
 | 
			
		||||
@ -105,6 +106,7 @@ export class StateService {
 | 
			
		||||
  utxoSpent$ = new Subject<object>();
 | 
			
		||||
  difficultyAdjustment$ = new ReplaySubject<DifficultyAdjustment>(1);
 | 
			
		||||
  mempoolTransactions$ = new Subject<Transaction>();
 | 
			
		||||
  mempoolTxPosition$ = new Subject<{ txid: string, position: MempoolPosition}>();
 | 
			
		||||
  blockTransactions$ = new Subject<Transaction>();
 | 
			
		||||
  isLoadingWebSocket$ = new ReplaySubject<boolean>(1);
 | 
			
		||||
  vbytesPerSecond$ = new ReplaySubject<number>(1);
 | 
			
		||||
 | 
			
		||||
@ -249,6 +249,10 @@ export class WebsocketService {
 | 
			
		||||
      this.stateService.mempoolTransactions$.next(response.tx);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (response['txPosition']) {
 | 
			
		||||
      this.stateService.mempoolTxPosition$.next(response['txPosition']);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (response.block) {
 | 
			
		||||
      if (response.block.height > this.stateService.latestBlockHeight) {
 | 
			
		||||
        this.stateService.updateChainTip(response.block.height);
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user