Merge branch 'master' into nymkappa/feature/use-block-count-timespan

This commit is contained in:
wiz 2022-05-25 20:16:32 +09:00 committed by GitHub
commit df9c9e334d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 238 additions and 473 deletions

View File

@ -417,10 +417,7 @@ class Blocks {
return blockExtended; return blockExtended;
} }
public async $getBlocksExtras(fromHeight?: number, limit: number = 15): Promise<BlockExtended[]> { public async $getBlocks(fromHeight?: number, limit: number = 15): Promise<BlockExtended[]> {
// Note - This API is breaking if indexing is not available. For now it is okay because we only
// use it for the mining pages, and mining pages should not be available if indexing is turned off.
// I'll need to fix it before we refactor the block(s) related pages
try { try {
let currentHeight = fromHeight !== undefined ? fromHeight : this.getCurrentBlockHeight(); let currentHeight = fromHeight !== undefined ? fromHeight : this.getCurrentBlockHeight();
const returnBlocks: BlockExtended[] = []; const returnBlocks: BlockExtended[] = [];

View File

@ -27,7 +27,6 @@ import icons from './api/liquid/icons';
import { Common } from './api/common'; import { Common } from './api/common';
import mining from './api/mining'; import mining from './api/mining';
import HashratesRepository from './repositories/HashratesRepository'; import HashratesRepository from './repositories/HashratesRepository';
import BlocksRepository from './repositories/BlocksRepository';
import poolsUpdater from './tasks/pools-updater'; import poolsUpdater from './tasks/pools-updater';
import indexer from './indexer'; import indexer from './indexer';
@ -315,8 +314,8 @@ class Server {
} }
this.app this.app
.get(config.MEMPOOL.API_URL_PREFIX + 'blocks-extras', routes.getBlocksExtras) .get(config.MEMPOOL.API_URL_PREFIX + 'blocks', routes.getBlocks.bind(routes))
.get(config.MEMPOOL.API_URL_PREFIX + 'blocks-extras/:height', routes.getBlocksExtras) .get(config.MEMPOOL.API_URL_PREFIX + 'blocks/:height', routes.getBlocks.bind(routes))
.get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash', routes.getBlock); .get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash', routes.getBlock);
if (config.MEMPOOL.BACKEND !== 'esplora') { if (config.MEMPOOL.BACKEND !== 'esplora') {
@ -330,8 +329,6 @@ class Server {
.get(config.MEMPOOL.API_URL_PREFIX + 'tx/:txId/status', routes.getTransactionStatus) .get(config.MEMPOOL.API_URL_PREFIX + 'tx/:txId/status', routes.getTransactionStatus)
.get(config.MEMPOOL.API_URL_PREFIX + 'tx/:txId/outspends', routes.getTransactionOutspends) .get(config.MEMPOOL.API_URL_PREFIX + 'tx/:txId/outspends', routes.getTransactionOutspends)
.get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash/header', routes.getBlockHeader) .get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash/header', routes.getBlockHeader)
.get(config.MEMPOOL.API_URL_PREFIX + 'blocks', routes.getBlocks)
.get(config.MEMPOOL.API_URL_PREFIX + 'blocks/:height', routes.getBlocks)
.get(config.MEMPOOL.API_URL_PREFIX + 'blocks/tip/height', routes.getBlockTipHeight) .get(config.MEMPOOL.API_URL_PREFIX + 'blocks/tip/height', routes.getBlockTipHeight)
.get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash/txs', routes.getBlockTransactions) .get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash/txs', routes.getBlockTransactions)
.get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash/txs/:index', routes.getBlockTransactions) .get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash/txs/:index', routes.getBlockTransactions)

View File

@ -81,7 +81,7 @@ export interface TransactionStripped {
export interface BlockExtension { export interface BlockExtension {
totalFees?: number; totalFees?: number;
medianFee?: number; // Actually the median fee rate that we compute ourself medianFee?: number;
feeRange?: number[]; feeRange?: number[];
reward?: number; reward?: number;
coinbaseTx?: TransactionMinerInfo; coinbaseTx?: TransactionMinerInfo;

View File

@ -718,20 +718,22 @@ class Routes {
} }
} }
public async getBlocksExtras(req: Request, res: Response) { public async getBlocks(req: Request, res: Response) {
try { try {
const height = req.params.height === undefined ? undefined : parseInt(req.params.height, 10); if (['mainnet', 'testnet', 'signet', 'regtest'].includes(config.MEMPOOL.NETWORK)) { // Bitcoin
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); const height = req.params.height === undefined ? undefined : parseInt(req.params.height, 10);
res.json(await blocks.$getBlocksExtras(height, 15)); res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
res.json(await blocks.$getBlocks(height, 15));
} else { // Liquid, Bisq
return await this.getLegacyBlocks(req, res);
}
} catch (e) { } catch (e) {
res.status(500).send(e instanceof Error ? e.message : e); res.status(500).send(e instanceof Error ? e.message : e);
} }
} }
public async getBlocks(req: Request, res: Response) { public async getLegacyBlocks(req: Request, res: Response) {
try { try {
loadingIndicators.setProgress('blocks', 0);
const returnBlocks: IEsploraApi.Block[] = []; const returnBlocks: IEsploraApi.Block[] = [];
const fromHeight = parseInt(req.params.height, 10) || blocks.getCurrentBlockHeight(); const fromHeight = parseInt(req.params.height, 10) || blocks.getCurrentBlockHeight();
@ -755,16 +757,15 @@ class Routes {
returnBlocks.push(block); returnBlocks.push(block);
nextHash = block.previousblockhash; nextHash = block.previousblockhash;
} }
loadingIndicators.setProgress('blocks', i / 10 * 100);
} }
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
res.json(returnBlocks); res.json(returnBlocks);
} catch (e) { } catch (e) {
loadingIndicators.setProgress('blocks', 100);
res.status(500).send(e instanceof Error ? e.message : e); res.status(500).send(e instanceof Error ? e.message : e);
} }
} }
public async getBlockTransactions(req: Request, res: Response) { public async getBlockTransactions(req: Request, res: Response) {
try { try {
loadingIndicators.setProgress('blocktxs-' + req.params.hash, 0); loadingIndicators.setProgress('blocktxs-' + req.params.hash, 0);

View File

@ -15,7 +15,7 @@ export function prepareBlock(block: any): BlockExtended {
weight: block.weight, weight: block.weight,
previousblockhash: block.previousblockhash, previousblockhash: block.previousblockhash,
extras: { extras: {
coinbaseRaw: block.coinbase_raw ?? block.extras.coinbaseRaw, coinbaseRaw: block.coinbase_raw ?? block.extras?.coinbaseRaw,
medianFee: block.medianFee ?? block.median_fee ?? block.extras?.medianFee, medianFee: block.medianFee ?? block.median_fee ?? block.extras?.medianFee,
feeRange: block.feeRange ?? block.fee_span, feeRange: block.feeRange ?? block.fee_span,
reward: block.reward ?? block?.extras?.reward, reward: block.reward ?? block?.extras?.reward,

View File

@ -3900,9 +3900,9 @@
} }
}, },
"node_modules/@socket.io/component-emitter": { "node_modules/@socket.io/component-emitter": {
"version": "3.0.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.0.0.tgz", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
"integrity": "sha512-2pTGuibAXJswAPJjaKisthqS/NOK5ypG4LYT6tEAV0S/mxW0zOIvYvGK0V8w8+SHxAm6vRMSjqSalFXeBAqs+Q==", "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==",
"dev": true "dev": true
}, },
"node_modules/@tootallnate/once": { "node_modules/@tootallnate/once": {
@ -4755,15 +4755,17 @@
} }
}, },
"node_modules/async": { "node_modules/async": {
"version": "1.5.2", "version": "2.6.4",
"resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
"integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
"dev": true "dependencies": {
"lodash": "^4.17.14"
}
}, },
"node_modules/async-each-series": { "node_modules/async-each-series": {
"version": "0.1.1", "version": "0.1.1",
"resolved": "https://registry.npmjs.org/async-each-series/-/async-each-series-0.1.1.tgz", "resolved": "https://registry.npmjs.org/async-each-series/-/async-each-series-0.1.1.tgz",
"integrity": "sha1-dhfBkXQB/Yykooqtzj266Yr+tDI=", "integrity": "sha512-p4jj6Fws4Iy2m0iCmI2am2ZNZCgbdgE+P8F/8csmn2vx7ixXrO2zGcuNsD46X5uZSVecmkEy/M06X2vG8KD6dQ==",
"dev": true, "dev": true,
"engines": { "engines": {
"node": ">=0.8.0" "node": ">=0.8.0"
@ -4996,12 +4998,6 @@
"@babel/core": "^7.0.0-0" "@babel/core": "^7.0.0-0"
} }
}, },
"node_modules/backo2": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
"integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=",
"dev": true
},
"node_modules/balanced-match": { "node_modules/balanced-match": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
@ -5254,13 +5250,13 @@
} }
}, },
"node_modules/browser-sync": { "node_modules/browser-sync": {
"version": "2.27.9", "version": "2.27.10",
"resolved": "https://registry.npmjs.org/browser-sync/-/browser-sync-2.27.9.tgz", "resolved": "https://registry.npmjs.org/browser-sync/-/browser-sync-2.27.10.tgz",
"integrity": "sha512-3zBtggcaZIeU9so4ja9yxk7/CZu9B3DOL6zkxFpzHCHsQmkGBPVXg61jItbeoa+WXgNLnr1sYES/2yQwyEZ2+w==", "integrity": "sha512-xKm+6KJmJu6RuMWWbFkKwOCSqQOxYe3nOrFkKI5Tr/ZzjPxyU3pFShKK3tWnazBo/3lYQzN7fzjixG8fwJh1Xw==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"browser-sync-client": "^2.27.9", "browser-sync-client": "^2.27.10",
"browser-sync-ui": "^2.27.9", "browser-sync-ui": "^2.27.10",
"bs-recipes": "1.3.4", "bs-recipes": "1.3.4",
"bs-snippet-injector": "^2.0.1", "bs-snippet-injector": "^2.0.1",
"chokidar": "^3.5.1", "chokidar": "^3.5.1",
@ -5277,7 +5273,7 @@
"localtunnel": "^2.0.1", "localtunnel": "^2.0.1",
"micromatch": "^4.0.2", "micromatch": "^4.0.2",
"opn": "5.3.0", "opn": "5.3.0",
"portscanner": "2.1.1", "portscanner": "2.2.0",
"qs": "6.2.3", "qs": "6.2.3",
"raw-body": "^2.3.2", "raw-body": "^2.3.2",
"resp-modifier": "6.0.2", "resp-modifier": "6.0.2",
@ -5298,15 +5294,16 @@
} }
}, },
"node_modules/browser-sync-client": { "node_modules/browser-sync-client": {
"version": "2.27.9", "version": "2.27.10",
"resolved": "https://registry.npmjs.org/browser-sync-client/-/browser-sync-client-2.27.9.tgz", "resolved": "https://registry.npmjs.org/browser-sync-client/-/browser-sync-client-2.27.10.tgz",
"integrity": "sha512-FHW8kydp7FXo6jnX3gXJCpHAHtWNLK0nx839nnK+boMfMI1n4KZd0+DmTxHBsHsF3OHud4V4jwoN8U5HExMIdQ==", "integrity": "sha512-KCFKA1YDj6cNul0VsA28apohtBsdk5Wv8T82ClOZPZMZWxPj4Ny5AUbrj9UlAb/k6pdxE5HABrWDhP9+cjt4HQ==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"etag": "1.8.1", "etag": "1.8.1",
"fresh": "0.5.2", "fresh": "0.5.2",
"mitt": "^1.1.3", "mitt": "^1.1.3",
"rxjs": "^5.5.6" "rxjs": "^5.5.6",
"typescript": "^4.6.2"
}, },
"engines": { "engines": {
"node": ">=8.0.0" "node": ">=8.0.0"
@ -5333,10 +5330,23 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/browser-sync-client/node_modules/typescript": {
"version": "4.6.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz",
"integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=4.2.0"
}
},
"node_modules/browser-sync-ui": { "node_modules/browser-sync-ui": {
"version": "2.27.9", "version": "2.27.10",
"resolved": "https://registry.npmjs.org/browser-sync-ui/-/browser-sync-ui-2.27.9.tgz", "resolved": "https://registry.npmjs.org/browser-sync-ui/-/browser-sync-ui-2.27.10.tgz",
"integrity": "sha512-rsduR2bRIwFvM8CX6iY/Nu5aWub0WB9zfSYg9Le/RV5N5DEyxJYey0VxdfWCnzDOoelassTDzYQo+r0iJno3qw==", "integrity": "sha512-elbJILq4Uo6OQv6gsvS3Y9vRAJlWu+h8j0JDkF0X/ua+3S6SVbbiWnZc8sNOFlG7yvVGIwBED3eaYQ0iBo1Dtw==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"async-each-series": "0.1.1", "async-each-series": "0.1.1",
@ -7891,20 +7901,16 @@
} }
}, },
"node_modules/engine.io-client": { "node_modules/engine.io-client": {
"version": "6.1.1", "version": "6.2.2",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.1.1.tgz", "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.2.2.tgz",
"integrity": "sha512-V05mmDo4gjimYW+FGujoGmmmxRaDsrVr7AXA3ZIfa04MWM1jOfZfUwou0oNqhNwy/votUDvGDt4JA4QF4e0b4g==", "integrity": "sha512-8ZQmx0LQGRTYkHuogVZuGSpDqYZtCM/nv8zQ68VZ+JkOpazJ7ICdsSpaO6iXwvaU30oFg5QJOJWj8zWqhbKjkQ==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@socket.io/component-emitter": "~3.0.0", "@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.1", "debug": "~4.3.1",
"engine.io-parser": "~5.0.0", "engine.io-parser": "~5.0.3",
"has-cors": "1.1.0",
"parseqs": "0.0.6",
"parseuri": "0.0.6",
"ws": "~8.2.3", "ws": "~8.2.3",
"xmlhttprequest-ssl": "~2.0.0", "xmlhttprequest-ssl": "~2.0.0"
"yeast": "0.1.2"
} }
}, },
"node_modules/engine.io-client/node_modules/ws": { "node_modules/engine.io-client/node_modules/ws": {
@ -9571,12 +9577,6 @@
"resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz",
"integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==" "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA=="
}, },
"node_modules/has-cors": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
"integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=",
"dev": true
},
"node_modules/has-flag": { "node_modules/has-flag": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
@ -13181,18 +13181,6 @@
"parse5": "^6.0.1" "parse5": "^6.0.1"
} }
}, },
"node_modules/parseqs": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz",
"integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==",
"dev": true
},
"node_modules/parseuri": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz",
"integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==",
"dev": true
},
"node_modules/parseurl": { "node_modules/parseurl": {
"version": "1.3.3", "version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
@ -13366,14 +13354,6 @@
"node": ">= 0.12.0" "node": ">= 0.12.0"
} }
}, },
"node_modules/portfinder/node_modules/async": {
"version": "2.6.4",
"resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
"integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
"dependencies": {
"lodash": "^4.17.14"
}
},
"node_modules/portfinder/node_modules/debug": { "node_modules/portfinder/node_modules/debug": {
"version": "3.2.7", "version": "3.2.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
@ -13383,12 +13363,12 @@
} }
}, },
"node_modules/portscanner": { "node_modules/portscanner": {
"version": "2.1.1", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/portscanner/-/portscanner-2.1.1.tgz", "resolved": "https://registry.npmjs.org/portscanner/-/portscanner-2.2.0.tgz",
"integrity": "sha1-6rtAnk3iSVD1oqUW01rnaTQ/u5Y=", "integrity": "sha512-IFroCz/59Lqa2uBvzK3bKDbDDIEaAY8XJ1jFxcLWTqosrsc32//P4VuSB2vZXoHiHqOmx8B5L5hnKOxL/7FlPw==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"async": "1.5.2", "async": "^2.6.0",
"is-number-like": "^1.0.3" "is-number-like": "^1.0.3"
}, },
"engines": { "engines": {
@ -15098,29 +15078,27 @@
"devOptional": true "devOptional": true
}, },
"node_modules/socket.io-client": { "node_modules/socket.io-client": {
"version": "4.4.1", "version": "4.5.1",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.4.1.tgz", "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.5.1.tgz",
"integrity": "sha512-N5C/L5fLNha5Ojd7Yeb/puKcPWWcoB/A09fEjjNsg91EDVr5twk/OEyO6VT9dlLSUNY85NpW6KBhVMvaLKQ3vQ==", "integrity": "sha512-e6nLVgiRYatS+AHXnOnGi4ocOpubvOUCGhyWw8v+/FxW8saHkinG6Dfhi9TU0Kt/8mwJIAASxvw6eujQmjdZVA==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@socket.io/component-emitter": "~3.0.0", "@socket.io/component-emitter": "~3.1.0",
"backo2": "~1.0.2",
"debug": "~4.3.2", "debug": "~4.3.2",
"engine.io-client": "~6.1.1", "engine.io-client": "~6.2.1",
"parseuri": "0.0.6", "socket.io-parser": "~4.2.0"
"socket.io-parser": "~4.1.1"
}, },
"engines": { "engines": {
"node": ">=10.0.0" "node": ">=10.0.0"
} }
}, },
"node_modules/socket.io-client/node_modules/socket.io-parser": { "node_modules/socket.io-client/node_modules/socket.io-parser": {
"version": "4.1.2", "version": "4.2.0",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.1.2.tgz", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.0.tgz",
"integrity": "sha512-j3kk71QLJuyQ/hh5F/L2t1goqzdTL0gvDzuhTuNSwihfuFUrcSji0qFZmJJPtG6Rmug153eOPsUizeirf1IIog==", "integrity": "sha512-tLfmEwcEwnlQTxFB7jibL/q2+q8dlVQzj4JdRLJ/W/G1+Fu9VSxCx1Lo+n1HvXxKnM//dUuD0xgiA7tQf57Vng==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@socket.io/component-emitter": "~3.0.0", "@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.1" "debug": "~4.3.1"
}, },
"engines": { "engines": {
@ -17264,12 +17242,6 @@
"fd-slicer": "~1.1.0" "fd-slicer": "~1.1.0"
} }
}, },
"node_modules/yeast": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
"integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=",
"dev": true
},
"node_modules/yn": { "node_modules/yn": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
@ -20020,9 +19992,9 @@
"devOptional": true "devOptional": true
}, },
"@socket.io/component-emitter": { "@socket.io/component-emitter": {
"version": "3.0.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.0.0.tgz", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
"integrity": "sha512-2pTGuibAXJswAPJjaKisthqS/NOK5ypG4LYT6tEAV0S/mxW0zOIvYvGK0V8w8+SHxAm6vRMSjqSalFXeBAqs+Q==", "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==",
"dev": true "dev": true
}, },
"@tootallnate/once": { "@tootallnate/once": {
@ -20794,15 +20766,17 @@
"optional": true "optional": true
}, },
"async": { "async": {
"version": "1.5.2", "version": "2.6.4",
"resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
"integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
"dev": true "requires": {
"lodash": "^4.17.14"
}
}, },
"async-each-series": { "async-each-series": {
"version": "0.1.1", "version": "0.1.1",
"resolved": "https://registry.npmjs.org/async-each-series/-/async-each-series-0.1.1.tgz", "resolved": "https://registry.npmjs.org/async-each-series/-/async-each-series-0.1.1.tgz",
"integrity": "sha1-dhfBkXQB/Yykooqtzj266Yr+tDI=", "integrity": "sha512-p4jj6Fws4Iy2m0iCmI2am2ZNZCgbdgE+P8F/8csmn2vx7ixXrO2zGcuNsD46X5uZSVecmkEy/M06X2vG8KD6dQ==",
"dev": true "dev": true
}, },
"asynckit": { "asynckit": {
@ -20976,12 +20950,6 @@
"@babel/helper-define-polyfill-provider": "^0.3.1" "@babel/helper-define-polyfill-provider": "^0.3.1"
} }
}, },
"backo2": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
"integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=",
"dev": true
},
"balanced-match": { "balanced-match": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
@ -21210,13 +21178,13 @@
} }
}, },
"browser-sync": { "browser-sync": {
"version": "2.27.9", "version": "2.27.10",
"resolved": "https://registry.npmjs.org/browser-sync/-/browser-sync-2.27.9.tgz", "resolved": "https://registry.npmjs.org/browser-sync/-/browser-sync-2.27.10.tgz",
"integrity": "sha512-3zBtggcaZIeU9so4ja9yxk7/CZu9B3DOL6zkxFpzHCHsQmkGBPVXg61jItbeoa+WXgNLnr1sYES/2yQwyEZ2+w==", "integrity": "sha512-xKm+6KJmJu6RuMWWbFkKwOCSqQOxYe3nOrFkKI5Tr/ZzjPxyU3pFShKK3tWnazBo/3lYQzN7fzjixG8fwJh1Xw==",
"dev": true, "dev": true,
"requires": { "requires": {
"browser-sync-client": "^2.27.9", "browser-sync-client": "^2.27.10",
"browser-sync-ui": "^2.27.9", "browser-sync-ui": "^2.27.10",
"bs-recipes": "1.3.4", "bs-recipes": "1.3.4",
"bs-snippet-injector": "^2.0.1", "bs-snippet-injector": "^2.0.1",
"chokidar": "^3.5.1", "chokidar": "^3.5.1",
@ -21233,7 +21201,7 @@
"localtunnel": "^2.0.1", "localtunnel": "^2.0.1",
"micromatch": "^4.0.2", "micromatch": "^4.0.2",
"opn": "5.3.0", "opn": "5.3.0",
"portscanner": "2.1.1", "portscanner": "2.2.0",
"qs": "6.2.3", "qs": "6.2.3",
"raw-body": "^2.3.2", "raw-body": "^2.3.2",
"resp-modifier": "6.0.2", "resp-modifier": "6.0.2",
@ -21343,15 +21311,16 @@
} }
}, },
"browser-sync-client": { "browser-sync-client": {
"version": "2.27.9", "version": "2.27.10",
"resolved": "https://registry.npmjs.org/browser-sync-client/-/browser-sync-client-2.27.9.tgz", "resolved": "https://registry.npmjs.org/browser-sync-client/-/browser-sync-client-2.27.10.tgz",
"integrity": "sha512-FHW8kydp7FXo6jnX3gXJCpHAHtWNLK0nx839nnK+boMfMI1n4KZd0+DmTxHBsHsF3OHud4V4jwoN8U5HExMIdQ==", "integrity": "sha512-KCFKA1YDj6cNul0VsA28apohtBsdk5Wv8T82ClOZPZMZWxPj4Ny5AUbrj9UlAb/k6pdxE5HABrWDhP9+cjt4HQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"etag": "1.8.1", "etag": "1.8.1",
"fresh": "0.5.2", "fresh": "0.5.2",
"mitt": "^1.1.3", "mitt": "^1.1.3",
"rxjs": "^5.5.6" "rxjs": "^5.5.6",
"typescript": "^4.6.2"
}, },
"dependencies": { "dependencies": {
"rxjs": { "rxjs": {
@ -21368,13 +21337,19 @@
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz",
"integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=", "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=",
"dev": true "dev": true
},
"typescript": {
"version": "4.6.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz",
"integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==",
"dev": true
} }
} }
}, },
"browser-sync-ui": { "browser-sync-ui": {
"version": "2.27.9", "version": "2.27.10",
"resolved": "https://registry.npmjs.org/browser-sync-ui/-/browser-sync-ui-2.27.9.tgz", "resolved": "https://registry.npmjs.org/browser-sync-ui/-/browser-sync-ui-2.27.10.tgz",
"integrity": "sha512-rsduR2bRIwFvM8CX6iY/Nu5aWub0WB9zfSYg9Le/RV5N5DEyxJYey0VxdfWCnzDOoelassTDzYQo+r0iJno3qw==", "integrity": "sha512-elbJILq4Uo6OQv6gsvS3Y9vRAJlWu+h8j0JDkF0X/ua+3S6SVbbiWnZc8sNOFlG7yvVGIwBED3eaYQ0iBo1Dtw==",
"dev": true, "dev": true,
"requires": { "requires": {
"async-each-series": "0.1.1", "async-each-series": "0.1.1",
@ -23395,20 +23370,16 @@
} }
}, },
"engine.io-client": { "engine.io-client": {
"version": "6.1.1", "version": "6.2.2",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.1.1.tgz", "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.2.2.tgz",
"integrity": "sha512-V05mmDo4gjimYW+FGujoGmmmxRaDsrVr7AXA3ZIfa04MWM1jOfZfUwou0oNqhNwy/votUDvGDt4JA4QF4e0b4g==", "integrity": "sha512-8ZQmx0LQGRTYkHuogVZuGSpDqYZtCM/nv8zQ68VZ+JkOpazJ7ICdsSpaO6iXwvaU30oFg5QJOJWj8zWqhbKjkQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@socket.io/component-emitter": "~3.0.0", "@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.1", "debug": "~4.3.1",
"engine.io-parser": "~5.0.0", "engine.io-parser": "~5.0.3",
"has-cors": "1.1.0",
"parseqs": "0.0.6",
"parseuri": "0.0.6",
"ws": "~8.2.3", "ws": "~8.2.3",
"xmlhttprequest-ssl": "~2.0.0", "xmlhttprequest-ssl": "~2.0.0"
"yeast": "0.1.2"
}, },
"dependencies": { "dependencies": {
"ws": { "ws": {
@ -24622,12 +24593,6 @@
"resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz",
"integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==" "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA=="
}, },
"has-cors": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
"integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=",
"dev": true
},
"has-flag": { "has-flag": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
@ -27435,18 +27400,6 @@
"parse5": "^6.0.1" "parse5": "^6.0.1"
} }
}, },
"parseqs": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz",
"integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==",
"dev": true
},
"parseuri": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz",
"integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==",
"dev": true
},
"parseurl": { "parseurl": {
"version": "1.3.3", "version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
@ -27576,14 +27529,6 @@
"mkdirp": "^0.5.5" "mkdirp": "^0.5.5"
}, },
"dependencies": { "dependencies": {
"async": {
"version": "2.6.4",
"resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
"integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
"requires": {
"lodash": "^4.17.14"
}
},
"debug": { "debug": {
"version": "3.2.7", "version": "3.2.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
@ -27595,12 +27540,12 @@
} }
}, },
"portscanner": { "portscanner": {
"version": "2.1.1", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/portscanner/-/portscanner-2.1.1.tgz", "resolved": "https://registry.npmjs.org/portscanner/-/portscanner-2.2.0.tgz",
"integrity": "sha1-6rtAnk3iSVD1oqUW01rnaTQ/u5Y=", "integrity": "sha512-IFroCz/59Lqa2uBvzK3bKDbDDIEaAY8XJ1jFxcLWTqosrsc32//P4VuSB2vZXoHiHqOmx8B5L5hnKOxL/7FlPw==",
"dev": true, "dev": true,
"requires": { "requires": {
"async": "1.5.2", "async": "^2.6.0",
"is-number-like": "^1.0.3" "is-number-like": "^1.0.3"
} }
}, },
@ -28879,26 +28824,24 @@
"devOptional": true "devOptional": true
}, },
"socket.io-client": { "socket.io-client": {
"version": "4.4.1", "version": "4.5.1",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.4.1.tgz", "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.5.1.tgz",
"integrity": "sha512-N5C/L5fLNha5Ojd7Yeb/puKcPWWcoB/A09fEjjNsg91EDVr5twk/OEyO6VT9dlLSUNY85NpW6KBhVMvaLKQ3vQ==", "integrity": "sha512-e6nLVgiRYatS+AHXnOnGi4ocOpubvOUCGhyWw8v+/FxW8saHkinG6Dfhi9TU0Kt/8mwJIAASxvw6eujQmjdZVA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@socket.io/component-emitter": "~3.0.0", "@socket.io/component-emitter": "~3.1.0",
"backo2": "~1.0.2",
"debug": "~4.3.2", "debug": "~4.3.2",
"engine.io-client": "~6.1.1", "engine.io-client": "~6.2.1",
"parseuri": "0.0.6", "socket.io-parser": "~4.2.0"
"socket.io-parser": "~4.1.1"
}, },
"dependencies": { "dependencies": {
"socket.io-parser": { "socket.io-parser": {
"version": "4.1.2", "version": "4.2.0",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.1.2.tgz", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.0.tgz",
"integrity": "sha512-j3kk71QLJuyQ/hh5F/L2t1goqzdTL0gvDzuhTuNSwihfuFUrcSji0qFZmJJPtG6Rmug153eOPsUizeirf1IIog==", "integrity": "sha512-tLfmEwcEwnlQTxFB7jibL/q2+q8dlVQzj4JdRLJ/W/G1+Fu9VSxCx1Lo+n1HvXxKnM//dUuD0xgiA7tQf57Vng==",
"dev": true, "dev": true,
"requires": { "requires": {
"@socket.io/component-emitter": "~3.0.0", "@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.1" "debug": "~4.3.1"
} }
} }
@ -30543,12 +30486,6 @@
"fd-slicer": "~1.1.0" "fd-slicer": "~1.1.0"
} }
}, },
"yeast": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
"integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=",
"dev": true
},
"yn": { "yn": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",

View File

@ -7,7 +7,6 @@ import { AddressComponent } from './components/address/address.component';
import { MasterPageComponent } from './components/master-page/master-page.component'; import { MasterPageComponent } from './components/master-page/master-page.component';
import { AboutComponent } from './components/about/about.component'; import { AboutComponent } from './components/about/about.component';
import { StatusViewComponent } from './components/status-view/status-view.component'; import { StatusViewComponent } from './components/status-view/status-view.component';
import { LatestBlocksComponent } from './components/latest-blocks/latest-blocks.component';
import { TermsOfServiceComponent } from './components/terms-of-service/terms-of-service.component'; import { TermsOfServiceComponent } from './components/terms-of-service/terms-of-service.component';
import { PrivacyPolicyComponent } from './components/privacy-policy/privacy-policy.component'; import { PrivacyPolicyComponent } from './components/privacy-policy/privacy-policy.component';
import { TrademarkPolicyComponent } from './components/trademark-policy/trademark-policy.component'; import { TrademarkPolicyComponent } from './components/trademark-policy/trademark-policy.component';
@ -36,19 +35,20 @@ let routes: Routes = [
component: MasterPageComponent, component: MasterPageComponent,
children: [ children: [
{ {
path: 'tx/push', path: 'mining/blocks',
component: PushTransactionComponent, redirectTo: 'blocks',
pathMatch: 'full'
}, },
{ {
path: 'blocks', path: 'tx/push',
component: LatestBlocksComponent, component: PushTransactionComponent,
}, },
{ {
path: 'about', path: 'about',
component: AboutComponent, component: AboutComponent,
}, },
{ {
path: 'mining/blocks', path: 'blocks',
component: BlocksList, component: BlocksList,
}, },
{ {
@ -115,6 +115,11 @@ let routes: Routes = [
{ {
path: 'signet', path: 'signet',
children: [ children: [
{
path: 'mining/blocks',
redirectTo: 'blocks',
pathMatch: 'full'
},
{ {
path: '', path: '',
pathMatch: 'full', pathMatch: 'full',
@ -128,16 +133,12 @@ let routes: Routes = [
path: 'tx/push', path: 'tx/push',
component: PushTransactionComponent, component: PushTransactionComponent,
}, },
{
path: 'blocks',
component: LatestBlocksComponent,
},
{ {
path: 'about', path: 'about',
component: AboutComponent, component: AboutComponent,
}, },
{ {
path: 'mining/blocks', path: 'blocks',
component: BlocksList, component: BlocksList,
}, },
{ {
@ -211,19 +212,20 @@ let routes: Routes = [
component: MasterPageComponent, component: MasterPageComponent,
children: [ children: [
{ {
path: 'tx/push', path: 'mining/blocks',
component: PushTransactionComponent, redirectTo: 'blocks',
pathMatch: 'full'
}, },
{ {
path: 'blocks', path: 'tx/push',
component: LatestBlocksComponent, component: PushTransactionComponent,
}, },
{ {
path: 'about', path: 'about',
component: AboutComponent, component: AboutComponent,
}, },
{ {
path: 'mining/blocks', path: 'blocks',
component: BlocksList, component: BlocksList,
}, },
{ {
@ -321,16 +323,12 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') {
path: 'tx/push', path: 'tx/push',
component: PushTransactionComponent, component: PushTransactionComponent,
}, },
{
path: 'blocks',
component: LatestBlocksComponent,
},
{ {
path: 'about', path: 'about',
component: AboutComponent, component: AboutComponent,
}, },
{ {
path: 'mining/blocks', path: 'blocks',
component: BlocksList, component: BlocksList,
}, },
{ {
@ -429,16 +427,12 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') {
path: 'tx/push', path: 'tx/push',
component: PushTransactionComponent, component: PushTransactionComponent,
}, },
{
path: 'blocks',
component: LatestBlocksComponent,
},
{ {
path: 'about', path: 'about',
component: AboutComponent, component: AboutComponent,
}, },
{ {
path: 'mining/blocks', path: 'blocks',
component: BlocksList, component: BlocksList,
}, },
{ {

View File

@ -197,7 +197,18 @@
<app-transactions-list [transactions]="transactions" [paginated]="true"></app-transactions-list> <app-transactions-list [transactions]="transactions" [paginated]="true"></app-transactions-list>
<ng-template [ngIf]="isLoadingTransactions"> <ng-template [ngIf]="transactionsError">
<div class="text-center">
<br>
<span i18n="error.general-loading-data">Error loading data.</span>
<br><br>
<i>{{ transactionsError.status }}: {{ transactionsError.error }}</i>
<br>
<br>
</div>
</ng-template>
<ng-template [ngIf]="isLoadingTransactions && !transactionsError">
<div class="text-center mb-4" class="tx-skeleton"> <div class="text-center mb-4" class="tx-skeleton">
<ng-container *ngIf="(txsLoadingStatus$ | async) as txsLoadingStatus; else headerLoader"> <ng-container *ngIf="(txsLoadingStatus$ | async) as txsLoadingStatus; else headerLoader">
@ -271,9 +282,9 @@
<ng-template [ngIf]="error"> <ng-template [ngIf]="error">
<div class="text-center"> <div class="text-center">
<span i18n="block.error.loading-block-data">Error loading block data.</span> <span i18n="error.general-loading-data">Error loading data.</span>
<br><br> <br><br>
<i>{{ error.error }}</i> <i>{{ error.code }}: {{ error.error }}</i>
</div> </div>
</ng-template> </ng-template>

View File

@ -38,6 +38,7 @@ export class BlockComponent implements OnInit, OnDestroy {
showDetails = false; showDetails = false;
showPreviousBlocklink = true; showPreviousBlocklink = true;
showNextBlocklink = true; showNextBlocklink = true;
transactionsError: any = null;
subscription: Subscription; subscription: Subscription;
keyNavigationSubscription: Subscription; keyNavigationSubscription: Subscription;
@ -152,12 +153,13 @@ export class BlockComponent implements OnInit, OnDestroy {
this.stateService.markBlock$.next({ blockHeight: this.blockHeight }); this.stateService.markBlock$.next({ blockHeight: this.blockHeight });
this.isLoadingTransactions = true; this.isLoadingTransactions = true;
this.transactions = null; this.transactions = null;
this.transactionsError = null;
}), }),
debounceTime(300), debounceTime(300),
switchMap((block) => this.electrsApiService.getBlockTransactions$(block.id) switchMap((block) => this.electrsApiService.getBlockTransactions$(block.id)
.pipe( .pipe(
catchError((err) => { catchError((err) => {
console.log(err); this.transactionsError = err;
return of([]); return of([]);
})) }))
), ),
@ -218,9 +220,16 @@ export class BlockComponent implements OnInit, OnDestroy {
const start = (page - 1) * this.itemsPerPage; const start = (page - 1) * this.itemsPerPage;
this.isLoadingTransactions = true; this.isLoadingTransactions = true;
this.transactions = null; this.transactions = null;
this.transactionsError = null;
target.scrollIntoView(); // works for chrome target.scrollIntoView(); // works for chrome
this.electrsApiService.getBlockTransactions$(this.block.id, start) this.electrsApiService.getBlockTransactions$(this.block.id, start)
.pipe(
catchError((err) => {
this.transactionsError = err;
return of([]);
})
)
.subscribe((transactions) => { .subscribe((transactions) => {
this.transactions = transactions; this.transactions = transactions;
this.isLoadingTransactions = false; this.isLoadingTransactions = false;

View File

@ -1,6 +1,6 @@
<app-indexing-progress *ngIf="!widget"></app-indexing-progress> <app-indexing-progress *ngIf="!widget"></app-indexing-progress>
<div class="container-xl" [class]="widget ? 'widget' : 'full-height'"> <div class="container-xl" style="min-height: 335px" [ngClass]="{'widget': widget, 'full-height': !widget, 'legacy': !indexingAvailable}">
<h1 *ngIf="!widget" class="float-left" i18n="master-page.blocks">Blocks</h1> <h1 *ngIf="!widget" class="float-left" i18n="master-page.blocks">Blocks</h1>
<div class="clearfix"></div> <div class="clearfix"></div>
@ -8,22 +8,22 @@
<div style="min-height: 295px"> <div style="min-height: 295px">
<table class="table table-borderless"> <table class="table table-borderless">
<thead> <thead>
<th class="height text-left" [class]="widget ? 'widget' : ''" i18n="latest-blocks.height">Height</th> <th class="height text-left" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}" i18n="latest-blocks.height">Height</th>
<th class="pool text-left" [class]="widget ? 'widget' : ''" i18n="mining.pool-name">Pool</th> <th *ngIf="indexingAvailable" class="pool text-left" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}" i18n="mining.pool-name">Pool</th>
<th class="timestamp" i18n="latest-blocks.timestamp" *ngIf="!widget">Timestamp</th> <th class="timestamp" i18n="latest-blocks.timestamp" *ngIf="!widget" [class]="indexingAvailable ? '' : 'legacy'">Timestamp</th>
<th class="mined" i18n="latest-blocks.mined" *ngIf="!widget">Mined</th> <th class="mined" i18n="latest-blocks.mined" *ngIf="!widget" [class]="indexingAvailable ? '' : 'legacy'">Mined</th>
<th class="reward text-right" i18n="latest-blocks.reward" [class]="widget ? 'widget' : ''">Reward</th> <th *ngIf="indexingAvailable" class="reward text-right" i18n="latest-blocks.reward" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}">Reward</th>
<th class="fees text-right" i18n="latest-blocks.fees" *ngIf="!widget">Fees</th> <th *ngIf="indexingAvailable && !widget" class="fees text-right" i18n="latest-blocks.fees" [class]="indexingAvailable ? '' : 'legacy'">Fees</th>
<th class="txs text-right" i18n="dashboard.txs" [class]="widget ? 'widget' : ''">TXs</th> <th *ngIf="indexingAvailable" class="txs text-right" i18n="dashboard.txs" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}">TXs</th>
<th class="size" i18n="latest-blocks.size" *ngIf="!widget">Size</th> <th *ngIf="!indexingAvailable" class="txs text-right" i18n="dashboard.txs" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}">Transactions</th>
<th class="size" i18n="latest-blocks.size" *ngIf="!widget" [class]="indexingAvailable ? '' : 'legacy'">Size</th>
</thead> </thead>
<tbody *ngIf="blocks$ | async as blocks; else skeleton" [style]="isLoading ? 'opacity: 0.75' : ''"> <tbody *ngIf="blocks$ | async as blocks; else skeleton" [style]="isLoading ? 'opacity: 0.75' : ''">
<tr *ngFor="let block of blocks; let i= index; trackBy: trackByBlock"> <tr *ngFor="let block of blocks; let i= index; trackBy: trackByBlock">
<td class="text-left" [class]="widget ? 'widget' : ''"> <td class="text-left" [class]="widget ? 'widget' : ''">
<a [routerLink]="['/block' | relativeUrl, block.height]">{{ block.height <a [routerLink]="['/block' | relativeUrl, block.height]">{{ block.height }}</a>
}}</a>
</td> </td>
<td class="pool text-left" [class]="widget ? 'widget' : ''"> <td *ngIf="indexingAvailable" class="pool text-left" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}">
<div class="tooltip-custom"> <div class="tooltip-custom">
<a class="clear-link" [routerLink]="['/mining/pool' | relativeUrl, block.extras.pool.slug]"> <a class="clear-link" [routerLink]="['/mining/pool' | relativeUrl, block.extras.pool.slug]">
<img width="22" height="22" src="{{ block.extras.pool['logo'] }}" <img width="22" height="22" src="{{ block.extras.pool['logo'] }}"
@ -36,19 +36,19 @@
<td class="timestamp" *ngIf="!widget"> <td class="timestamp" *ngIf="!widget">
&lrm;{{ block.timestamp * 1000 | date:'yyyy-MM-dd HH:mm' }} &lrm;{{ block.timestamp * 1000 | date:'yyyy-MM-dd HH:mm' }}
</td> </td>
<td class="mined" *ngIf="!widget"> <td class="mined" *ngIf="!widget" [class]="indexingAvailable ? '' : 'legacy'">
<app-time-since [time]="block.timestamp" [fastRender]="true"></app-time-since> <app-time-since [time]="block.timestamp" [fastRender]="true"></app-time-since>
</td> </td>
<td class="reward text-right" [class]="widget ? 'widget' : ''"> <td *ngIf="indexingAvailable" class="reward text-right" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}">
<app-amount [satoshis]="block.extras.reward" [noFiat]="true" digitsInfo="1.2-2"></app-amount> <app-amount [satoshis]="block.extras.reward" [noFiat]="true" digitsInfo="1.2-2"></app-amount>
</td> </td>
<td class="fees text-right" *ngIf="!widget"> <td *ngIf="indexingAvailable && !widget" class="fees text-right" [class]="indexingAvailable ? '' : 'legacy'">
<app-amount [satoshis]="block.extras.totalFees" [noFiat]="true" digitsInfo="1.2-2"></app-amount> <app-amount [satoshis]="block.extras.totalFees" [noFiat]="true" digitsInfo="1.2-2"></app-amount>
</td> </td>
<td class="txs text-right" [class]="widget ? 'widget' : ''"> <td class="txs text-right" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}">
{{ block.tx_count | number }} {{ block.tx_count | number }}
</td> </td>
<td class="size" *ngIf="!widget"> <td class="size" *ngIf="!widget" [class]="indexingAvailable ? '' : 'legacy'">
<div class="progress"> <div class="progress">
<div class="progress-bar progress-mempool" role="progressbar" <div class="progress-bar progress-mempool" role="progressbar"
[ngStyle]="{'width': (block.weight / stateService.env.BLOCK_WEIGHT_UNITS)*100 + '%' }"></div> [ngStyle]="{'width': (block.weight / stateService.env.BLOCK_WEIGHT_UNITS)*100 + '%' }"></div>
@ -60,29 +60,29 @@
<ng-template #skeleton> <ng-template #skeleton>
<tbody> <tbody>
<tr *ngFor="let item of skeletonLines"> <tr *ngFor="let item of skeletonLines">
<td class="height text-left" [class]="widget ? 'widget' : ''"> <td class="height text-left" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}">
<span class="skeleton-loader" style="max-width: 75px"></span> <span class="skeleton-loader" style="max-width: 75px"></span>
</td> </td>
<td class="pool text-left" [class]="widget ? 'widget' : ''"> <td *ngIf="indexingAvailable" class="pool text-left" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}">
<img width="1" height="25" style="opacity: 0"> <img width="1" height="25" style="opacity: 0">
<span class="skeleton-loader" style="max-width: 125px"></span> <span class="skeleton-loader" style="max-width: 125px"></span>
</td> </td>
<td class="timestamp" *ngIf="!widget"> <td class="timestamp" *ngIf="!widget" [class]="indexingAvailable ? '' : 'legacy'">
<span class="skeleton-loader" style="max-width: 150px"></span> <span class="skeleton-loader" style="max-width: 150px"></span>
</td> </td>
<td class="mined" *ngIf="!widget"> <td class="mined" *ngIf="!widget" [class]="indexingAvailable ? '' : 'legacy'">
<span class="skeleton-loader" style="max-width: 125px"></span> <span class="skeleton-loader" style="max-width: 125px"></span>
</td> </td>
<td class="reward text-right" [class]="widget ? 'widget' : ''"> <td *ngIf="indexingAvailable" class="reward text-right" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}">
<span class="skeleton-loader" style="max-width: 75px"></span> <span class="skeleton-loader" style="max-width: 75px"></span>
</td> </td>
<td class="fees text-right" *ngIf="!widget"> <td *ngIf="indexingAvailable && !widget" class="fees text-right" [class]="indexingAvailable ? '' : 'legacy'">
<span class="skeleton-loader" style="max-width: 75px"></span> <span class="skeleton-loader" style="max-width: 75px"></span>
</td> </td>
<td class="txs text-right" [class]="widget ? 'widget' : ''"> <td class="txs text-right" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}">
<span class="skeleton-loader" style="max-width: 75px"></span> <span class="skeleton-loader" style="max-width: 75px"></span>
</td> </td>
<td class="size" *ngIf="!widget"> <td class="size" *ngIf="!widget" [class]="indexingAvailable ? '' : 'legacy'">
<span class="skeleton-loader"></span> <span class="skeleton-loader"></span>
</td> </td>
</tr> </tr>

View File

@ -6,6 +6,9 @@
padding-left: 0px; padding-left: 0px;
padding-bottom: 0px; padding-bottom: 0px;
} }
.container-xl.legacy {
max-width: 1140px;
}
.container { .container {
max-width: 100%; max-width: 100%;
@ -58,6 +61,9 @@ tr, td, th {
width: 10%; width: 10%;
} }
} }
.height.legacy {
width: 15%;
}
.timestamp { .timestamp {
width: 18%; width: 18%;
@ -65,6 +71,9 @@ tr, td, th {
display: none; display: none;
} }
} }
.timestamp.legacy {
width: 20%;
}
.mined { .mined {
width: 13%; width: 13%;
@ -72,6 +81,12 @@ tr, td, th {
display: none; display: none;
} }
} }
.mined.legacy {
width: 15%;
@media (max-width: 576px) {
display: table-cell;
}
}
.txs { .txs {
padding-right: 40px; padding-right: 40px;
@ -88,6 +103,10 @@ tr, td, th {
display: none; display: none;
} }
} }
.txs.legacy {
padding-right: 80px;
width: 10%;
}
.fees { .fees {
width: 10%; width: 10%;
@ -126,6 +145,12 @@ tr, td, th {
display: none; display: none;
} }
} }
.size.legacy {
width: 20%;
@media (max-width: 576px) {
display: table-cell;
}
}
/* Tooltip text */ /* Tooltip text */
.tooltip-custom { .tooltip-custom {

View File

@ -17,6 +17,7 @@ export class BlocksList implements OnInit {
blocks$: Observable<any[]> = undefined; blocks$: Observable<any[]> = undefined;
indexingAvailable = false;
isLoading = true; isLoading = true;
fromBlockHeight = undefined; fromBlockHeight = undefined;
paginationMaxSize: number; paginationMaxSize: number;
@ -35,6 +36,9 @@ export class BlocksList implements OnInit {
} }
ngOnInit(): void { ngOnInit(): void {
this.indexingAvailable = (this.stateService.env.BASE_MODULE === 'mempool' &&
this.stateService.env.MINING_DASHBOARD === true);
if (!this.widget) { if (!this.widget) {
this.websocketService.want(['blocks']); this.websocketService.want(['blocks']);
} }
@ -55,17 +59,19 @@ export class BlocksList implements OnInit {
this.isLoading = false; this.isLoading = false;
}), }),
map(blocks => { map(blocks => {
for (const block of blocks) { if (this.indexingAvailable) {
// @ts-ignore: Need to add an extra field for the template for (const block of blocks) {
block.extras.pool.logo = `./resources/mining-pools/` + // @ts-ignore: Need to add an extra field for the template
block.extras.pool.name.toLowerCase().replace(' ', '').replace('.', '') + '.svg'; block.extras.pool.logo = `./resources/mining-pools/` +
block.extras.pool.name.toLowerCase().replace(' ', '').replace('.', '') + '.svg';
}
} }
if (this.widget) { if (this.widget) {
return blocks.slice(0, 6); return blocks.slice(0, 6);
} }
return blocks; return blocks;
}), }),
retryWhen(errors => errors.pipe(delayWhen(() => timer(1000)))) retryWhen(errors => errors.pipe(delayWhen(() => timer(10000))))
) )
}) })
), ),
@ -81,9 +87,11 @@ export class BlocksList implements OnInit {
return blocks[0]; return blocks[0];
} }
this.blocksCount = Math.max(this.blocksCount, blocks[1][0].height) + 1; this.blocksCount = Math.max(this.blocksCount, blocks[1][0].height) + 1;
// @ts-ignore: Need to add an extra field for the template if (this.stateService.env.MINING_DASHBOARD) {
blocks[1][0].extras.pool.logo = `./resources/mining-pools/` + // @ts-ignore: Need to add an extra field for the template
blocks[1][0].extras.pool.name.toLowerCase().replace(' ', '').replace('.', '') + '.svg'; blocks[1][0].extras.pool.logo = `./resources/mining-pools/` +
blocks[1][0].extras.pool.name.toLowerCase().replace(' ', '').replace('.', '') + '.svg';
}
acc.unshift(blocks[1][0]); acc.unshift(blocks[1][0]);
acc = acc.slice(0, this.widget ? 6 : 15); acc = acc.slice(0, this.widget ? 6 : 15);
return acc; return acc;

