Merge pull request #3843 from mempool/mononaut/projected-fee-graph
Better projected fee graph
This commit is contained in:
		
						commit
						b5fdb6d64f
					
				@ -143,7 +143,7 @@ class MempoolBlocks {
 | 
				
			|||||||
          const stackWeight = transactionsSorted.slice(index).reduce((total, tx) => total + (tx.weight || 0), 0);
 | 
					          const stackWeight = transactionsSorted.slice(index).reduce((total, tx) => total + (tx.weight || 0), 0);
 | 
				
			||||||
          if (stackWeight > config.MEMPOOL.BLOCK_WEIGHT_UNITS) {
 | 
					          if (stackWeight > config.MEMPOOL.BLOCK_WEIGHT_UNITS) {
 | 
				
			||||||
            onlineStats = true;
 | 
					            onlineStats = true;
 | 
				
			||||||
            feeStatsCalculator = new OnlineFeeStatsCalculator(stackWeight, 0.5);
 | 
					            feeStatsCalculator = new OnlineFeeStatsCalculator(stackWeight, 0.5, [10, 20, 30, 40, 50, 60, 70, 80, 90]);
 | 
				
			||||||
            feeStatsCalculator.processNext(tx);
 | 
					            feeStatsCalculator.processNext(tx);
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -334,7 +334,7 @@ class MempoolBlocks {
 | 
				
			|||||||
    if (hasBlockStack) {
 | 
					    if (hasBlockStack) {
 | 
				
			||||||
      stackWeight = blocks[blocks.length - 1].reduce((total, tx) => total + (mempool[tx]?.weight || 0), 0);
 | 
					      stackWeight = blocks[blocks.length - 1].reduce((total, tx) => total + (mempool[tx]?.weight || 0), 0);
 | 
				
			||||||
      hasBlockStack = stackWeight > config.MEMPOOL.BLOCK_WEIGHT_UNITS;
 | 
					      hasBlockStack = stackWeight > config.MEMPOOL.BLOCK_WEIGHT_UNITS;
 | 
				
			||||||
      feeStatsCalculator = new OnlineFeeStatsCalculator(stackWeight, 0.5);
 | 
					      feeStatsCalculator = new OnlineFeeStatsCalculator(stackWeight, 0.5, [10, 20, 30, 40, 50, 60, 70, 80, 90]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const readyBlocks: { transactionIds, transactions, totalSize, totalWeight, totalFees, feeStats }[] = [];
 | 
					    const readyBlocks: { transactionIds, transactions, totalSize, totalWeight, totalFees, feeStats }[] = [];
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,9 @@
 | 
				
			|||||||
import { OnChanges } from '@angular/core';
 | 
					import { OnChanges } from '@angular/core';
 | 
				
			||||||
import { Component, Input, OnInit, ChangeDetectionStrategy } from '@angular/core';
 | 
					import { Component, Input, OnInit, ChangeDetectionStrategy } from '@angular/core';
 | 
				
			||||||
 | 
					import { TransactionStripped } from '../../interfaces/websocket.interface';
 | 
				
			||||||
 | 
					import { StateService } from '../../services/state.service';
 | 
				
			||||||
 | 
					import { VbytesPipe } from '../../shared/pipes/bytes-pipe/vbytes.pipe';
 | 
				
			||||||
 | 
					import { selectPowerOfTen } from '../../bitcoin.utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-fee-distribution-graph',
 | 
					  selector: 'app-fee-distribution-graph',
 | 
				
			||||||
@ -7,47 +11,121 @@ import { Component, Input, OnInit, ChangeDetectionStrategy } from '@angular/core
 | 
				
			|||||||
  changeDetection: ChangeDetectionStrategy.OnPush,
 | 
					  changeDetection: ChangeDetectionStrategy.OnPush,
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class FeeDistributionGraphComponent implements OnInit, OnChanges {
 | 
					export class FeeDistributionGraphComponent implements OnInit, OnChanges {
 | 
				
			||||||
  @Input() data: any;
 | 
					  @Input() feeRange: number[];
 | 
				
			||||||
 | 
					  @Input() vsize: number;
 | 
				
			||||||
 | 
					  @Input() transactions: TransactionStripped[];
 | 
				
			||||||
  @Input() height: number | string = 210;
 | 
					  @Input() height: number | string = 210;
 | 
				
			||||||
  @Input() top: number | string = 20;
 | 
					  @Input() top: number | string = 20;
 | 
				
			||||||
  @Input() right: number | string = 22;
 | 
					  @Input() right: number | string = 22;
 | 
				
			||||||
  @Input() left: number | string = 30;
 | 
					  @Input() left: number | string = 30;
 | 
				
			||||||
 | 
					  @Input() numSamples: number = 200;
 | 
				
			||||||
 | 
					  @Input() numLabels: number = 10;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  simple: boolean = false;
 | 
				
			||||||
 | 
					  data: number[][];
 | 
				
			||||||
 | 
					  labelInterval: number = 50;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  mempoolVsizeFeesOptions: any;
 | 
					  mempoolVsizeFeesOptions: any;
 | 
				
			||||||
  mempoolVsizeFeesInitOptions = {
 | 
					  mempoolVsizeFeesInitOptions = {
 | 
				
			||||||
    renderer: 'svg'
 | 
					    renderer: 'svg'
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor() { }
 | 
					  constructor(
 | 
				
			||||||
 | 
					    private stateService: StateService,
 | 
				
			||||||
 | 
					    private vbytesPipe: VbytesPipe,
 | 
				
			||||||
 | 
					  ) { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnInit() {
 | 
					  ngOnInit(): void {
 | 
				
			||||||
    this.mountChart();
 | 
					    this.mountChart();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnChanges() {
 | 
					  ngOnChanges(): void {
 | 
				
			||||||
 | 
					    this.simple = !!this.feeRange?.length;
 | 
				
			||||||
 | 
					    this.prepareChart();
 | 
				
			||||||
    this.mountChart();
 | 
					    this.mountChart();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  mountChart() {
 | 
					  prepareChart(): void {
 | 
				
			||||||
 | 
					    if (this.simple) {
 | 
				
			||||||
 | 
					      this.data = this.feeRange.map((rate, index) => [index * 10, rate]);
 | 
				
			||||||
 | 
					      this.labelInterval = 1;
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    this.data = [];
 | 
				
			||||||
 | 
					    if (!this.transactions?.length) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const samples = [];
 | 
				
			||||||
 | 
					    const txs = this.transactions.map(tx => { return { vsize: tx.vsize, rate: tx.rate || (tx.fee / tx.vsize) }; }).sort((a, b) => { return b.rate - a.rate; });
 | 
				
			||||||
 | 
					    const maxBlockVSize = this.stateService.env.BLOCK_WEIGHT_UNITS / 4;
 | 
				
			||||||
 | 
					    const sampleInterval = maxBlockVSize / this.numSamples;
 | 
				
			||||||
 | 
					    let cumVSize = 0;
 | 
				
			||||||
 | 
					    let sampleIndex = 0;
 | 
				
			||||||
 | 
					    let nextSample = 0;
 | 
				
			||||||
 | 
					    let txIndex = 0;
 | 
				
			||||||
 | 
					    this.labelInterval = this.numSamples / this.numLabels;
 | 
				
			||||||
 | 
					    while (nextSample <= maxBlockVSize) {
 | 
				
			||||||
 | 
					      if (txIndex >= txs.length) {
 | 
				
			||||||
 | 
					        samples.push([(1 - (sampleIndex / this.numSamples)) * 100, 0]);
 | 
				
			||||||
 | 
					        nextSample += sampleInterval;
 | 
				
			||||||
 | 
					        sampleIndex++;
 | 
				
			||||||
 | 
					        continue;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      while (txs[txIndex] && nextSample < cumVSize + txs[txIndex].vsize) {
 | 
				
			||||||
 | 
					        samples.push([(1 - (sampleIndex / this.numSamples)) * 100, txs[txIndex].rate]);
 | 
				
			||||||
 | 
					        nextSample += sampleInterval;
 | 
				
			||||||
 | 
					        sampleIndex++;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      cumVSize += txs[txIndex].vsize;
 | 
				
			||||||
 | 
					      txIndex++;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    this.data = samples.reverse();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  mountChart(): void {
 | 
				
			||||||
    this.mempoolVsizeFeesOptions = {
 | 
					    this.mempoolVsizeFeesOptions = {
 | 
				
			||||||
      grid: {
 | 
					      grid: {
 | 
				
			||||||
        height: '210',
 | 
					        height: '210',
 | 
				
			||||||
        right: '20',
 | 
					        right: '20',
 | 
				
			||||||
        top: '22',
 | 
					        top: '22',
 | 
				
			||||||
        left: '30',
 | 
					        left: '40',
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      xAxis: {
 | 
					      xAxis: {
 | 
				
			||||||
        type: 'category',
 | 
					        type: 'category',
 | 
				
			||||||
        boundaryGap: false,
 | 
					        boundaryGap: false,
 | 
				
			||||||
 | 
					        name: '% Weight',
 | 
				
			||||||
 | 
					        nameLocation: 'middle',
 | 
				
			||||||
 | 
					        nameGap: 0,
 | 
				
			||||||
 | 
					        nameTextStyle: {
 | 
				
			||||||
 | 
					          verticalAlign: 'top',
 | 
				
			||||||
 | 
					          padding: [30, 0, 0, 0],
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        axisLabel: {
 | 
				
			||||||
 | 
					          interval: (index: number): boolean => { return index && (index % this.labelInterval === 0); },
 | 
				
			||||||
 | 
					          formatter: (value: number): string => { return Number(value).toFixed(0); },
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        axisTick: {
 | 
				
			||||||
 | 
					          interval: (index:number): boolean => { return (index % this.labelInterval === 0); },
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      yAxis: {
 | 
					      yAxis: {
 | 
				
			||||||
        type: 'value',
 | 
					        type: 'value',
 | 
				
			||||||
 | 
					        // name: 'Effective Fee Rate s/vb',
 | 
				
			||||||
 | 
					        // nameLocation: 'middle',
 | 
				
			||||||
        splitLine: {
 | 
					        splitLine: {
 | 
				
			||||||
          lineStyle: {
 | 
					          lineStyle: {
 | 
				
			||||||
            type: 'dotted',
 | 
					            type: 'dotted',
 | 
				
			||||||
            color: '#ffffff66',
 | 
					            color: '#ffffff66',
 | 
				
			||||||
            opacity: 0.25,
 | 
					            opacity: 0.25,
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        axisLabel: {
 | 
				
			||||||
 | 
					          formatter: (value: number): string => {
 | 
				
			||||||
 | 
					            const selectedPowerOfTen = selectPowerOfTen(value);
 | 
				
			||||||
 | 
					            const newVal = Math.round(value / selectedPowerOfTen.divider);
 | 
				
			||||||
 | 
					            return `${newVal}${selectedPowerOfTen.unit}`;
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      series: [{
 | 
					      series: [{
 | 
				
			||||||
@ -58,14 +136,18 @@ export class FeeDistributionGraphComponent implements OnInit, OnChanges {
 | 
				
			|||||||
          position: 'top',
 | 
					          position: 'top',
 | 
				
			||||||
          color: '#ffffff',
 | 
					          color: '#ffffff',
 | 
				
			||||||
          textShadowBlur: 0,
 | 
					          textShadowBlur: 0,
 | 
				
			||||||
          formatter: (label: any) => {
 | 
					          formatter: (label: { data: number[] }): string => {
 | 
				
			||||||
            return Math.floor(label.data);
 | 
					            const value = label.data[1];
 | 
				
			||||||
 | 
					            const selectedPowerOfTen = selectPowerOfTen(value);
 | 
				
			||||||
 | 
					            const newVal = Math.round(value / selectedPowerOfTen.divider);
 | 
				
			||||||
 | 
					            return `${newVal}${selectedPowerOfTen.unit}`;
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        showAllSymbol: false,
 | 
				
			||||||
        smooth: true,
 | 
					        smooth: true,
 | 
				
			||||||
        lineStyle: {
 | 
					        lineStyle: {
 | 
				
			||||||
          color: '#D81B60',
 | 
					          color: '#D81B60',
 | 
				
			||||||
          width: 4,
 | 
					          width: 1,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        itemStyle: {
 | 
					        itemStyle: {
 | 
				
			||||||
          color: '#b71c1c',
 | 
					          color: '#b71c1c',
 | 
				
			||||||
 | 
				
			|||||||
@ -39,11 +39,11 @@
 | 
				
			|||||||
            </tr>
 | 
					            </tr>
 | 
				
			||||||
          </tbody>
 | 
					          </tbody>
 | 
				
			||||||
        </table>
 | 
					        </table>
 | 
				
			||||||
        <app-fee-distribution-graph *ngIf="webGlEnabled" [data]="mempoolBlock.feeRange" ></app-fee-distribution-graph>
 | 
					        <app-fee-distribution-graph *ngIf="webGlEnabled" [transactions]="mempoolBlockTransactions$ | async" [feeRange]="mempoolBlock.isStack ? mempoolBlock.feeRange : []" [vsize]="mempoolBlock.blockVSize" ></app-fee-distribution-graph>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
      <div class="col-md chart-container">
 | 
					      <div class="col-md chart-container">
 | 
				
			||||||
        <app-mempool-block-overview *ngIf="webGlEnabled" [index]="mempoolBlockIndex" (txPreviewEvent)="setTxPreview($event)"></app-mempool-block-overview>
 | 
					        <app-mempool-block-overview *ngIf="webGlEnabled" [index]="mempoolBlockIndex" (txPreviewEvent)="setTxPreview($event)"></app-mempool-block-overview>
 | 
				
			||||||
        <app-fee-distribution-graph *ngIf="!webGlEnabled" [data]="mempoolBlock.feeRange" ></app-fee-distribution-graph>
 | 
					        <app-fee-distribution-graph *ngIf="!webGlEnabled" [transactions]="mempoolBlockTransactions$ | async" [feeRange]="mempoolBlock.isStack ? mempoolBlock.feeRange : []" [vsize]="mempoolBlock.blockVSize" ></app-fee-distribution-graph>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
 | 
				
			|||||||
@ -17,6 +17,7 @@ export class MempoolBlockComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
  network$: Observable<string>;
 | 
					  network$: Observable<string>;
 | 
				
			||||||
  mempoolBlockIndex: number;
 | 
					  mempoolBlockIndex: number;
 | 
				
			||||||
  mempoolBlock$: Observable<MempoolBlock>;
 | 
					  mempoolBlock$: Observable<MempoolBlock>;
 | 
				
			||||||
 | 
					  mempoolBlockTransactions$: Observable<TransactionStripped[]>;
 | 
				
			||||||
  ordinal$: BehaviorSubject<string> = new BehaviorSubject('');
 | 
					  ordinal$: BehaviorSubject<string> = new BehaviorSubject('');
 | 
				
			||||||
  previewTx: TransactionStripped | void;
 | 
					  previewTx: TransactionStripped | void;
 | 
				
			||||||
  webGlEnabled: boolean;
 | 
					  webGlEnabled: boolean;
 | 
				
			||||||
@ -53,6 +54,7 @@ export class MempoolBlockComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
                const ordinal = this.getOrdinal(mempoolBlocks[this.mempoolBlockIndex]);
 | 
					                const ordinal = this.getOrdinal(mempoolBlocks[this.mempoolBlockIndex]);
 | 
				
			||||||
                this.ordinal$.next(ordinal);
 | 
					                this.ordinal$.next(ordinal);
 | 
				
			||||||
                this.seoService.setTitle(ordinal);
 | 
					                this.seoService.setTitle(ordinal);
 | 
				
			||||||
 | 
					                mempoolBlocks[this.mempoolBlockIndex].isStack = mempoolBlocks[this.mempoolBlockIndex].blockVSize > this.stateService.blockVSize;
 | 
				
			||||||
                return mempoolBlocks[this.mempoolBlockIndex];
 | 
					                return mempoolBlocks[this.mempoolBlockIndex];
 | 
				
			||||||
              })
 | 
					              })
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
@ -62,6 +64,8 @@ export class MempoolBlockComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
        })
 | 
					        })
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.mempoolBlockTransactions$ = this.stateService.liveMempoolBlockTransactions$.pipe(map(txMap => Object.values(txMap)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.network$ = this.stateService.networkChanged$;
 | 
					    this.network$ = this.stateService.networkChanged$;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,11 +1,11 @@
 | 
				
			|||||||
import { Inject, Injectable, PLATFORM_ID, LOCALE_ID } from '@angular/core';
 | 
					import { Inject, Injectable, PLATFORM_ID, LOCALE_ID } from '@angular/core';
 | 
				
			||||||
import { ReplaySubject, BehaviorSubject, Subject, fromEvent, Observable } from 'rxjs';
 | 
					import { ReplaySubject, BehaviorSubject, Subject, fromEvent, Observable, merge } from 'rxjs';
 | 
				
			||||||
import { Transaction } from '../interfaces/electrs.interface';
 | 
					import { Transaction } from '../interfaces/electrs.interface';
 | 
				
			||||||
import { IBackendInfo, MempoolBlock, MempoolBlockWithTransactions, MempoolBlockDelta, MempoolInfo, Recommendedfees, ReplacedTransaction, TransactionStripped } from '../interfaces/websocket.interface';
 | 
					import { IBackendInfo, MempoolBlock, MempoolBlockWithTransactions, MempoolBlockDelta, MempoolInfo, Recommendedfees, ReplacedTransaction, TransactionStripped } from '../interfaces/websocket.interface';
 | 
				
			||||||
import { BlockExtended, DifficultyAdjustment, MempoolPosition, 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 { Router, NavigationStart } from '@angular/router';
 | 
				
			||||||
import { isPlatformBrowser } from '@angular/common';
 | 
					import { isPlatformBrowser } from '@angular/common';
 | 
				
			||||||
import { map, shareReplay } from 'rxjs/operators';
 | 
					import { map, scan, shareReplay, tap } from 'rxjs/operators';
 | 
				
			||||||
import { StorageService } from './storage.service';
 | 
					import { StorageService } from './storage.service';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface MarkBlockState {
 | 
					interface MarkBlockState {
 | 
				
			||||||
@ -100,6 +100,7 @@ export class StateService {
 | 
				
			|||||||
  mempoolBlocks$ = new ReplaySubject<MempoolBlock[]>(1);
 | 
					  mempoolBlocks$ = new ReplaySubject<MempoolBlock[]>(1);
 | 
				
			||||||
  mempoolBlockTransactions$ = new Subject<TransactionStripped[]>();
 | 
					  mempoolBlockTransactions$ = new Subject<TransactionStripped[]>();
 | 
				
			||||||
  mempoolBlockDelta$ = new Subject<MempoolBlockDelta>();
 | 
					  mempoolBlockDelta$ = new Subject<MempoolBlockDelta>();
 | 
				
			||||||
 | 
					  liveMempoolBlockTransactions$: Observable<{ [txid: string]: TransactionStripped}>;
 | 
				
			||||||
  txReplaced$ = new Subject<ReplacedTransaction>();
 | 
					  txReplaced$ = new Subject<ReplacedTransaction>();
 | 
				
			||||||
  txRbfInfo$ = new Subject<RbfTree>();
 | 
					  txRbfInfo$ = new Subject<RbfTree>();
 | 
				
			||||||
  rbfLatest$ = new Subject<RbfTree[]>();
 | 
					  rbfLatest$ = new Subject<RbfTree[]>();
 | 
				
			||||||
@ -166,6 +167,30 @@ export class StateService {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    this.blocks$ = new ReplaySubject<[BlockExtended, string]>(this.env.KEEP_BLOCKS_AMOUNT);
 | 
					    this.blocks$ = new ReplaySubject<[BlockExtended, string]>(this.env.KEEP_BLOCKS_AMOUNT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.liveMempoolBlockTransactions$ = merge(
 | 
				
			||||||
 | 
					      this.mempoolBlockTransactions$.pipe(map(transactions => { return { transactions }; })),
 | 
				
			||||||
 | 
					      this.mempoolBlockDelta$.pipe(map(delta => { return { delta }; })),
 | 
				
			||||||
 | 
					    ).pipe(scan((transactions: { [txid: string]: TransactionStripped }, change: any): { [txid: string]: TransactionStripped } => {
 | 
				
			||||||
 | 
					      if (change.transactions) {
 | 
				
			||||||
 | 
					        const txMap = {}
 | 
				
			||||||
 | 
					        change.transactions.forEach(tx => {
 | 
				
			||||||
 | 
					          txMap[tx.txid] = tx;
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        return txMap;
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        change.delta.changed.forEach(tx => {
 | 
				
			||||||
 | 
					          transactions[tx.txid].rate = tx.rate;
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        change.delta.removed.forEach(txid => {
 | 
				
			||||||
 | 
					          delete transactions[txid];
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        change.delta.added.forEach(tx => {
 | 
				
			||||||
 | 
					          transactions[tx.txid] = tx;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        return transactions;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }, {}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (this.env.BASE_MODULE === 'bisq') {
 | 
					    if (this.env.BASE_MODULE === 'bisq') {
 | 
				
			||||||
      this.network = this.env.BASE_MODULE;
 | 
					      this.network = this.env.BASE_MODULE;
 | 
				
			||||||
      this.networkChanged$.next(this.env.BASE_MODULE);
 | 
					      this.networkChanged$.next(this.env.BASE_MODULE);
 | 
				
			||||||
 | 
				
			|||||||
@ -500,7 +500,7 @@ html:lang(ru) .card-title {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.fee-distribution-chart {
 | 
					.fee-distribution-chart {
 | 
				
			||||||
  height: 250px;
 | 
					  height: 265px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.fees-wrapper-tooltip-chart {
 | 
					.fees-wrapper-tooltip-chart {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user