From f87bf4df771ff82f144b610b5fe6636baf2d8faf Mon Sep 17 00:00:00 2001 From: bennyhodl Date: Wed, 28 Jun 2023 18:40:57 -0400 Subject: [PATCH 01/11] bennyhodl contributor agreement --- contributors/bennyhodl.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 contributors/bennyhodl.txt diff --git a/contributors/bennyhodl.txt b/contributors/bennyhodl.txt new file mode 100644 index 000000000..c5f779de6 --- /dev/null +++ b/contributors/bennyhodl.txt @@ -0,0 +1,3 @@ +I hereby accept the terms of the Contributor License Agreement in the CONTRIBUTING.md file of the mempool/mempool git repository as of June 28, 2023. + +Signed: bennyhodl From 8477600859f50748e5803d6cdb06b928bffa1d61 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Fri, 14 Jul 2023 11:05:09 +0900 Subject: [PATCH 02/11] Fix difficulty chart bug --- .../app/components/hashrate-chart/hashrate-chart.component.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts index d562375b8..62cc71ca6 100644 --- a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts +++ b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts @@ -130,6 +130,7 @@ export class HashrateChartComponent implements OnInit { }); ++hashIndex; } + diffIndex++; break; } From e9c618849d58d1f6a5feae414100b12b929d6290 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Wed, 12 Jul 2023 10:11:04 +0900 Subject: [PATCH 03/11] Highlight matching transactions in the block visualizations --- .../block-overview-graph.component.ts | 29 ++++++++++++++ .../block-overview-graph/block-scene.ts | 4 ++ .../block-overview-graph/tx-view.ts | 39 ++++++++++++++++++- .../search-form/search-form.component.ts | 3 ++ frontend/src/app/services/state.service.ts | 1 + 5 files changed, 74 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts b/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts index 15e41f1a7..583f9875c 100644 --- a/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts +++ b/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts @@ -6,6 +6,8 @@ import TxSprite from './tx-sprite'; import TxView from './tx-view'; import { Position } from './sprite-types'; import { Price } from '../../services/price.service'; +import { StateService } from '../../services/state.service'; +import { Subscription } from 'rxjs'; @Component({ selector: 'app-block-overview-graph', @@ -44,16 +46,25 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On scene: BlockScene; hoverTx: TxView | void; selectedTx: TxView | void; + highlightTx: TxView | void; mirrorTx: TxView | void; tooltipPosition: Position; readyNextFrame = false; + searchText: string; + searchSubscription: Subscription; + constructor( readonly ngZone: NgZone, readonly elRef: ElementRef, + private stateService: StateService, ) { this.vertexArray = new FastVertexArray(512, TxSprite.dataSize); + this.searchSubscription = this.stateService.searchText$.subscribe((text) => { + this.searchText = text; + this.updateSearchHighlight(); + }); } ngAfterViewInit(): void { @@ -109,6 +120,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On this.scene.setup(transactions); this.readyNextFrame = true; this.start(); + this.updateSearchHighlight(); } } @@ -116,6 +128,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On if (this.scene) { this.scene.enter(transactions, direction); this.start(); + this.updateSearchHighlight(); } } @@ -123,6 +136,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On if (this.scene) { this.scene.exit(direction); this.start(); + this.updateSearchHighlight(); } } @@ -130,6 +144,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On if (this.scene) { this.scene.replace(transactions || [], direction, sort); this.start(); + this.updateSearchHighlight(); } } @@ -137,6 +152,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On if (this.scene) { this.scene.update(add, remove, change, direction, resetLayout); this.start(); + this.updateSearchHighlight(); } } @@ -406,6 +422,19 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On } } + updateSearchHighlight(): void { + if (this.highlightTx && this.highlightTx.txid !== this.searchText && this.scene) { + this.scene.setHighlight(this.highlightTx, false); + this.start(); + } else if (this.searchText && this.searchText.length === 64) { + this.highlightTx = this.scene.txs[this.searchText]; + if (this.highlightTx) { + this.scene.setHighlight(this.highlightTx, true); + this.start(); + } + } + } + setHighlightingEnabled(enabled: boolean): void { if (this.scene) { this.scene.setHighlighting(enabled); diff --git a/frontend/src/app/components/block-overview-graph/block-scene.ts b/frontend/src/app/components/block-overview-graph/block-scene.ts index 0cd5c9391..e7241141d 100644 --- a/frontend/src/app/components/block-overview-graph/block-scene.ts +++ b/frontend/src/app/components/block-overview-graph/block-scene.ts @@ -215,6 +215,10 @@ export default class BlockScene { this.animateUntil = Math.max(this.animateUntil, tx.setHover(value)); } + setHighlight(tx: TxView, value: boolean): void { + this.animateUntil = Math.max(this.animateUntil, tx.setHighlight(value)); + } + private init({ width, height, resolution, blockLimit, orientation, flip, vertexArray, highlighting, pixelAlign }: { width: number, height: number, resolution: number, blockLimit: number, orientation: string, flip: boolean, vertexArray: FastVertexArray, highlighting: boolean, pixelAlign: boolean } diff --git a/frontend/src/app/components/block-overview-graph/tx-view.ts b/frontend/src/app/components/block-overview-graph/tx-view.ts index 7d3e0ee13..77f5a182a 100644 --- a/frontend/src/app/components/block-overview-graph/tx-view.ts +++ b/frontend/src/app/components/block-overview-graph/tx-view.ts @@ -7,6 +7,7 @@ import BlockScene from './block-scene'; const hoverTransitionTime = 300; const defaultHoverColor = hexToColor('1bd8f4'); +const defaultHighlightColor = hexToColor('800080'); const feeColors = mempoolFeeColors.map(hexToColor); const auditFeeColors = feeColors.map((color) => darken(desaturate(color, 0.3), 0.9)); @@ -44,8 +45,10 @@ export default class TxView implements TransactionStripped { initialised: boolean; vertexArray: FastVertexArray; hover: boolean; + highlight: boolean; sprite: TxSprite; hoverColor: Color | void; + highlightColor: Color | void; screenPosition: Square; gridPosition: Square | void; @@ -150,8 +153,40 @@ export default class TxView implements TransactionStripped { } else { this.hover = false; this.hoverColor = null; - if (this.sprite) { - this.sprite.resume(hoverTransitionTime); + if (this.highlight) { + this.setHighlight(true, this.highlightColor); + } else { + if (this.sprite) { + this.sprite.resume(hoverTransitionTime); + } + } + } + this.dirty = false; + return performance.now() + hoverTransitionTime; + } + + // Temporarily override the tx color + // returns minimum transition end time + setHighlight(highlightOn: boolean, color: Color | void = defaultHighlightColor): number { + if (highlightOn) { + this.highlight = true; + this.highlightColor = color; + + this.sprite.update({ + ...this.highlightColor, + duration: hoverTransitionTime, + adjust: false, + temp: true + }); + } else { + this.highlight = false; + this.highlightColor = null; + if (this.hover) { + this.setHover(true, this.hoverColor); + } else { + if (this.sprite) { + this.sprite.resume(hoverTransitionTime); + } } } this.dirty = false; diff --git a/frontend/src/app/components/search-form/search-form.component.ts b/frontend/src/app/components/search-form/search-form.component.ts index 422cb2f45..ab42fe1f7 100644 --- a/frontend/src/app/components/search-form/search-form.component.ts +++ b/frontend/src/app/components/search-form/search-form.component.ts @@ -80,6 +80,9 @@ export class SearchFormComponent implements OnInit { } return text.trim(); }), + tap((text) => { + this.stateService.searchText$.next(text); + }), distinctUntilChanged(), ); diff --git a/frontend/src/app/services/state.service.ts b/frontend/src/app/services/state.service.ts index f38600605..16252a9ec 100644 --- a/frontend/src/app/services/state.service.ts +++ b/frontend/src/app/services/state.service.ts @@ -129,6 +129,7 @@ export class StateService { markBlock$ = new BehaviorSubject({}); keyNavigation$ = new Subject(); + searchText$ = new BehaviorSubject(''); blockScrolling$: Subject = new Subject(); resetScroll$: Subject = new Subject(); From d60709deff25b04572e1475a26b337ec93c4f3bf Mon Sep 17 00:00:00 2001 From: Mononaut Date: Fri, 14 Jul 2023 11:21:23 +0900 Subject: [PATCH 04/11] Fix scene null check on visualization load --- .../block-overview-graph/block-overview-graph.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts b/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts index 583f9875c..cc9934af8 100644 --- a/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts +++ b/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts @@ -426,7 +426,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On if (this.highlightTx && this.highlightTx.txid !== this.searchText && this.scene) { this.scene.setHighlight(this.highlightTx, false); this.start(); - } else if (this.searchText && this.searchText.length === 64) { + } else if (this.scene?.txs && this.searchText && this.searchText.length === 64) { this.highlightTx = this.scene.txs[this.searchText]; if (this.highlightTx) { this.scene.setHighlight(this.highlightTx, true); From 756fac7270c50fa88ccf6f71bb9389cf5e5a0da4 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Fri, 14 Jul 2023 11:52:07 +0900 Subject: [PATCH 05/11] Switch "latest blocks" to "latest replacements" --- .../app/dashboard/dashboard.component.html | 39 +++++------ .../app/dashboard/dashboard.component.scss | 46 ++++++------ .../src/app/dashboard/dashboard.component.ts | 70 ++++++++++++++----- 3 files changed, 88 insertions(+), 67 deletions(-) diff --git a/frontend/src/app/dashboard/dashboard.component.html b/frontend/src/app/dashboard/dashboard.component.html index 620678a28..90ec01e1d 100644 --- a/frontend/src/app/dashboard/dashboard.component.html +++ b/frontend/src/app/dashboard/dashboard.component.html @@ -75,36 +75,31 @@
- -
Latest blocks
+
+
Latest Replacements
 
- +
- - - - - + + + + - - - - + - - + + diff --git a/frontend/src/app/dashboard/dashboard.component.scss b/frontend/src/app/dashboard/dashboard.component.scss index eb466fc16..5633a3c7e 100644 --- a/frontend/src/app/dashboard/dashboard.component.scss +++ b/frontend/src/app/dashboard/dashboard.component.scss @@ -175,40 +175,34 @@ height: 18px; } -.lastest-blocks-table { +.lastest-replacements-table { width: 100%; text-align: left; + table-layout:fixed; tr, td, th { border: 0px; - padding-top: 0.65rem !important; - padding-bottom: 0.7rem !important; + padding-top: 0.71rem !important; + padding-bottom: 0.75rem !important; } - .table-cell-height { - width: 15%; + td { + overflow:hidden; + width: 25%; } - .table-cell-mined { - width: 35%; - text-align: left; + .table-cell-txid { + width: 33%; + text-align: start; } - .table-cell-transaction-count { - display: none; - text-align: right; - width: 20%; - display: table-cell; + .table-cell-old-fee { + width: 33%; + text-align: end; } - .table-cell-size { - display: none; - text-align: center; - width: 30%; - @media (min-width: 485px) { - display: table-cell; - } - @media (min-width: 768px) { - display: none; - } - @media (min-width: 992px) { - display: table-cell; - } + .table-cell-new-fee { + width: 33%; + text-align: end; + } + .table-cell-badges { + width: 25%; + text-align: end; } } diff --git a/frontend/src/app/dashboard/dashboard.component.ts b/frontend/src/app/dashboard/dashboard.component.ts index 6cf487be6..4ef4501aa 100644 --- a/frontend/src/app/dashboard/dashboard.component.ts +++ b/frontend/src/app/dashboard/dashboard.component.ts @@ -1,7 +1,7 @@ import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core'; import { combineLatest, merge, Observable, of, Subscription } from 'rxjs'; import { filter, map, scan, share, switchMap, tap } from 'rxjs/operators'; -import { BlockExtended, OptimizedMempoolStats } from '../interfaces/node-api.interface'; +import { BlockExtended, OptimizedMempoolStats, RbfTree } from '../interfaces/node-api.interface'; import { MempoolInfo, TransactionStripped } from '../interfaces/websocket.interface'; import { ApiService } from '../services/api.service'; import { StateService } from '../services/state.service'; @@ -25,6 +25,17 @@ interface MempoolStatsData { weightPerSecond: any; } +interface ReplacementInfo { + tree: RbfTree; + mined: boolean; + fullRbf: boolean; + txid: string; + oldFee: number; + oldVsize: number; + newFee: number; + newVsize: number; +} + @Component({ selector: 'app-dashboard', templateUrl: './dashboard.component.html', @@ -38,8 +49,8 @@ export class DashboardComponent implements OnInit, OnDestroy { mempoolInfoData$: Observable; mempoolLoadingStatus$: Observable; vBytesPerSecondLimit = 1667; - blocks$: Observable; transactions$: Observable; + replacements$: Observable; latestBlockHeight: number; mempoolTransactionsWeightPerSecondData: any; mempoolStats$: Observable; @@ -64,6 +75,7 @@ export class DashboardComponent implements OnInit, OnDestroy { this.isLoadingWebSocket$ = this.stateService.isLoadingWebSocket$; this.seoService.resetTitle(); this.websocketService.want(['blocks', 'stats', 'mempool-blocks', 'live-2h-chart']); + this.websocketService.startTrackRbf('all'); this.network$ = merge(of(''), this.stateService.networkChanged$); this.mempoolLoadingStatus$ = this.stateService.loadingIndicators$ .pipe( @@ -130,23 +142,6 @@ export class DashboardComponent implements OnInit, OnDestroy { }), ); - this.blocks$ = this.stateService.blocks$ - .pipe( - tap((blocks) => { - this.latestBlockHeight = blocks[0].height; - }), - switchMap((blocks) => { - if (this.stateService.env.MINING_DASHBOARD === true) { - for (const block of blocks) { - // @ts-ignore: Need to add an extra field for the template - block.extras.pool.logo = `/resources/mining-pools/` + - block.extras.pool.name.toLowerCase().replace(' ', '').replace('.', '') + '.svg'; - } - } - return of(blocks.slice(0, 6)); - }) - ); - this.transactions$ = this.stateService.transactions$ .pipe( scan((acc, tx) => { @@ -159,6 +154,31 @@ export class DashboardComponent implements OnInit, OnDestroy { }, []), ); + this.replacements$ = this.stateService.rbfLatest$.pipe( + switchMap((rbfList) => { + const replacements = rbfList.slice(0, 6).map(rbfTree => { + let oldFee = 0; + let oldVsize = 0; + for (const replaced of rbfTree.replaces) { + oldFee += replaced.tx.fee; + oldVsize += replaced.tx.vsize; + } + this.checkFullRbf(rbfTree); + return { + tree: rbfTree, + txid: rbfTree.tx.txid, + mined: rbfTree.tx.mined, + fullRbf: rbfTree.tx.fullRbf, + oldFee, + oldVsize, + newFee: rbfTree.tx.fee, + newVsize: rbfTree.tx.vsize, + }; + }); + return of(replacements); + }) + ); + this.mempoolStats$ = this.stateService.connectionState$ .pipe( filter((state) => state === 2), @@ -219,4 +239,16 @@ export class DashboardComponent implements OnInit, OnDestroy { trackByBlock(index: number, block: BlockExtended) { return block.height; } + + checkFullRbf(tree: RbfTree): void { + let fullRbf = false; + for (const replaced of tree.replaces) { + if (!replaced.tx.rbf) { + fullRbf = true; + } + replaced.replacedBy = tree.tx; + this.checkFullRbf(replaced); + } + tree.tx.fullRbf = fullRbf; + } } From 240afbed955604f00a11eb07c1d531d69825a2e1 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Fri, 14 Jul 2023 12:11:14 +0900 Subject: [PATCH 06/11] adjust latest replacements labels & layout --- .../src/app/dashboard/dashboard.component.html | 6 +++--- .../src/app/dashboard/dashboard.component.scss | 18 ++++++++++++++---- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/frontend/src/app/dashboard/dashboard.component.html b/frontend/src/app/dashboard/dashboard.component.html index 90ec01e1d..3faef5a83 100644 --- a/frontend/src/app/dashboard/dashboard.component.html +++ b/frontend/src/app/dashboard/dashboard.component.html @@ -76,16 +76,16 @@
HeightMinedPoolTXsSizeTXIDOld feeNew fee
{{ block.height }} - - - {{ block.extras.pool.name }} +
+ + {{ block.tx_count | number }} -
-
 
-
-
+
+ Mined + Full RBF + RBF
- + - + diff --git a/frontend/src/app/dashboard/dashboard.component.scss b/frontend/src/app/dashboard/dashboard.component.scss index 5633a3c7e..f1e835d9c 100644 --- a/frontend/src/app/dashboard/dashboard.component.scss +++ b/frontend/src/app/dashboard/dashboard.component.scss @@ -189,20 +189,30 @@ width: 25%; } .table-cell-txid { - width: 33%; + width: 25%; text-align: start; } .table-cell-old-fee { - width: 33%; + width: 25%; text-align: end; + + @media(max-width: 1080px) { + display: none; + } } .table-cell-new-fee { - width: 33%; + width: 20%; text-align: end; } .table-cell-badges { - width: 25%; + width: 23%; + padding-right: 0; + padding-left: 5px; text-align: end; + + .badge { + margin-left: 5px; + } } } From 3cca6f6b8bb867faaec12e819652029751bedb4e Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sun, 2 Apr 2023 06:54:42 +0900 Subject: [PATCH 07/11] Pixel-aligned grids for sharper block visualizations --- .../block-overview-graph.component.html | 28 ++++++++++--------- .../block-overview-graph.component.scss | 8 ++++++ .../block/block-preview.component.html | 2 +- .../block/block-preview.component.scss | 4 +-- .../app/components/block/block.component.html | 6 ++-- .../mempool-block-overview.component.html | 2 +- 6 files changed, 30 insertions(+), 20 deletions(-) diff --git a/frontend/src/app/components/block-overview-graph/block-overview-graph.component.html b/frontend/src/app/components/block-overview-graph/block-overview-graph.component.html index 2a357843b..a625a0385 100644 --- a/frontend/src/app/components/block-overview-graph/block-overview-graph.component.html +++ b/frontend/src/app/components/block-overview-graph/block-overview-graph.component.html @@ -1,15 +1,17 @@ -
- -
-
-
not available
-
- +
+
+ +
+
+
not available
+
+ +
diff --git a/frontend/src/app/components/block-overview-graph/block-overview-graph.component.scss b/frontend/src/app/components/block-overview-graph/block-overview-graph.component.scss index 58b53aebf..d30dd3305 100644 --- a/frontend/src/app/components/block-overview-graph/block-overview-graph.component.scss +++ b/frontend/src/app/components/block-overview-graph/block-overview-graph.component.scss @@ -6,8 +6,16 @@ display: flex; justify-content: center; align-items: center; + grid-column: 1/-1; } +.grid-align { + position: relative; + width: 100%; + display: grid; + grid-template-columns: repeat(auto-fit, 75px); + justify-content: center; +} .block-overview-canvas { position: absolute; diff --git a/frontend/src/app/components/block/block-preview.component.html b/frontend/src/app/components/block/block-preview.component.html index 83962857c..0c33246a7 100644 --- a/frontend/src/app/components/block/block-preview.component.html +++ b/frontend/src/app/components/block/block-preview.component.html @@ -71,7 +71,7 @@

Expected Block beta

- @@ -239,7 +239,7 @@

Actual Block

- diff --git a/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.html b/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.html index 37c82afad..7d5ddec30 100644 --- a/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.html +++ b/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.html @@ -1,7 +1,7 @@ Date: Fri, 14 Jul 2023 14:44:15 +0900 Subject: [PATCH 08/11] tweak default sizes & resolutions --- .../block-overview-graph.component.ts | 3 +- .../block-overview-graph/block-scene.ts | 30 ++++++------------- .../app/components/block/block.component.html | 8 ++--- .../app/components/block/block.component.scss | 4 +++ .../app/components/clock/clock.component.html | 2 +- .../mempool-block-overview.component.html | 3 +- .../mempool-block-overview.component.ts | 1 - 7 files changed, 20 insertions(+), 31 deletions(-) diff --git a/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts b/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts index cc9934af8..49da16d55 100644 --- a/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts +++ b/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts @@ -25,7 +25,6 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On @Input() unavailable: boolean = false; @Input() auditHighlighting: boolean = false; @Input() blockConversion: Price; - @Input() pixelAlign: boolean = false; @Output() txClickEvent = new EventEmitter<{ tx: TransactionStripped, keyModifier: boolean}>(); @Output() txHoverEvent = new EventEmitter(); @Output() readyEvent = new EventEmitter(); @@ -219,7 +218,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On } else { this.scene = new BlockScene({ width: this.displayWidth, height: this.displayHeight, resolution: this.resolution, blockLimit: this.blockLimit, orientation: this.orientation, flip: this.flip, vertexArray: this.vertexArray, - highlighting: this.auditHighlighting, pixelAlign: this.pixelAlign }); + highlighting: this.auditHighlighting }); this.start(); } } diff --git a/frontend/src/app/components/block-overview-graph/block-scene.ts b/frontend/src/app/components/block-overview-graph/block-scene.ts index e7241141d..510803f03 100644 --- a/frontend/src/app/components/block-overview-graph/block-scene.ts +++ b/frontend/src/app/components/block-overview-graph/block-scene.ts @@ -15,7 +15,6 @@ export default class BlockScene { gridWidth: number; gridHeight: number; gridSize: number; - pixelAlign: boolean; vbytesPerUnit: number; unitPadding: number; unitWidth: number; @@ -24,24 +23,19 @@ export default class BlockScene { animateUntil = 0; dirty: boolean; - constructor({ width, height, resolution, blockLimit, orientation, flip, vertexArray, highlighting, pixelAlign }: + constructor({ width, height, resolution, blockLimit, orientation, flip, vertexArray, highlighting }: { width: number, height: number, resolution: number, blockLimit: number, - orientation: string, flip: boolean, vertexArray: FastVertexArray, highlighting: boolean, pixelAlign: boolean } + orientation: string, flip: boolean, vertexArray: FastVertexArray, highlighting: boolean } ) { - this.init({ width, height, resolution, blockLimit, orientation, flip, vertexArray, highlighting, pixelAlign }); + this.init({ width, height, resolution, blockLimit, orientation, flip, vertexArray, highlighting }); } resize({ width = this.width, height = this.height, animate = true }: { width?: number, height?: number, animate: boolean }): void { this.width = width; this.height = height; this.gridSize = this.width / this.gridWidth; - if (this.pixelAlign) { - this.unitPadding = Math.max(1, Math.floor(this.gridSize / 2.5)); - this.unitWidth = this.gridSize - (this.unitPadding); - } else { - this.unitPadding = width / 500; - this.unitWidth = this.gridSize - (this.unitPadding * 2); - } + this.unitPadding = Math.max(1, Math.floor(this.gridSize / 5)); + this.unitWidth = this.gridSize - (this.unitPadding * 2); this.dirty = true; if (this.initialised && this.scene) { @@ -219,15 +213,14 @@ export default class BlockScene { this.animateUntil = Math.max(this.animateUntil, tx.setHighlight(value)); } - private init({ width, height, resolution, blockLimit, orientation, flip, vertexArray, highlighting, pixelAlign }: + private init({ width, height, resolution, blockLimit, orientation, flip, vertexArray, highlighting }: { width: number, height: number, resolution: number, blockLimit: number, - orientation: string, flip: boolean, vertexArray: FastVertexArray, highlighting: boolean, pixelAlign: boolean } + orientation: string, flip: boolean, vertexArray: FastVertexArray, highlighting: boolean } ): void { this.orientation = orientation; this.flip = flip; this.vertexArray = vertexArray; this.highlightingEnabled = highlighting; - this.pixelAlign = pixelAlign; this.scene = { count: 0, @@ -353,12 +346,7 @@ export default class BlockScene { private gridToScreen(position: Square | void): Square { if (position) { const slotSize = (position.s * this.gridSize); - let squareSize; - if (this.pixelAlign) { - squareSize = slotSize - (this.unitPadding); - } else { - squareSize = slotSize - (this.unitPadding * 2); - } + const squareSize = slotSize - (this.unitPadding * 2); // The grid is laid out notionally left-to-right, bottom-to-top, // so we rotate and/or flip the y axis to match the target configuration. @@ -434,7 +422,7 @@ export default class BlockScene { // calculates and returns the size of the tx in multiples of the grid size private txSize(tx: TxView): number { - const scale = Math.max(1, Math.round(Math.sqrt(tx.vsize / this.vbytesPerUnit))); + const scale = Math.max(1, Math.round(Math.sqrt(1.1 * tx.vsize / this.vbytesPerUnit))); return Math.min(this.gridWidth, Math.max(1, scale)); // bound between 1 and the max displayable size (just in case!) } diff --git a/frontend/src/app/components/block/block.component.html b/frontend/src/app/components/block/block.component.html index 84fdfcd91..e65905cd2 100644 --- a/frontend/src/app/components/block/block.component.html +++ b/frontend/src/app/components/block/block.component.html @@ -100,7 +100,7 @@
TXIDOld feePrevious fee New feeStatus
-
+
@@ -110,7 +110,7 @@

Expected Block beta

- @@ -239,7 +239,7 @@

Actual Block

- diff --git a/frontend/src/app/components/block/block.component.scss b/frontend/src/app/components/block/block.component.scss index a15c876e6..7f042552e 100644 --- a/frontend/src/app/components/block/block.component.scss +++ b/frontend/src/app/components/block/block.component.scss @@ -293,3 +293,7 @@ h1 { margin-top: 0.75rem; } } + +.graph-col { + flex-grow: 1.11; +} diff --git a/frontend/src/app/components/clock/clock.component.html b/frontend/src/app/components/clock/clock.component.html index 373653b7e..bdddef730 100644 --- a/frontend/src/app/components/clock/clock.component.html +++ b/frontend/src/app/components/clock/clock.component.html @@ -25,7 +25,7 @@
- +
diff --git a/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.html b/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.html index 7d5ddec30..503f2e38d 100644 --- a/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.html +++ b/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.html @@ -1,10 +1,9 @@ diff --git a/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.ts b/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.ts index 540046e13..30632a862 100644 --- a/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.ts +++ b/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.ts @@ -16,7 +16,6 @@ import { Router } from '@angular/router'; }) export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChanges, AfterViewInit { @Input() index: number; - @Input() pixelAlign: boolean = false; @Output() txPreviewEvent = new EventEmitter(); @ViewChild('blockGraph') blockGraph: BlockOverviewGraphComponent; From fa48791c59a10cb7a0caab02bba0d3fb723a25b1 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Fri, 14 Jul 2023 16:08:57 +0900 Subject: [PATCH 09/11] reduce latest rbf websocket data --- backend/src/api/rbf-cache.ts | 45 +++++++++++++++++-- backend/src/api/websocket-handler.ts | 27 +++++++++-- .../src/app/dashboard/dashboard.component.ts | 43 +++--------------- .../src/app/interfaces/websocket.interface.ts | 12 +++++ frontend/src/app/services/state.service.ts | 3 +- .../src/app/services/websocket.service.ts | 15 +++++++ 6 files changed, 99 insertions(+), 46 deletions(-) diff --git a/backend/src/api/rbf-cache.ts b/backend/src/api/rbf-cache.ts index a3714406f..367ba1c0e 100644 --- a/backend/src/api/rbf-cache.ts +++ b/backend/src/api/rbf-cache.ts @@ -6,6 +6,7 @@ import { Common } from "./common"; interface RbfTransaction extends TransactionStripped { rbf?: boolean; mined?: boolean; + fullRbf?: boolean; } interface RbfTree { @@ -17,6 +18,16 @@ interface RbfTree { replaces: RbfTree[]; } +export interface ReplacementInfo { + mined: boolean; + fullRbf: boolean; + txid: string; + oldFee: number; + oldVsize: number; + newFee: number; + newVsize: number; +} + class RbfCache { private replacedBy: Map = new Map(); private replaces: Map = new Map(); @@ -41,11 +52,15 @@ class RbfCache { this.txs.set(newTx.txid, newTxExtended); // maintain rbf trees - let fullRbf = false; + let txFullRbf = false; + let treeFullRbf = false; const replacedTrees: RbfTree[] = []; for (const replacedTxExtended of replaced) { const replacedTx = Common.stripTransaction(replacedTxExtended) as RbfTransaction; replacedTx.rbf = replacedTxExtended.vin.some((v) => v.sequence < 0xfffffffe); + if (!replacedTx.rbf) { + txFullRbf = true; + } this.replacedBy.set(replacedTx.txid, newTx.txid); if (this.treeMap.has(replacedTx.txid)) { const treeId = this.treeMap.get(replacedTx.txid); @@ -55,7 +70,7 @@ class RbfCache { if (tree) { tree.interval = newTime - tree?.time; replacedTrees.push(tree); - fullRbf = fullRbf || tree.fullRbf || !tree.tx.rbf; + treeFullRbf = treeFullRbf || tree.fullRbf || !tree.tx.rbf; } } } else { @@ -67,15 +82,16 @@ class RbfCache { fullRbf: !replacedTx.rbf, replaces: [], }); - fullRbf = fullRbf || !replacedTx.rbf; + treeFullRbf = treeFullRbf || !replacedTx.rbf; this.txs.set(replacedTx.txid, replacedTxExtended); } } + newTx.fullRbf = txFullRbf; const treeId = replacedTrees[0].tx.txid; const newTree = { tx: newTx, time: newTime, - fullRbf, + fullRbf: treeFullRbf, replaces: replacedTrees }; this.rbfTrees.set(treeId, newTree); @@ -349,6 +365,27 @@ class RbfCache { } return tree; } + + public getLatestRbfSummary(): ReplacementInfo[] { + const rbfList = this.getRbfTrees(false); + return rbfList.slice(0, 6).map(rbfTree => { + let oldFee = 0; + let oldVsize = 0; + for (const replaced of rbfTree.replaces) { + oldFee += replaced.tx.fee; + oldVsize += replaced.tx.vsize; + } + return { + txid: rbfTree.tx.txid, + mined: !!rbfTree.tx.mined, + fullRbf: !!rbfTree.tx.fullRbf, + oldFee, + oldVsize, + newFee: rbfTree.tx.fee, + newVsize: rbfTree.tx.vsize, + }; + }); + } } export default new RbfCache(); diff --git a/backend/src/api/websocket-handler.ts b/backend/src/api/websocket-handler.ts index f91947dcb..48e9106f0 100644 --- a/backend/src/api/websocket-handler.ts +++ b/backend/src/api/websocket-handler.ts @@ -12,7 +12,7 @@ import { Common } from './common'; import loadingIndicators from './loading-indicators'; import config from '../config'; import transactionUtils from './transaction-utils'; -import rbfCache from './rbf-cache'; +import rbfCache, { ReplacementInfo } from './rbf-cache'; import difficultyAdjustment from './difficulty-adjustment'; import feeApi from './fee-api'; import BlocksAuditsRepository from '../repositories/BlocksAuditsRepository'; @@ -40,6 +40,7 @@ class WebsocketHandler { private socketData: { [key: string]: string } = {}; private serializedInitData: string = '{}'; + private lastRbfSummary: ReplacementInfo | null = null; constructor() { } @@ -225,6 +226,15 @@ class WebsocketHandler { } } + if (parsedMessage && parsedMessage['track-rbf-summary'] != null) { + if (parsedMessage['track-rbf-summary']) { + client['track-rbf-summary'] = true; + response['rbfLatestSummary'] = this.socketData['rbfSummary']; + } else { + client['track-rbf-summary'] = false; + } + } + if (parsedMessage.action === 'init') { if (!this.socketData['blocks']?.length || !this.socketData['da']) { this.updateSocketData(); @@ -395,10 +405,13 @@ class WebsocketHandler { const rbfChanges = rbfCache.getRbfChanges(); let rbfReplacements; let fullRbfReplacements; + let rbfSummary; if (Object.keys(rbfChanges.trees).length) { rbfReplacements = rbfCache.getRbfTrees(false); fullRbfReplacements = rbfCache.getRbfTrees(true); + rbfSummary = rbfCache.getLatestRbfSummary(); } + for (const deletedTx of deletedTransactions) { rbfCache.evict(deletedTx.txid); } @@ -409,7 +422,7 @@ class WebsocketHandler { const latestTransactions = newTransactions.slice(0, 6).map((tx) => Common.stripTransaction(tx)); // update init data - this.updateSocketDataFields({ + const socketDataFields = { 'mempoolInfo': mempoolInfo, 'vBytesPerSecond': vBytesPerSecond, 'mempool-blocks': mBlocks, @@ -417,7 +430,11 @@ class WebsocketHandler { 'loadingIndicators': loadingIndicators.getLoadingIndicators(), 'da': da?.previousTime ? da : undefined, 'fees': recommendedFees, - }); + }; + if (rbfSummary) { + socketDataFields['rbfSummary'] = rbfSummary; + } + this.updateSocketDataFields(socketDataFields); // cache serialized objects to avoid stringify-ing the same thing for every client const responseCache = { ...this.socketData }; @@ -601,6 +618,10 @@ class WebsocketHandler { response['rbfLatest'] = getCachedResponse('fullrbfLatest', fullRbfReplacements); } + if (client['track-rbf-summary'] && rbfSummary) { + response['rbfLatestSummary'] = getCachedResponse('rbfLatestSummary', rbfSummary); + } + if (Object.keys(response).length) { const serializedResponse = this.serializeResponse(response); client.send(serializedResponse); diff --git a/frontend/src/app/dashboard/dashboard.component.ts b/frontend/src/app/dashboard/dashboard.component.ts index 4ef4501aa..b1bc35eca 100644 --- a/frontend/src/app/dashboard/dashboard.component.ts +++ b/frontend/src/app/dashboard/dashboard.component.ts @@ -1,8 +1,8 @@ import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core'; import { combineLatest, merge, Observable, of, Subscription } from 'rxjs'; -import { filter, map, scan, share, switchMap, tap } from 'rxjs/operators'; +import { filter, map, scan, share, switchMap } from 'rxjs/operators'; import { BlockExtended, OptimizedMempoolStats, RbfTree } from '../interfaces/node-api.interface'; -import { MempoolInfo, TransactionStripped } from '../interfaces/websocket.interface'; +import { MempoolInfo, TransactionStripped, ReplacementInfo } from '../interfaces/websocket.interface'; import { ApiService } from '../services/api.service'; import { StateService } from '../services/state.service'; import { WebsocketService } from '../services/websocket.service'; @@ -25,17 +25,6 @@ interface MempoolStatsData { weightPerSecond: any; } -interface ReplacementInfo { - tree: RbfTree; - mined: boolean; - fullRbf: boolean; - txid: string; - oldFee: number; - oldVsize: number; - newFee: number; - newVsize: number; -} - @Component({ selector: 'app-dashboard', templateUrl: './dashboard.component.html', @@ -69,13 +58,14 @@ export class DashboardComponent implements OnInit, OnDestroy { ngOnDestroy(): void { this.currencySubscription.unsubscribe(); + this.websocketService.stopTrackRbfSummary(); } ngOnInit(): void { this.isLoadingWebSocket$ = this.stateService.isLoadingWebSocket$; this.seoService.resetTitle(); this.websocketService.want(['blocks', 'stats', 'mempool-blocks', 'live-2h-chart']); - this.websocketService.startTrackRbf('all'); + this.websocketService.startTrackRbfSummary(); this.network$ = merge(of(''), this.stateService.networkChanged$); this.mempoolLoadingStatus$ = this.stateService.loadingIndicators$ .pipe( @@ -154,30 +144,7 @@ export class DashboardComponent implements OnInit, OnDestroy { }, []), ); - this.replacements$ = this.stateService.rbfLatest$.pipe( - switchMap((rbfList) => { - const replacements = rbfList.slice(0, 6).map(rbfTree => { - let oldFee = 0; - let oldVsize = 0; - for (const replaced of rbfTree.replaces) { - oldFee += replaced.tx.fee; - oldVsize += replaced.tx.vsize; - } - this.checkFullRbf(rbfTree); - return { - tree: rbfTree, - txid: rbfTree.tx.txid, - mined: rbfTree.tx.mined, - fullRbf: rbfTree.tx.fullRbf, - oldFee, - oldVsize, - newFee: rbfTree.tx.fee, - newVsize: rbfTree.tx.vsize, - }; - }); - return of(replacements); - }) - ); + this.replacements$ = this.stateService.rbfLatestSummary$; this.mempoolStats$ = this.stateService.connectionState$ .pipe( diff --git a/frontend/src/app/interfaces/websocket.interface.ts b/frontend/src/app/interfaces/websocket.interface.ts index 20a114c72..991fe2680 100644 --- a/frontend/src/app/interfaces/websocket.interface.ts +++ b/frontend/src/app/interfaces/websocket.interface.ts @@ -18,6 +18,7 @@ export interface WebsocketResponse { txReplaced?: ReplacedTransaction; rbfInfo?: RbfTree; rbfLatest?: RbfTree[]; + rbfLatestSummary?: ReplacementInfo[]; utxoSpent?: object; transactions?: TransactionStripped[]; loadingIndicators?: ILoadingIndicators; @@ -29,6 +30,7 @@ export interface WebsocketResponse { 'track-asset'?: string; 'track-mempool-block'?: number; 'track-rbf'?: string; + 'track-rbf-summary'?: boolean; 'watch-mempool'?: boolean; 'track-bisq-market'?: string; 'refresh-blocks'?: boolean; @@ -37,6 +39,16 @@ export interface WebsocketResponse { export interface ReplacedTransaction extends Transaction { txid: string; } + +export interface ReplacementInfo { + mined: boolean; + fullRbf: boolean; + txid: string; + oldFee: number; + oldVsize: number; + newFee: number; + newVsize: number; +} export interface MempoolBlock { blink?: boolean; height?: number; diff --git a/frontend/src/app/services/state.service.ts b/frontend/src/app/services/state.service.ts index f38600605..c1964e85e 100644 --- a/frontend/src/app/services/state.service.ts +++ b/frontend/src/app/services/state.service.ts @@ -1,7 +1,7 @@ import { Inject, Injectable, PLATFORM_ID, LOCALE_ID } from '@angular/core'; import { ReplaySubject, BehaviorSubject, Subject, fromEvent, Observable, merge } from 'rxjs'; import { Transaction } from '../interfaces/electrs.interface'; -import { IBackendInfo, MempoolBlock, MempoolBlockDelta, MempoolInfo, Recommendedfees, ReplacedTransaction, TransactionStripped } from '../interfaces/websocket.interface'; +import { IBackendInfo, MempoolBlock, MempoolBlockDelta, MempoolInfo, Recommendedfees, ReplacedTransaction, ReplacementInfo, TransactionStripped } from '../interfaces/websocket.interface'; import { BlockExtended, DifficultyAdjustment, MempoolPosition, OptimizedMempoolStats, RbfTree } from '../interfaces/node-api.interface'; import { Router, NavigationStart } from '@angular/router'; import { isPlatformBrowser } from '@angular/common'; @@ -108,6 +108,7 @@ export class StateService { txReplaced$ = new Subject(); txRbfInfo$ = new Subject(); rbfLatest$ = new Subject(); + rbfLatestSummary$ = new Subject(); utxoSpent$ = new Subject(); difficultyAdjustment$ = new ReplaySubject(1); mempoolTransactions$ = new Subject(); diff --git a/frontend/src/app/services/websocket.service.ts b/frontend/src/app/services/websocket.service.ts index 7eed09e77..f32f772ac 100644 --- a/frontend/src/app/services/websocket.service.ts +++ b/frontend/src/app/services/websocket.service.ts @@ -29,6 +29,7 @@ export class WebsocketService { private trackingTxId: string; private isTrackingMempoolBlock = false; private isTrackingRbf = false; + private isTrackingRbfSummary = false; private trackingMempoolBlock: number; private latestGitCommit = ''; private onlineCheckTimeout: number; @@ -185,6 +186,16 @@ export class WebsocketService { this.isTrackingRbf = false; } + startTrackRbfSummary() { + this.websocketSubject.next({ 'track-rbf-summary': true }); + this.isTrackingRbfSummary = true; + } + + stopTrackRbfSummary() { + this.websocketSubject.next({ 'track-rbf-summary': false }); + this.isTrackingRbfSummary = false; + } + startTrackBisqMarket(market: string) { this.websocketSubject.next({ 'track-bisq-market': market }); } @@ -283,6 +294,10 @@ export class WebsocketService { this.stateService.rbfLatest$.next(response.rbfLatest); } + if (response.rbfLatestSummary) { + this.stateService.rbfLatestSummary$.next(response.rbfLatestSummary); + } + if (response.txReplaced) { this.stateService.txReplaced$.next(response.txReplaced); } From 7f0218e343e34c897426e83b72b5deeb2a63689a Mon Sep 17 00:00:00 2001 From: Mononaut Date: Fri, 14 Jul 2023 18:39:28 +0900 Subject: [PATCH 10/11] add margin between mobile audit tabs & visualization --- frontend/src/app/components/block/block.component.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/app/components/block/block.component.scss b/frontend/src/app/components/block/block.component.scss index 7f042552e..c413b1fce 100644 --- a/frontend/src/app/components/block/block.component.scss +++ b/frontend/src/app/components/block/block.component.scss @@ -239,6 +239,7 @@ h1 { .nav-tabs { border-color: white; border-width: 1px; + margin-bottom: 1em; } .nav-tabs .nav-link { From 23151ec3dbc530166df7f474a4e702915808a057 Mon Sep 17 00:00:00 2001 From: wiz Date: Fri, 14 Jul 2023 18:39:35 +0900 Subject: [PATCH 11/11] Bump version to 3.0.0-dev - Now requires mempool/electrs - Mempool Accelerator integration - Rust GBT integration - And more! --- backend/package-lock.json | 4 ++-- backend/package.json | 2 +- backend/rust-gbt/package-lock.json | 4 ++-- backend/rust-gbt/package.json | 4 ++-- frontend/package-lock.json | 4 ++-- frontend/package.json | 4 ++-- unfurler/package-lock.json | 4 ++-- unfurler/package.json | 2 +- 8 files changed, 14 insertions(+), 14 deletions(-) diff --git a/backend/package-lock.json b/backend/package-lock.json index 4f36005a7..6800c24c0 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -1,12 +1,12 @@ { "name": "mempool-backend", - "version": "2.6.0-dev", + "version": "3.0.0-dev", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "mempool-backend", - "version": "2.6.0-dev", + "version": "3.0.0-dev", "license": "GNU Affero General Public License v3.0", "dependencies": { "@babel/core": "^7.21.3", diff --git a/backend/package.json b/backend/package.json index bc4771ebb..5cfb7982e 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "mempool-backend", - "version": "2.6.0-dev", + "version": "3.0.0-dev", "description": "Bitcoin mempool visualizer and blockchain explorer backend", "license": "GNU Affero General Public License v3.0", "homepage": "https://mempool.space", diff --git a/backend/rust-gbt/package-lock.json b/backend/rust-gbt/package-lock.json index e20a6f5bf..b7949b9ab 100644 --- a/backend/rust-gbt/package-lock.json +++ b/backend/rust-gbt/package-lock.json @@ -1,12 +1,12 @@ { "name": "gbt", - "version": "0.1.0", + "version": "3.0.0-dev", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "gbt", - "version": "0.1.0", + "version": "3.0.0-dev", "hasInstallScript": true, "dependencies": { "@napi-rs/cli": "^2.16.1" diff --git a/backend/rust-gbt/package.json b/backend/rust-gbt/package.json index e65fb2209..c95f36b06 100644 --- a/backend/rust-gbt/package.json +++ b/backend/rust-gbt/package.json @@ -1,6 +1,6 @@ { "name": "gbt", - "version": "0.1.0", + "version": "3.0.0-dev", "description": "An inefficient re-implementation of the getBlockTemplate algorithm in Rust", "main": "index.js", "types": "index.d.ts", @@ -30,4 +30,4 @@ "engines": { "node": ">= 12" } -} \ No newline at end of file +} diff --git a/frontend/package-lock.json b/frontend/package-lock.json index a9f8b7049..13dc6d1b6 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "mempool-frontend", - "version": "2.6.0-dev", + "version": "3.0.0-dev", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "mempool-frontend", - "version": "2.6.0-dev", + "version": "3.0.0-dev", "license": "GNU Affero General Public License v3.0", "dependencies": { "@angular-devkit/build-angular": "^14.2.10", diff --git a/frontend/package.json b/frontend/package.json index c06fe74a7..d73fa8fa2 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "mempool-frontend", - "version": "2.6.0-dev", + "version": "3.0.0-dev", "description": "Bitcoin mempool visualizer and blockchain explorer backend", "license": "GNU Affero General Public License v3.0", "homepage": "https://mempool.space", @@ -119,4 +119,4 @@ "scarfSettings": { "enabled": false } -} \ No newline at end of file +} diff --git a/unfurler/package-lock.json b/unfurler/package-lock.json index 40520d413..16968f203 100644 --- a/unfurler/package-lock.json +++ b/unfurler/package-lock.json @@ -1,12 +1,12 @@ { "name": "mempool-unfurl", - "version": "0.1.0", + "version": "3.0.0-dev", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "mempool-unfurl", - "version": "0.1.0", + "version": "3.0.0-dev", "dependencies": { "@types/node": "^16.11.41", "express": "^4.18.0", diff --git a/unfurler/package.json b/unfurler/package.json index 59d48aa50..ec0a153b6 100644 --- a/unfurler/package.json +++ b/unfurler/package.json @@ -1,6 +1,6 @@ { "name": "mempool-unfurl", - "version": "0.1.0", + "version": "3.0.0-dev", "description": "Renderer for mempool open graph link preview images", "repository": { "type": "git",