View File

@ -1,4 +1,4 @@
<div style="min-height: 295px"> <div style="min-height: 335px">
<table class="table latest-adjustments"> <table class="table latest-adjustments">
<thead> <thead>
<tr> <tr>

View File

@ -1,57 +0,0 @@
<div class="container-xl">
<h1 class="float-left" i18n="master-page.blocks">Blocks</h1>
<br>
<div class="clearfix"></div>
<table class="table table-borderless" [alwaysCallback]="true" infiniteScroll [infiniteScrollDistance]="1.5" [infiniteScrollUpDistance]="1.5" [infiniteScrollThrottle]="50" (scrolled)="loadMore()">
<thead>
<th style="width: 15%;" i18n="latest-blocks.height">Height</th>
<th class="d-none d-md-block" style="width: 20%;" i18n="latest-blocks.timestamp">Timestamp</th>
<th style="width: 20%;" i18n="latest-blocks.mined">Mined</th>
<th class="d-none d-lg-block" style="width: 15%;" i18n="latest-blocks.transactions">Transactions</th>
<th style="width: 20%;" i18n="latest-blocks.size">Size</th>
</thead>
<tbody>
<tr *ngFor="let block of blocks; let i= index; trackBy: trackByBlock">
<td><a [routerLink]="['/block' | relativeUrl, block.id]" [state]="{ data: { block: block } }">{{ block.height }}</a></td>
<td class="d-none d-md-block">&lrm;{{ block.timestamp * 1000 | date:'yyyy-MM-dd HH:mm' }}</td>
<td><app-time-since [time]="block.timestamp" [fastRender]="true"></app-time-since></td>
<td class="d-none d-lg-block">{{ block.tx_count | number }}</td>
<td>
<div class="progress">
<div class="progress-bar progress-mempool {{ network$ | async }}" role="progressbar" [ngStyle]="{'width': (block.weight / stateService.env.BLOCK_WEIGHT_UNITS)*100 + '%' }"></div>
<div class="progress-text" [innerHTML]="block.size | bytes: 2"></div>
</div>
</td>
</tr>
<ng-template [ngIf]="isLoading">
<tr *ngFor="let item of [1,2,3,4,5,6,7,8,9,10]">
<td><span class="skeleton-loader"></span></td>
<td class="d-none d-md-block"><span class="skeleton-loader"></span></td>
<td><span class="skeleton-loader"></span></td>
<td class="d-none d-lg-block"><span class="skeleton-loader"></span></td>
<td><span class="skeleton-loader"></span></td>
</tr>
<ng-container *ngIf="(blocksLoadingStatus$ | async) as blocksLoadingStatus">
<tr>
<td colspan="5">
<div class="progress progress-dark">
<div class="progress-bar progress-darklight" role="progressbar" [ngStyle]="{'width': blocksLoadingStatus + '%' }"></div>
</div>
</td>
</tr>
</ng-container>
</ng-template>
</tbody>
</table>
<ng-template [ngIf]="error">
<div class="text-center">
<span>Error loading blocks</span>
<br>
<i>{{ error.error }}</i>
</div>
</ng-template>
</div>

