From 89021076632a64510040e038fe0e8ab577041c33 Mon Sep 17 00:00:00 2001 From: Antoni Spaanderman <56turtle56@gmail.com> Date: Sun, 13 Feb 2022 13:52:04 +0100 Subject: [PATCH 1/7] implement /api/mempool --- backend/src/api/bitcoin/bitcoin-api.interface.ts | 1 + backend/src/api/mempool.ts | 2 +- backend/src/routes.ts | 7 ++++++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/backend/src/api/bitcoin/bitcoin-api.interface.ts b/backend/src/api/bitcoin/bitcoin-api.interface.ts index e2b9158bb..6a22af9a0 100644 --- a/backend/src/api/bitcoin/bitcoin-api.interface.ts +++ b/backend/src/api/bitcoin/bitcoin-api.interface.ts @@ -4,6 +4,7 @@ export namespace IBitcoinApi { size: number; // (numeric) Current tx count bytes: number; // (numeric) Sum of all virtual transaction sizes as defined in BIP 141. usage: number; // (numeric) Total memory usage for the mempool + total_fee: number; // (numeric) Total fees of transactions in the mempool maxmempool: number; // (numeric) Maximum memory usage for the mempool mempoolminfee: number; // (numeric) Minimum fee rate in BTC/kB for tx to be accepted. minrelaytxfee: number; // (numeric) Current minimum relay fee for transactions diff --git a/backend/src/api/mempool.ts b/backend/src/api/mempool.ts index b1bd6a159..3a389c059 100644 --- a/backend/src/api/mempool.ts +++ b/backend/src/api/mempool.ts @@ -14,7 +14,7 @@ class Mempool { private static LAZY_DELETE_AFTER_SECONDS = 30; private inSync: boolean = false; private mempoolCache: { [txId: string]: TransactionExtended } = {}; - private mempoolInfo: IBitcoinApi.MempoolInfo = { loaded: false, size: 0, bytes: 0, usage: 0, + private mempoolInfo: IBitcoinApi.MempoolInfo = { loaded: false, size: 0, bytes: 0, usage: 0, total_fee: 0, maxmempool: 300000000, mempoolminfee: 0.00001000, minrelaytxfee: 0.00001000 }; private mempoolChangedCallback: ((newMempool: {[txId: string]: TransactionExtended; }, newTransactions: TransactionExtended[], deletedTransactions: TransactionExtended[]) => void) | undefined; diff --git a/backend/src/routes.ts b/backend/src/routes.ts index e06177ddd..e03eb3a03 100644 --- a/backend/src/routes.ts +++ b/backend/src/routes.ts @@ -699,7 +699,12 @@ class Routes { } public async getMempool(req: Request, res: Response) { - res.status(501).send('Not implemented'); + const info = mempool.getMempoolInfo(); + res.json({ + count: info.size, + vsize: info.bytes, + total_fee: info.total_fee * 1e8 + }); } public async getMempoolTxIds(req: Request, res: Response) { From 7a44705ef176733a8397d92616f18ef1df1ceccb Mon Sep 17 00:00:00 2001 From: Antoni Spaanderman <56turtle56@gmail.com> Date: Sun, 13 Feb 2022 16:13:46 +0100 Subject: [PATCH 2/7] set fee_histogram to [] --- backend/src/routes.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/src/routes.ts b/backend/src/routes.ts index e03eb3a03..9e25f0d35 100644 --- a/backend/src/routes.ts +++ b/backend/src/routes.ts @@ -703,7 +703,8 @@ class Routes { res.json({ count: info.size, vsize: info.bytes, - total_fee: info.total_fee * 1e8 + total_fee: info.total_fee * 1e8, + fee_histogram: [] }); } From 61f15a1c867243e766e432c8d08123caa9299a19 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Fri, 11 Feb 2022 20:35:53 +0900 Subject: [PATCH 3/7] Revert "Merge pull request #1240 from nymkappa/feature/mempool-sync-threshold" This reverts commit 2f921f4cc73994cc9ea9c317c8897de0d1881340, reversing changes made to 877be47e5be03e31b25cc12fbe2ee45be881e5d8. --- backend/src/api/mempool.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/backend/src/api/mempool.ts b/backend/src/api/mempool.ts index 3a389c059..a8b0b461a 100644 --- a/backend/src/api/mempool.ts +++ b/backend/src/api/mempool.ts @@ -168,8 +168,7 @@ class Mempool { const newTransactionsStripped = newTransactions.map((tx) => Common.stripTransaction(tx)); this.latestTransactions = newTransactionsStripped.concat(this.latestTransactions).slice(0, 6); - const syncedThreshold = 0.99; // If we synced 99% of the mempool tx count, consider we're synced - if (!this.inSync && Object.keys(this.mempoolCache).length >= transactions.length * syncedThreshold) { + if (!this.inSync && transactions.length === Object.keys(this.mempoolCache).length) { this.inSync = true; logger.notice('The mempool is now in sync!'); loadingIndicators.setProgress('mempool', 100); From 9ed500fe9618a6dfa4e952d3442905aea2ce9ca3 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Fri, 11 Feb 2022 21:25:58 +0900 Subject: [PATCH 4/7] Don't wait for 100% mempool sync before starting block indexing --- backend/src/api/blocks.ts | 6 +++--- backend/src/api/mempool.ts | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/backend/src/api/blocks.ts b/backend/src/api/blocks.ts index d2487414f..affbf0049 100644 --- a/backend/src/api/blocks.ts +++ b/backend/src/api/blocks.ts @@ -61,7 +61,7 @@ class Blocks { // optimize here by directly fetching txs in the "outdated" mempool transactions.push(mempool[txIds[i]]); transactionsFound++; - } else if (config.MEMPOOL.BACKEND === 'esplora' || memPool.isInSync() || i === 0) { + } else if (config.MEMPOOL.BACKEND === 'esplora' || !memPool.hasPriority() || i === 0) { // Otherwise we fetch the tx data through backend services (esplora, electrum, core rpc...) if (!quiet && (i % (Math.round((txIds.length) / 10)) === 0 || i + 1 === txIds.length)) { // Avoid log spam logger.debug(`Indexing tx ${i + 1} of ${txIds.length} in block #${blockHeight}`); @@ -175,7 +175,7 @@ class Blocks { public async $generateBlockDatabase() { if (['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) === false || // Bitcoin only config.MEMPOOL.INDEXING_BLOCKS_AMOUNT === 0 || // Indexing of older blocks must be enabled - !memPool.isInSync() || // We sync the mempool first + memPool.hasPriority() || // We sync the mempool first this.blockIndexingStarted === true || // Indexing must not already be in progress config.DATABASE.ENABLED === false ) { @@ -314,7 +314,7 @@ class Blocks { if (this.newBlockCallbacks.length) { this.newBlockCallbacks.forEach((cb) => cb(blockExtended, txIds, transactions)); } - if (memPool.isInSync()) { + if (!memPool.hasPriority()) { diskCache.$saveCacheToDisk(); } } diff --git a/backend/src/api/mempool.ts b/backend/src/api/mempool.ts index a8b0b461a..64505ba2b 100644 --- a/backend/src/api/mempool.ts +++ b/backend/src/api/mempool.ts @@ -13,6 +13,7 @@ class Mempool { private static WEBSOCKET_REFRESH_RATE_MS = 10000; private static LAZY_DELETE_AFTER_SECONDS = 30; private inSync: boolean = false; + private mempoolCacheDelta: number = -1; private mempoolCache: { [txId: string]: TransactionExtended } = {}; private mempoolInfo: IBitcoinApi.MempoolInfo = { loaded: false, size: 0, bytes: 0, usage: 0, total_fee: 0, maxmempool: 300000000, mempoolminfee: 0.00001000, minrelaytxfee: 0.00001000 }; @@ -32,6 +33,17 @@ class Mempool { setInterval(this.deleteExpiredTransactions.bind(this), 20000); } + /** + * Return true if we should leave resources available for mempool tx caching + */ + public hasPriority(): boolean { + if (this.inSync) { + return false; + } else { + return this.mempoolCacheDelta == -1 || this.mempoolCacheDelta > 25; + } + } + public isInSync(): boolean { return this.inSync; } @@ -100,6 +112,8 @@ class Mempool { const diff = transactions.length - currentMempoolSize; const newTransactions: TransactionExtended[] = []; + this.mempoolCacheDelta = Math.abs(diff); + if (!this.inSync) { loadingIndicators.setProgress('mempool', Object.keys(this.mempoolCache).length / transactions.length * 100); } @@ -174,6 +188,8 @@ class Mempool { loadingIndicators.setProgress('mempool', 100); } + this.mempoolCacheDelta = Math.abs(transactions.length - Object.keys(this.mempoolCache).length); + if (this.mempoolChangedCallback && (hasChange || deletedTransactions.length)) { this.mempoolChangedCallback(this.mempoolCache, newTransactions, deletedTransactions); } From 2bb8b60278655c402cc6e076012e4783db13f05f Mon Sep 17 00:00:00 2001 From: nymkappa Date: Tue, 15 Feb 2022 15:56:40 +0900 Subject: [PATCH 5/7] Revert "Update tests - Replace button click blocks -> pools" This reverts commit 73019b485fefda0be8e5b6e0fc0e0569721879b5. --- .../integration/mainnet/mainnet.spec.ts | 96 ------------------- .../cypress/integration/signet/signet.spec.ts | 4 +- .../integration/testnet/testnet.spec.ts | 4 +- 3 files changed, 4 insertions(+), 100 deletions(-) diff --git a/frontend/cypress/integration/mainnet/mainnet.spec.ts b/frontend/cypress/integration/mainnet/mainnet.spec.ts index 752617092..34e5bfac9 100644 --- a/frontend/cypress/integration/mainnet/mainnet.spec.ts +++ b/frontend/cypress/integration/mainnet/mainnet.spec.ts @@ -274,102 +274,6 @@ describe('Mainnet', () => { }); }); }); - }); - }); - - - it('loads skeleton when changes between networks', () => { - cy.visit('/'); - cy.waitForSkeletonGone(); - - cy.changeNetwork("testnet"); - cy.changeNetwork("signet"); - cy.changeNetwork("mainnet"); - }); - - it.skip('loads the dashboard with the skeleton blocks', () => { - cy.mockMempoolSocket(); - cy.visit("/"); - cy.get(':nth-child(1) > #bitcoin-block-0').should('be.visible'); - cy.get(':nth-child(2) > #bitcoin-block-0').should('be.visible'); - cy.get(':nth-child(3) > #bitcoin-block-0').should('be.visible'); - cy.get('#mempool-block-0').should('be.visible'); - cy.get('#mempool-block-1').should('be.visible'); - cy.get('#mempool-block-2').should('be.visible'); - - emitMempoolInfo({ - 'params': { - command: 'init' - } - }); - - cy.get(':nth-child(1) > #bitcoin-block-0').should('not.exist'); - cy.get(':nth-child(2) > #bitcoin-block-0').should('not.exist'); - cy.get(':nth-child(3) > #bitcoin-block-0').should('not.exist'); - }); - - it('loads the pools screen', () => { - cy.visit('/'); - cy.waitForSkeletonGone(); - cy.get('#btn-pools').click().then(() => { - cy.waitForPageIdle(); - }); - }); - - it('loads the graphs screen', () => { - cy.visit('/'); - cy.waitForSkeletonGone(); - cy.get('#btn-graphs').click().then(() => { - cy.wait(1000); - }); - }); - - describe('graphs page', () => { - it('check buttons - mobile', () => { - cy.viewport('iphone-6'); - cy.visit('/graphs'); - cy.waitForSkeletonGone(); - cy.get('.small-buttons > :nth-child(2)').should('be.visible'); - cy.get('#dropdownFees').should('be.visible'); - cy.get('.btn-group').should('be.visible'); - }); - it('check buttons - tablet', () => { - cy.viewport('ipad-2'); - cy.visit('/graphs'); - cy.waitForSkeletonGone(); - cy.get('.small-buttons > :nth-child(2)').should('be.visible'); - cy.get('#dropdownFees').should('be.visible'); - cy.get('.btn-group').should('be.visible'); - }); - it('check buttons - desktop', () => { - cy.viewport('macbook-16'); - cy.visit('/graphs'); - cy.waitForSkeletonGone(); - cy.get('.small-buttons > :nth-child(2)').should('be.visible'); - cy.get('#dropdownFees').should('be.visible'); - cy.get('.btn-group').should('be.visible'); - }); - }); - - it('loads the tv screen - desktop', () => { - cy.viewport('macbook-16'); - cy.visit('/'); - cy.waitForSkeletonGone(); - cy.get('#btn-tv').click().then(() => { - cy.viewport('macbook-16'); - cy.get('.chart-holder'); - cy.get('.blockchain-wrapper').should('be.visible'); - cy.get('#mempool-block-0').should('be.visible'); - }); - }); - - it('loads the tv screen - mobile', () => { - cy.viewport('iphone-6'); - cy.visit('/tv'); - cy.waitForSkeletonGone(); - cy.get('.chart-holder'); - cy.get('.blockchain-wrapper').should('not.visible'); - }); it('loads genesis block and click on the arrow left', () => { cy.viewport('macbook-16'); diff --git a/frontend/cypress/integration/signet/signet.spec.ts b/frontend/cypress/integration/signet/signet.spec.ts index d2bbd1196..9ebf67b81 100644 --- a/frontend/cypress/integration/signet/signet.spec.ts +++ b/frontend/cypress/integration/signet/signet.spec.ts @@ -44,10 +44,10 @@ describe('Signet', () => { cy.get(':nth-child(3) > #bitcoin-block-0').should('not.exist'); }); - it('loads the pools screen', () => { + it('loads the blocks screen', () => { cy.visit('/signet'); cy.waitForSkeletonGone(); - cy.get('#btn-pools').click().then(() => { + cy.get('#btn-blocks').click().then(() => { cy.wait(1000); }); }); diff --git a/frontend/cypress/integration/testnet/testnet.spec.ts b/frontend/cypress/integration/testnet/testnet.spec.ts index c0c07aa74..6f3264244 100644 --- a/frontend/cypress/integration/testnet/testnet.spec.ts +++ b/frontend/cypress/integration/testnet/testnet.spec.ts @@ -44,10 +44,10 @@ describe('Testnet', () => { cy.get(':nth-child(3) > #bitcoin-block-0').should('not.exist'); }); - it('loads the pools screen', () => { + it('loads the blocks screen', () => { cy.visit('/testnet'); cy.waitForSkeletonGone(); - cy.get('#btn-pools').click().then(() => { + cy.get('#btn-blocks').click().then(() => { cy.wait(1000); }); }); From aa3b2dffc63fb2670a1a0a442ce46c63c37f894b Mon Sep 17 00:00:00 2001 From: nymkappa Date: Tue, 15 Feb 2022 16:19:58 +0900 Subject: [PATCH 6/7] Re-apply test updates from bogus commit 73019b485fefda0be8e5b6e0fc0e0569721879b5 --- .../integration/mainnet/mainnet.spec.ts | 22 ++++++++++--------- .../cypress/integration/signet/signet.spec.ts | 4 ++-- .../integration/testnet/testnet.spec.ts | 4 ++-- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/frontend/cypress/integration/mainnet/mainnet.spec.ts b/frontend/cypress/integration/mainnet/mainnet.spec.ts index 34e5bfac9..473c480f4 100644 --- a/frontend/cypress/integration/mainnet/mainnet.spec.ts +++ b/frontend/cypress/integration/mainnet/mainnet.spec.ts @@ -275,16 +275,18 @@ describe('Mainnet', () => { }); }); - it('loads genesis block and click on the arrow left', () => { - cy.viewport('macbook-16'); - cy.visit('/block/0'); - cy.waitForSkeletonGone(); - cy.waitForPageIdle(); - cy.get('[ngbtooltip="Next Block"] > .ng-fa-icon > .svg-inline--fa').should('be.visible'); - cy.get('[ngbtooltip="Previous Block"] > .ng-fa-icon > .svg-inline--fa').should('not.exist'); - cy.get('[ngbtooltip="Next Block"] > .ng-fa-icon > .svg-inline--fa').click().then(() => { - cy.get('[ngbtooltip="Next Block"] > .ng-fa-icon > .svg-inline--fa').should('be.visible'); - cy.get('[ngbtooltip="Previous Block"] > .ng-fa-icon > .svg-inline--fa').should('be.visible'); + it('loads genesis block and click on the arrow left', () => { + cy.viewport('macbook-16'); + cy.visit('/block/0'); + cy.waitForSkeletonGone(); + cy.waitForPageIdle(); + cy.get('[ngbtooltip="Next Block"] > .ng-fa-icon > .svg-inline--fa').should('be.visible'); + cy.get('[ngbtooltip="Previous Block"] > .ng-fa-icon > .svg-inline--fa').should('not.exist'); + cy.get('[ngbtooltip="Next Block"] > .ng-fa-icon > .svg-inline--fa').click().then(() => { + cy.get('[ngbtooltip="Next Block"] > .ng-fa-icon > .svg-inline--fa').should('be.visible'); + cy.get('[ngbtooltip="Previous Block"] > .ng-fa-icon > .svg-inline--fa').should('be.visible'); + }); + }); }); }); diff --git a/frontend/cypress/integration/signet/signet.spec.ts b/frontend/cypress/integration/signet/signet.spec.ts index 9ebf67b81..d2bbd1196 100644 --- a/frontend/cypress/integration/signet/signet.spec.ts +++ b/frontend/cypress/integration/signet/signet.spec.ts @@ -44,10 +44,10 @@ describe('Signet', () => { cy.get(':nth-child(3) > #bitcoin-block-0').should('not.exist'); }); - it('loads the blocks screen', () => { + it('loads the pools screen', () => { cy.visit('/signet'); cy.waitForSkeletonGone(); - cy.get('#btn-blocks').click().then(() => { + cy.get('#btn-pools').click().then(() => { cy.wait(1000); }); }); diff --git a/frontend/cypress/integration/testnet/testnet.spec.ts b/frontend/cypress/integration/testnet/testnet.spec.ts index 6f3264244..c0c07aa74 100644 --- a/frontend/cypress/integration/testnet/testnet.spec.ts +++ b/frontend/cypress/integration/testnet/testnet.spec.ts @@ -44,10 +44,10 @@ describe('Testnet', () => { cy.get(':nth-child(3) > #bitcoin-block-0').should('not.exist'); }); - it('loads the blocks screen', () => { + it('loads the pools screen', () => { cy.visit('/testnet'); cy.waitForSkeletonGone(); - cy.get('#btn-blocks').click().then(() => { + cy.get('#btn-pools').click().then(() => { cy.wait(1000); }); }); From de276c81e04b58f258a8617b23fe8b33ebef14b6 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Tue, 15 Feb 2022 16:02:30 +0900 Subject: [PATCH 7/7] Provide a way to completely disable block indexing and mining menu --- backend/src/api/blocks.ts | 24 +++++-------------- backend/src/api/common.ts | 8 +++++++ frontend/mempool-frontend-config.sample.json | 3 ++- .../master-page/master-page.component.html | 5 +++- .../master-page/master-page.component.ts | 2 +- frontend/src/app/services/state.service.ts | 2 ++ 6 files changed, 23 insertions(+), 21 deletions(-) diff --git a/backend/src/api/blocks.ts b/backend/src/api/blocks.ts index affbf0049..7513f259e 100644 --- a/backend/src/api/blocks.ts +++ b/backend/src/api/blocks.ts @@ -116,10 +116,7 @@ class Blocks { blockExtended.extras.feeRange = transactionsTmp.length > 0 ? Common.getFeesInRange(transactionsTmp, 8) : [0, 0]; - const indexingAvailable = - ['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) && - config.DATABASE.ENABLED === true; - if (indexingAvailable) { + if (Common.indexingEnabled()) { let pool: PoolTag; if (blockExtended.extras?.coinbaseTx !== undefined) { pool = await this.$findBlockMiner(blockExtended.extras?.coinbaseTx); @@ -173,11 +170,9 @@ class Blocks { * Index all blocks metadata for the mining dashboard */ public async $generateBlockDatabase() { - if (['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) === false || // Bitcoin only - config.MEMPOOL.INDEXING_BLOCKS_AMOUNT === 0 || // Indexing of older blocks must be enabled - memPool.hasPriority() || // We sync the mempool first - this.blockIndexingStarted === true || // Indexing must not already be in progress - config.DATABASE.ENABLED === false + if (this.blockIndexingStarted === true || + !Common.indexingEnabled() || + memPool.hasPriority() ) { return; } @@ -293,10 +288,7 @@ class Blocks { const transactions = await this.$getTransactionsExtended(blockHash, block.height, false); const blockExtended: BlockExtended = await this.$getBlockExtended(block, transactions); - const indexingAvailable = - ['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) && - config.DATABASE.ENABLED === true; - if (indexingAvailable) { + if (Common.indexingEnabled()) { await blocksRepository.$saveBlockInDatabase(blockExtended); } @@ -340,10 +332,6 @@ class Blocks { } public async $getBlocksExtras(fromHeight: number): Promise { - const indexingAvailable = - ['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) && - config.DATABASE.ENABLED === true; - try { loadingIndicators.setProgress('blocks', 0); @@ -366,7 +354,7 @@ class Blocks { let nextHash = startFromHash; for (let i = 0; i < 10 && currentHeight >= 0; i++) { let block = this.getBlocks().find((b) => b.height === currentHeight); - if (!block && indexingAvailable) { + if (!block && Common.indexingEnabled()) { block = this.prepareBlock(await this.$indexBlock(currentHeight)); } else if (!block) { block = this.prepareBlock(await bitcoinApi.$getBlock(nextHash)); diff --git a/backend/src/api/common.ts b/backend/src/api/common.ts index 5e99e870c..3ab5c4b9f 100644 --- a/backend/src/api/common.ts +++ b/backend/src/api/common.ts @@ -154,4 +154,12 @@ export class Common { }); return parents; } + + static indexingEnabled(): boolean { + return ( + ['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) && + config.DATABASE.ENABLED === true && + config.MEMPOOL.INDEXING_BLOCKS_AMOUNT != 0 + ); + } } diff --git a/frontend/mempool-frontend-config.sample.json b/frontend/mempool-frontend-config.sample.json index 0715cb0bd..231f1c7c8 100644 --- a/frontend/mempool-frontend-config.sample.json +++ b/frontend/mempool-frontend-config.sample.json @@ -15,5 +15,6 @@ "BASE_MODULE": "mempool", "MEMPOOL_WEBSITE_URL": "https://mempool.space", "LIQUID_WEBSITE_URL": "https://liquid.network", - "BISQ_WEBSITE_URL": "https://bisq.markets" + "BISQ_WEBSITE_URL": "https://bisq.markets", + "MINING_DASHBOARD": true } diff --git a/frontend/src/app/components/master-page/master-page.component.html b/frontend/src/app/components/master-page/master-page.component.html index 4624340d3..af47b75c2 100644 --- a/frontend/src/app/components/master-page/master-page.component.html +++ b/frontend/src/app/components/master-page/master-page.component.html @@ -31,9 +31,12 @@ - + diff --git a/frontend/src/app/components/master-page/master-page.component.ts b/frontend/src/app/components/master-page/master-page.component.ts index fcff5629c..23a73178f 100644 --- a/frontend/src/app/components/master-page/master-page.component.ts +++ b/frontend/src/app/components/master-page/master-page.component.ts @@ -18,7 +18,7 @@ export class MasterPageComponent implements OnInit { urlLanguage: string; constructor( - private stateService: StateService, + public stateService: StateService, private languageService: LanguageService, ) { } diff --git a/frontend/src/app/services/state.service.ts b/frontend/src/app/services/state.service.ts index 230c9b150..14d67e765 100644 --- a/frontend/src/app/services/state.service.ts +++ b/frontend/src/app/services/state.service.ts @@ -36,6 +36,7 @@ export interface Env { MEMPOOL_WEBSITE_URL: string; LIQUID_WEBSITE_URL: string; BISQ_WEBSITE_URL: string; + MINING_DASHBOARD: boolean; } const defaultEnv: Env = { @@ -59,6 +60,7 @@ const defaultEnv: Env = { 'MEMPOOL_WEBSITE_URL': 'https://mempool.space', 'LIQUID_WEBSITE_URL': 'https://liquid.network', 'BISQ_WEBSITE_URL': 'https://bisq.markets', + 'MINING_DASHBOARD': true }; @Injectable({