View File

@ -1,14 +0,0 @@
.progress {
background-color: #2d3348;
}
@media (min-width: 768px) {
.d-md-block {
display: table-cell !important;
}
}
@media (min-width: 992px) {
.d-lg-block {
display: table-cell !important;
}
}

View File

@ -1,140 +0,0 @@
import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
import { ElectrsApiService } from '../../services/electrs-api.service';
import { StateService } from '../../services/state.service';
import { Block } from '../../interfaces/electrs.interface';
import { Subscription, Observable, merge, of } from 'rxjs';
import { SeoService } from '../../services/seo.service';
import { WebsocketService } from 'src/app/services/websocket.service';
import { map } from 'rxjs/operators';
@Component({
selector: 'app-latest-blocks',
templateUrl: './latest-blocks.component.html',
styleUrls: ['./latest-blocks.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class LatestBlocksComponent implements OnInit, OnDestroy {
network$: Observable<string>;
error: any;
blocks: any[] = [];
blockSubscription: Subscription;
isLoading = true;
interval: any;
blocksLoadingStatus$: Observable<number>;
latestBlockHeight: number;
heightOfPageUntilBlocks = 150;
heightOfBlocksTableChunk = 470;
constructor(
private electrsApiService: ElectrsApiService,
public stateService: StateService,
private seoService: SeoService,
private websocketService: WebsocketService,
private cd: ChangeDetectorRef,
) { }
ngOnInit() {
this.seoService.setTitle($localize`:@@8a7b4bd44c0ac71b2e72de0398b303257f7d2f54:Blocks`);
this.websocketService.want(['blocks']);
this.network$ = merge(of(''), this.stateService.networkChanged$);
this.blocksLoadingStatus$ = this.stateService.loadingIndicators$
.pipe(
map((indicators) => indicators['blocks'] !== undefined ? indicators['blocks'] : 0)
);
this.blockSubscription = this.stateService.blocks$
.subscribe(([block]) => {
if (block === null || !this.blocks.length) {
return;
}
this.latestBlockHeight = block.height;
if (block.height === this.blocks[0].height) {
return;
}
// If we are out of sync, reload the blocks instead
if (block.height > this.blocks[0].height + 1) {
this.loadInitialBlocks();
return;
}
if (block.height <= this.blocks[0].height) {
return;
}
this.blocks.pop();
this.blocks.unshift(block);
this.cd.markForCheck();
});
this.loadInitialBlocks();
}
ngOnDestroy() {
clearInterval(this.interval);
this.blockSubscription.unsubscribe();
}
loadInitialBlocks() {
this.electrsApiService.listBlocks$()
.subscribe((blocks) => {
this.blocks = blocks;
this.isLoading = false;
this.error = undefined;
this.latestBlockHeight = blocks[0].height;
const spaceForBlocks = window.innerHeight - this.heightOfPageUntilBlocks;
const chunks = Math.ceil(spaceForBlocks / this.heightOfBlocksTableChunk) - 1;
if (chunks > 0) {
this.loadMore(chunks);
}
this.cd.markForCheck();
},
(error) => {
console.log(error);
this.error = error;
this.isLoading = false;
this.cd.markForCheck();
});
}
loadMore(chunks = 0) {
if (this.isLoading) {
return;
}
const height = this.blocks[this.blocks.length - 1].height - 1;
if (height < 0) {
return;
}
this.isLoading = true;
this.electrsApiService.listBlocks$(height)
.subscribe((blocks) => {
this.blocks = this.blocks.concat(blocks);
this.isLoading = false;
this.error = undefined;
const chunksLeft = chunks - 1;
if (chunksLeft > 0) {
this.loadMore(chunksLeft);
}
this.cd.markForCheck();
},
(error) => {
console.log(error);
this.error = error;
this.isLoading = false;
this.cd.markForCheck();
});
}
trackByBlock(index: number, block: Block) {
return block.height;
}
}

View File

@ -51,7 +51,7 @@
<div class="card-body"> <div class="card-body">
<h5 class="card-title" i18n="dashboard.latest-blocks">Latest blocks</h5> <h5 class="card-title" i18n="dashboard.latest-blocks">Latest blocks</h5>
<app-blocks-list [widget]=true></app-blocks-list> <app-blocks-list [widget]=true></app-blocks-list>
<div><a [routerLink]="['/mining/blocks' | relativeUrl]" i18n="dashboard.view-more">View more &raquo;</a></div> <div><a [routerLink]="['/blocks' | relativeUrl]" i18n="dashboard.view-more">View more &raquo;</a></div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -136,7 +136,7 @@
</tr> </tr>
</tbody> </tbody>
</table> </table>
<div class=""><a href="" [routerLink]="[(stateService.env.MINING_DASHBOARD ? '/mining/blocks' : '/blocks') | relativeUrl]" i18n="dashboard.view-more">View more &raquo;</a></div> <div class=""><a href="" [routerLink]="['/blocks' | relativeUrl]" i18n="dashboard.view-more">View more &raquo;</a></div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -149,7 +149,7 @@ export class ApiService {
getBlocks$(from: number): Observable<BlockExtended[]> { getBlocks$(from: number): Observable<BlockExtended[]> {
return this.httpClient.get<BlockExtended[]>( return this.httpClient.get<BlockExtended[]>(
this.apiBaseUrl + this.apiBasePath + `/api/v1/blocks-extras` + this.apiBaseUrl + this.apiBasePath + `/api/v1/blocks` +
(from !== undefined ? `/${from}` : ``) (from !== undefined ? `/${from}` : ``)
); );
} }

View File

@ -48,7 +48,6 @@ import { TransactionsListComponent } from '../components/transactions-list/trans
import { BlockComponent } from '../components/block/block.component'; import { BlockComponent } from '../components/block/block.component';
import { AddressComponent } from '../components/address/address.component'; import { AddressComponent } from '../components/address/address.component';
import { SearchFormComponent } from '../components/search-form/search-form.component'; import { SearchFormComponent } from '../components/search-form/search-form.component';
import { LatestBlocksComponent } from '../components/latest-blocks/latest-blocks.component';
import { AddressLabelsComponent } from '../components/address-labels/address-labels.component'; import { AddressLabelsComponent } from '../components/address-labels/address-labels.component';
import { FooterComponent } from '../components/footer/footer.component'; import { FooterComponent } from '../components/footer/footer.component';
import { TimeSpanComponent } from '../components/time-span/time-span.component'; import { TimeSpanComponent } from '../components/time-span/time-span.component';
@ -113,7 +112,6 @@ import { IndexingProgressComponent } from '../components/indexing-progress/index
BlockComponent, BlockComponent,
TransactionsListComponent, TransactionsListComponent,
AddressComponent, AddressComponent,
LatestBlocksComponent,
SearchFormComponent, SearchFormComponent,
TimeSpanComponent, TimeSpanComponent,
AddressLabelsComponent, AddressLabelsComponent,
@ -208,7 +206,6 @@ import { IndexingProgressComponent } from '../components/indexing-progress/index
BlockComponent, BlockComponent,
TransactionsListComponent, TransactionsListComponent,
AddressComponent, AddressComponent,
LatestBlocksComponent,
SearchFormComponent, SearchFormComponent,
TimeSpanComponent, TimeSpanComponent,
AddressLabelsComponent, AddressLabelsComponent,

View File

@ -4,6 +4,7 @@ slugs=(`curl -sSL https://raw.githubusercontent.com/mempool/mining-pools/master/
while true while true
do for url in / \ do for url in / \
'/api/v1/blocks' \
'/api/v1/statistics/2h' \ '/api/v1/statistics/2h' \
'/api/v1/statistics/24h' \ '/api/v1/statistics/24h' \
'/api/v1/statistics/1w' \ '/api/v1/statistics/1w' \
@ -36,7 +37,6 @@ do for url in / \
'/api/v1/mining/hashrate/pools/3y' \ '/api/v1/mining/hashrate/pools/3y' \
'/api/v1/mining/hashrate/pools/all' \ '/api/v1/mining/hashrate/pools/all' \
'/api/v1/mining/reward-stats/144' \ '/api/v1/mining/reward-stats/144' \
'/api/v1/mining/blocks-extras' \
'/api/v1/mining/blocks/fees/24h' \ '/api/v1/mining/blocks/fees/24h' \
'/api/v1/mining/blocks/fees/3d' \ '/api/v1/mining/blocks/fees/3d' \
'/api/v1/mining/blocks/fees/1w' \ '/api/v1/mining/blocks/fees/1w' \