diff --git a/backend/src/api/blocks.ts b/backend/src/api/blocks.ts index de461e095..af25b957b 100644 --- a/backend/src/api/blocks.ts +++ b/backend/src/api/blocks.ts @@ -116,6 +116,9 @@ class Blocks { Common.median(transactionsTmp.map((tx) => tx.effectiveFeePerVsize)) : 0; blockExtended.extras.feeRange = transactionsTmp.length > 0 ? Common.getFeesInRange(transactionsTmp, 8) : [0, 0]; + blockExtended.extras.totalFees = transactionsTmp.reduce((acc, tx) => { + return acc + tx.fee; + }, 0) if (Common.indexingEnabled()) { let pool: PoolTag; diff --git a/backend/src/api/mining.ts b/backend/src/api/mining.ts index 1d5142080..9c3689011 100644 --- a/backend/src/api/mining.ts +++ b/backend/src/api/mining.ts @@ -42,9 +42,7 @@ class Mining { }); poolsStatistics['pools'] = poolsStats; - - const oldestBlock = new Date(await BlocksRepository.$oldestBlockTimestamp()); - poolsStatistics['oldestIndexedBlockTimestamp'] = oldestBlock.getTime(); + poolsStatistics['oldestIndexedBlockTimestamp'] = await BlocksRepository.$oldestBlockTimestamp(); const blockCount: number = await BlocksRepository.$blockCount(null, interval); poolsStatistics['blockCount'] = blockCount; @@ -79,26 +77,14 @@ class Mining { * Return the historical difficulty adjustments and oldest indexed block timestamp */ public async $getHistoricalDifficulty(interval: string | null): Promise { - const difficultyAdjustments = await BlocksRepository.$getBlocksDifficulty(interval); - const oldestBlock = new Date(await BlocksRepository.$oldestBlockTimestamp()); - - return { - adjustments: difficultyAdjustments, - oldestIndexedBlockTimestamp: oldestBlock.getTime(), - }; + return await BlocksRepository.$getBlocksDifficulty(interval); } /** * Return the historical hashrates and oldest indexed block timestamp */ public async $getHistoricalHashrates(interval: string | null): Promise { - const hashrates = await HashratesRepository.$get(interval); - const oldestBlock = new Date(await BlocksRepository.$oldestBlockTimestamp()); - - return { - hashrates: hashrates, - oldestIndexedBlockTimestamp: oldestBlock.getTime(), - }; + return await HashratesRepository.$get(interval); } /** @@ -175,7 +161,18 @@ class Mining { ++totalIndexed; } - await HashratesRepository.$saveHashrates(hashrates); + // Add genesis block manually + if (!indexedTimestamp.includes(genesisTimestamp)) { + hashrates.push({ + hashrateTimestamp: genesisTimestamp, + avgHashrate: await bitcoinClient.getNetworkHashPs(1, 1), + poolId: null + }); + } + + if (hashrates.length > 0) { + await HashratesRepository.$saveHashrates(hashrates); + } await HashratesRepository.$setLatestRunTimestamp(); this.hashrateIndexingStarted = false; diff --git a/backend/src/mempool.interfaces.ts b/backend/src/mempool.interfaces.ts index 4869561c2..810398cab 100644 --- a/backend/src/mempool.interfaces.ts +++ b/backend/src/mempool.interfaces.ts @@ -78,6 +78,7 @@ export interface TransactionStripped { } export interface BlockExtension { + totalFees?: number; medianFee?: number; feeRange?: number[]; reward?: number; diff --git a/backend/src/repositories/BlocksRepository.ts b/backend/src/repositories/BlocksRepository.ts index 235dc9ebd..a2b0b2f95 100644 --- a/backend/src/repositories/BlocksRepository.ts +++ b/backend/src/repositories/BlocksRepository.ts @@ -187,12 +187,11 @@ class BlocksRepository { * Get the oldest indexed block */ public async $oldestBlockTimestamp(): Promise { - const query = `SELECT blockTimestamp + const query = `SELECT UNIX_TIMESTAMP(blockTimestamp) as blockTimestamp FROM blocks ORDER BY height LIMIT 1;`; - // logger.debug(query); const connection = await DB.pool.getConnection(); const [rows]: any[] = await connection.query(query); @@ -266,15 +265,37 @@ class BlocksRepository { const connection = await DB.pool.getConnection(); - let query = `SELECT MIN(UNIX_TIMESTAMP(blockTimestamp)) as timestamp, difficulty, height - FROM blocks`; + // :D ... Yeah don't ask me about this one https://stackoverflow.com/a/40303162 + // Basically, using temporary user defined fields, we are able to extract all + // difficulty adjustments from the blocks tables. + // This allow use to avoid indexing it in another table. + let query = ` + SELECT + * + FROM + ( + SELECT + UNIX_TIMESTAMP(blockTimestamp) as timestamp, difficulty, height, + IF(@prevStatus = YT.difficulty, @rn := @rn + 1, + IF(@prevStatus := YT.difficulty, @rn := 1, @rn := 1) + ) AS rn + FROM blocks YT + CROSS JOIN + ( + SELECT @prevStatus := -1, @rn := 1 + ) AS var + `; if (interval) { query += ` WHERE blockTimestamp BETWEEN DATE_SUB(NOW(), INTERVAL ${interval}) AND NOW()`; } - query += ` GROUP BY difficulty - ORDER BY blockTimestamp`; + query += ` + ORDER BY YT.height + ) AS t + WHERE t.rn = 1 + ORDER BY t.height + `; const [rows]: any[] = await connection.query(query); connection.release(); diff --git a/backend/src/routes.ts b/backend/src/routes.ts index 1bf1c3434..7fba2e66b 100644 --- a/backend/src/routes.ts +++ b/backend/src/routes.ts @@ -588,11 +588,17 @@ class Routes { public async $getHistoricalHashrate(req: Request, res: Response) { try { - const stats = await mining.$getHistoricalHashrates(req.params.interval ?? null); + const hashrates = await mining.$getHistoricalHashrates(req.params.interval ?? null); + const difficulty = await mining.$getHistoricalDifficulty(req.params.interval ?? null); + const oldestIndexedBlockTimestamp = await BlocksRepository.$oldestBlockTimestamp(); res.header('Pragma', 'public'); res.header('Cache-control', 'public'); res.setHeader('Expires', new Date(Date.now() + 1000 * 300).toUTCString()); - res.json(stats); + res.json({ + oldestIndexedBlockTimestamp: oldestIndexedBlockTimestamp, + hashrates: hashrates, + difficulty: difficulty, + }); } catch (e) { res.status(500).send(e instanceof Error ? e.message : e); } diff --git a/frontend/proxy.conf.local.js b/frontend/proxy.conf.local.js index 5a6d1454e..b1bf0656d 100644 --- a/frontend/proxy.conf.local.js +++ b/frontend/proxy.conf.local.js @@ -42,7 +42,28 @@ if (configContent && configContent.BASE_MODULE === 'liquid') { pathRewrite: { "^/liquid/api/": "/api/v1/" }, - } + }, + { + context: ['/liquidtestnet/api/v1/**'], + target: `http://localhost:8999`, + secure: false, + ws: true, + changeOrigin: true, + proxyTimeout: 30000, + pathRewrite: { + "^/liquidtestnet": "" + }, + }, + { + context: ['/liquidtestnet/api/**'], + target: `http://localhost:8999`, + secure: false, + changeOrigin: true, + proxyTimeout: 30000, + pathRewrite: { + "^/liquidtestnet/api/": "/api/v1/" + }, + }, ]); } diff --git a/frontend/proxy.conf.mixed.js b/frontend/proxy.conf.mixed.js index 6ee44a16f..c0c7157ff 100644 --- a/frontend/proxy.conf.mixed.js +++ b/frontend/proxy.conf.mixed.js @@ -40,6 +40,24 @@ if (configContent && configContent.BASE_MODULE === 'liquid') { changeOrigin: true, proxyTimeout: 30000, }, + { + context: ['/liquidtestnet/api/v1/**'], + target: `http://localhost:8999`, + secure: false, + ws: true, + changeOrigin: true, + proxyTimeout: 30000, + pathRewrite: { + "^/liquidtestnet": "" + }, + }, + { + context: ['/liquidtestnet/api/**'], + target: `https://liquid.network`, + secure: false, + changeOrigin: true, + proxyTimeout: 30000, + }, ]); } diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index c8a1d98e6..bd7d2d516 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -28,7 +28,6 @@ import { AssetsFeaturedComponent } from './components/assets/assets-featured/ass import { AssetsComponent } from './components/assets/assets.component'; import { PoolComponent } from './components/pool/pool.component'; import { MiningDashboardComponent } from './components/mining-dashboard/mining-dashboard.component'; -import { DifficultyChartComponent } from './components/difficulty-chart/difficulty-chart.component'; import { HashrateChartComponent } from './components/hashrate-chart/hashrate-chart.component'; import { MiningStartComponent } from './components/mining-start/mining-start.component'; @@ -75,10 +74,6 @@ let routes: Routes = [ path: 'mining', component: MiningStartComponent, children: [ - { - path: 'difficulty', - component: DifficultyChartComponent, - }, { path: 'hashrate', component: HashrateChartComponent, @@ -194,10 +189,6 @@ let routes: Routes = [ path: 'mining', component: MiningStartComponent, children: [ - { - path: 'difficulty', - component: DifficultyChartComponent, - }, { path: 'hashrate', component: HashrateChartComponent, @@ -307,10 +298,6 @@ let routes: Routes = [ path: 'mining', component: MiningStartComponent, children: [ - { - path: 'difficulty', - component: DifficultyChartComponent, - }, { path: 'hashrate', component: HashrateChartComponent, diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index e7fe30c1d..c161e24dc 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -70,7 +70,6 @@ import { AssetsFeaturedComponent } from './components/assets/assets-featured/ass import { AssetGroupComponent } from './components/assets/asset-group/asset-group.component'; import { AssetCirculationComponent } from './components/asset-circulation/asset-circulation.component'; import { MiningDashboardComponent } from './components/mining-dashboard/mining-dashboard.component'; -import { DifficultyChartComponent } from './components/difficulty-chart/difficulty-chart.component'; import { HashrateChartComponent } from './components/hashrate-chart/hashrate-chart.component'; import { MiningStartComponent } from './components/mining-start/mining-start.component'; import { AmountShortenerPipe } from './shared/pipes/amount-shortener.pipe'; @@ -126,7 +125,6 @@ import { AmountShortenerPipe } from './shared/pipes/amount-shortener.pipe'; AssetGroupComponent, AssetCirculationComponent, MiningDashboardComponent, - DifficultyChartComponent, HashrateChartComponent, MiningStartComponent, AmountShortenerPipe, diff --git a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.html b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.html index 0f04f3a03..bc0025d2b 100644 --- a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.html +++ b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.html @@ -14,7 +14,7 @@ {{ block?.extras?.feeRange[1] | number:feeRounding }} - {{ block?.extras?.feeRange[block?.extras?.feeRange.length - 1] | number:feeRounding }} sat/vB
- +
diff --git a/frontend/src/app/components/difficulty-chart/difficulty-chart.component.html b/frontend/src/app/components/difficulty-chart/difficulty-chart.component.html deleted file mode 100644 index eb34d4075..000000000 --- a/frontend/src/app/components/difficulty-chart/difficulty-chart.component.html +++ /dev/null @@ -1,53 +0,0 @@ -
- -
-
-
- - - - - - -
-
-
- -
-
-
-
- - - - - - - - - - - - - - - - - - - -
BlockTimestampDifficultyChange
{{ diffChange.height }}‎{{ diffChange.timestamp * 1000 | date:'yyyy-MM-dd HH:mm' }}{{ formatNumber(diffChange.difficulty, locale, '1.2-2') }}{{ diffChange.difficultyShorten }}{{ formatNumber(diffChange.change, locale, '1.2-2') }}%
- -
diff --git a/frontend/src/app/components/difficulty-chart/difficulty-chart.component.scss b/frontend/src/app/components/difficulty-chart/difficulty-chart.component.scss deleted file mode 100644 index 4205c9db7..000000000 --- a/frontend/src/app/components/difficulty-chart/difficulty-chart.component.scss +++ /dev/null @@ -1,27 +0,0 @@ -.main-title { - position: relative; - color: #ffffff91; - margin-top: -13px; - font-size: 10px; - text-transform: uppercase; - font-weight: 500; - text-align: center; - padding-bottom: 3px; -} - -.formRadioGroup { - margin-top: 6px; - display: flex; - flex-direction: column; - @media (min-width: 830px) { - flex-direction: row; - float: right; - margin-top: 0px; - } - .btn-sm { - font-size: 9px; - @media (min-width: 830px) { - font-size: 14px; - } - } -} diff --git a/frontend/src/app/components/difficulty-chart/difficulty-chart.component.ts b/frontend/src/app/components/difficulty-chart/difficulty-chart.component.ts deleted file mode 100644 index 4bbc9520a..000000000 --- a/frontend/src/app/components/difficulty-chart/difficulty-chart.component.ts +++ /dev/null @@ -1,183 +0,0 @@ -import { Component, Inject, Input, LOCALE_ID, OnInit } from '@angular/core'; -import { EChartsOption, graphic } from 'echarts'; -import { Observable } from 'rxjs'; -import { map, share, startWith, switchMap, tap } from 'rxjs/operators'; -import { ApiService } from 'src/app/services/api.service'; -import { SeoService } from 'src/app/services/seo.service'; -import { formatNumber } from '@angular/common'; -import { FormBuilder, FormGroup } from '@angular/forms'; -import { selectPowerOfTen } from 'src/app/bitcoin.utils'; - -@Component({ - selector: 'app-difficulty-chart', - templateUrl: './difficulty-chart.component.html', - styleUrls: ['./difficulty-chart.component.scss'], - styles: [` - .loadingGraphs { - position: absolute; - top: 38%; - left: calc(50% - 15px); - z-index: 100; - } - `], -}) -export class DifficultyChartComponent implements OnInit { - @Input() widget: boolean = false; - - radioGroupForm: FormGroup; - - chartOptions: EChartsOption = {}; - chartInitOptions = { - renderer: 'svg' - }; - - difficultyObservable$: Observable; - isLoading = true; - formatNumber = formatNumber; - - constructor( - @Inject(LOCALE_ID) public locale: string, - private seoService: SeoService, - private apiService: ApiService, - private formBuilder: FormBuilder, - ) { - this.seoService.setTitle($localize`:@@mining.difficulty:Difficulty`); - this.radioGroupForm = this.formBuilder.group({ dateSpan: '1y' }); - this.radioGroupForm.controls.dateSpan.setValue('1y'); - } - - ngOnInit(): void { - this.difficultyObservable$ = this.radioGroupForm.get('dateSpan').valueChanges - .pipe( - startWith('1y'), - switchMap((timespan) => { - return this.apiService.getHistoricalDifficulty$(timespan) - .pipe( - tap(data => { - this.prepareChartOptions(data.adjustments.map(val => [val.timestamp * 1000, val.difficulty])); - this.isLoading = false; - }), - map(data => { - const availableTimespanDay = ( - (new Date().getTime() / 1000) - (data.oldestIndexedBlockTimestamp / 1000) - ) / 3600 / 24; - - const tableData = []; - for (let i = data.adjustments.length - 1; i > 0; --i) { - const selectedPowerOfTen: any = selectPowerOfTen(data.adjustments[i].difficulty); - const change = (data.adjustments[i].difficulty / data.adjustments[i - 1].difficulty - 1) * 100; - - tableData.push(Object.assign(data.adjustments[i], { - change: change, - difficultyShorten: formatNumber( - data.adjustments[i].difficulty / selectedPowerOfTen.divider, - this.locale, '1.2-2') + selectedPowerOfTen.unit - })); - } - return { - availableTimespanDay: availableTimespanDay, - data: tableData - }; - }), - ); - }), - share() - ); - } - - prepareChartOptions(data) { - this.chartOptions = { - color: new graphic.LinearGradient(0, 0, 0, 0.65, [ - { offset: 0, color: '#D81B60' }, - { offset: 0.25, color: '#8E24AA' }, - { offset: 0.5, color: '#5E35B1' }, - { offset: 0.75, color: '#3949AB' }, - { offset: 1, color: '#1E88E5' } - ]), - title: { - text: this.widget? '' : $localize`:@@mining.difficulty:Difficulty`, - left: 'center', - textStyle: { - color: '#FFF', - }, - }, - tooltip: { - show: true, - trigger: 'axis', - backgroundColor: 'rgba(17, 19, 31, 1)', - borderRadius: 4, - shadowColor: 'rgba(0, 0, 0, 0.5)', - textStyle: { - color: '#b1b1b1', - }, - borderColor: '#000', - formatter: params => { - return `${params[0].axisValueLabel}
- ${params[0].marker} ${formatNumber(params[0].value[1], this.locale, '1.0-0')}` - } - }, - axisPointer: { - type: 'line', - }, - xAxis: { - type: 'time', - splitNumber: this.isMobile() ? 5 : 10, - }, - yAxis: { - type: 'value', - axisLabel: { - formatter: (val) => { - const selectedPowerOfTen: any = selectPowerOfTen(val); - const diff = val / selectedPowerOfTen.divider; - return `${diff} ${selectedPowerOfTen.unit}`; - } - }, - splitLine: { - lineStyle: { - type: 'dotted', - color: '#ffffff66', - opacity: 0.25, - } - } - }, - series: { - showSymbol: false, - data: data, - type: 'line', - smooth: false, - lineStyle: { - width: 2, - }, - }, - dataZoom: this.widget ? null : [{ - type: 'inside', - realtime: true, - zoomLock: true, - zoomOnMouseWheel: true, - moveOnMouseMove: true, - maxSpan: 100, - minSpan: 10, - }, { - showDetail: false, - show: true, - type: 'slider', - brushSelect: false, - realtime: true, - bottom: 0, - selectedDataBackground: { - lineStyle: { - color: '#fff', - opacity: 0.45, - }, - areaStyle: { - opacity: 0, - } - }, - }], - }; - } - - isMobile() { - return (window.innerWidth <= 767.98); - } -} diff --git a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.html b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.html index 263df95b2..745fe491f 100644 --- a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.html +++ b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.html @@ -25,9 +25,34 @@
-
+
+
+ + + + + + + + + + + + + + + + + + + + +
BlockTimestampAdjustedDifficultyChange
{{ diffChange.height }}‎{{ diffChange.timestamp * 1000 | date:'yyyy-MM-dd HH:mm' }}{{ formatNumber(diffChange.difficulty, locale, '1.2-2') }}{{ diffChange.difficultyShorten }}{{ formatNumber(diffChange.change, locale, '1.2-2') }}%
+
+ diff --git a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.scss b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.scss index 62eac44f5..316f0fc47 100644 --- a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.scss +++ b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.scss @@ -26,6 +26,11 @@ padding-bottom: 20px; padding-right: 20px; } +.chart-widget { + width: 100%; + height: 100%; + max-height: 275px; +} .formRadioGroup { margin-top: 6px; 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 9a202b69a..f6995056e 100644 --- a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts +++ b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts @@ -23,7 +23,7 @@ import { selectPowerOfTen } from 'src/app/bitcoin.utils'; }) export class HashrateChartComponent implements OnInit { @Input() widget: boolean = false; - @Input() right: number | string = 10; + @Input() right: number | string = 45; @Input() left: number | string = 75; radioGroupForm: FormGroup; @@ -45,7 +45,7 @@ export class HashrateChartComponent implements OnInit { private apiService: ApiService, private formBuilder: FormBuilder, ) { - this.seoService.setTitle($localize`:@@mining.hashrate:hashrate`); + this.seoService.setTitle($localize`:@@mining.hashrate-difficulty:Hashrate and Difficulty`); this.radioGroupForm = this.formBuilder.group({ dateSpan: '1y' }); this.radioGroupForm.controls.dateSpan.setValue('1y'); } @@ -57,17 +57,61 @@ export class HashrateChartComponent implements OnInit { switchMap((timespan) => { return this.apiService.getHistoricalHashrate$(timespan) .pipe( - tap(data => { - this.prepareChartOptions(data.hashrates.map(val => [val.timestamp * 1000, val.avgHashrate])); + tap((data: any) => { + // We generate duplicated data point so the tooltip works nicely + const diffFixed = []; + let diffIndex = 1; + let hashIndex = 0; + while (hashIndex < data.hashrates.length) { + if (diffIndex >= data.difficulty.length) { + while (hashIndex < data.hashrates.length) { + diffFixed.push({ + timestamp: data.hashrates[hashIndex].timestamp, + difficulty: data.difficulty[data.difficulty.length - 1].difficulty + }); + ++hashIndex; + } + break; + } + + while (hashIndex < data.hashrates.length && diffIndex < data.difficulty.length && + data.hashrates[hashIndex].timestamp <= data.difficulty[diffIndex].timestamp + ) { + diffFixed.push({ + timestamp: data.hashrates[hashIndex].timestamp, + difficulty: data.difficulty[diffIndex - 1].difficulty + }); + ++hashIndex; + } + ++diffIndex; + } + + this.prepareChartOptions({ + hashrates: data.hashrates.map(val => [val.timestamp * 1000, val.avgHashrate]), + difficulty: diffFixed.map(val => [val.timestamp * 1000, val.difficulty]) + }); this.isLoading = false; }), - map(data => { + map((data: any) => { const availableTimespanDay = ( - (new Date().getTime() / 1000) - (data.oldestIndexedBlockTimestamp / 1000) + (new Date().getTime() / 1000) - (data.oldestIndexedBlockTimestamp) ) / 3600 / 24; + + const tableData = []; + for (let i = data.difficulty.length - 1; i > 0; --i) { + const selectedPowerOfTen: any = selectPowerOfTen(data.difficulty[i].difficulty); + const change = (data.difficulty[i].difficulty / data.difficulty[i - 1].difficulty - 1) * 100; + + tableData.push(Object.assign(data.difficulty[i], { + change: change, + difficultyShorten: formatNumber( + data.difficulty[i].difficulty / selectedPowerOfTen.divider, + this.locale, '1.2-2') + selectedPowerOfTen.unit + })); + } return { availableTimespanDay: availableTimespanDay, - data: data.hashrates + difficulty: tableData }; }), ); @@ -78,72 +122,144 @@ export class HashrateChartComponent implements OnInit { prepareChartOptions(data) { this.chartOptions = { - color: new graphic.LinearGradient(0, 0, 0, 0.65, [ - { offset: 0, color: '#F4511E' }, - { offset: 0.25, color: '#FB8C00' }, - { offset: 0.5, color: '#FFB300' }, - { offset: 0.75, color: '#FDD835' }, - { offset: 1, color: '#7CB342' } - ]), + color: [ + new graphic.LinearGradient(0, 0, 0, 0.65, [ + { offset: 0, color: '#F4511E' }, + { offset: 0.25, color: '#FB8C00' }, + { offset: 0.5, color: '#FFB300' }, + { offset: 0.75, color: '#FDD835' }, + { offset: 1, color: '#7CB342' } + ]), + '#D81B60', + ], grid: { right: this.right, left: this.left, - }, - title: { - text: this.widget ? '' : $localize`:@@mining.hashrate:Hashrate`, - left: 'center', - textStyle: { - color: '#FFF', - }, + bottom: this.widget ? 30 : 60, }, tooltip: { - show: true, trigger: 'axis', + axisPointer: { + type: 'line' + }, backgroundColor: 'rgba(17, 19, 31, 1)', borderRadius: 4, shadowColor: 'rgba(0, 0, 0, 0.5)', textStyle: { color: '#b1b1b1', + align: 'left', }, borderColor: '#000', - formatter: params => { - return `${params[0].axisValueLabel}
- ${params[0].marker} ${formatNumber(params[0].value[1], this.locale, '1.0-0')} H/s` - } - }, - axisPointer: { - type: 'line', + formatter: function (data) { + let hashratePowerOfTen: any = selectPowerOfTen(1); + let hashrate = data[0].data[1]; + let difficultyPowerOfTen = hashratePowerOfTen; + let difficulty = data[1].data[1]; + + if (this.isMobile()) { + hashratePowerOfTen = selectPowerOfTen(data[0].data[1]); + hashrate = Math.round(data[0].data[1] / hashratePowerOfTen.divider); + difficultyPowerOfTen = selectPowerOfTen(data[1].data[1]); + difficulty = Math.round(data[1].data[1] / difficultyPowerOfTen.divider); + } + + return ` + ${data[0].axisValueLabel}
+ ${data[0].marker} ${data[0].seriesName}: ${formatNumber(hashrate, this.locale, '1.0-0')} ${hashratePowerOfTen.unit}H/s
+ ${data[1].marker} ${data[1].seriesName}: ${formatNumber(difficulty, this.locale, '1.2-2')} ${difficultyPowerOfTen.unit} + `; + }.bind(this) }, xAxis: { type: 'time', splitNumber: this.isMobile() ? 5 : 10, }, - yAxis: { - type: 'value', - axisLabel: { - formatter: (val) => { - const selectedPowerOfTen: any = selectPowerOfTen(val); - const newVal = val / selectedPowerOfTen.divider; - return `${newVal} ${selectedPowerOfTen.unit}H/s` + legend: { + data: [ + { + name: 'Hashrate', + inactiveColor: 'rgb(110, 112, 121)', + textStyle: { + color: 'white', + }, + icon: 'roundRect', + itemStyle: { + color: '#FFB300', + }, + }, + { + name: 'Difficulty', + inactiveColor: 'rgb(110, 112, 121)', + textStyle: { + color: 'white', + }, + icon: 'roundRect', + itemStyle: { + color: '#D81B60', + } + }, + ], + }, + yAxis: [ + { + min: function (value) { + return value.min * 0.9; + }, + type: 'value', + name: 'Hashrate', + axisLabel: { + color: 'rgb(110, 112, 121)', + formatter: (val) => { + const selectedPowerOfTen: any = selectPowerOfTen(val); + const newVal = Math.round(val / selectedPowerOfTen.divider); + return `${newVal} ${selectedPowerOfTen.unit}H/s` + } + }, + splitLine: { + show: false, } }, - splitLine: { + { + min: function (value) { + return value.min * 0.9; + }, + type: 'value', + name: 'Difficulty', + position: 'right', + axisLabel: { + color: 'rgb(110, 112, 121)', + formatter: (val) => { + const selectedPowerOfTen: any = selectPowerOfTen(val); + const newVal = Math.round(val / selectedPowerOfTen.divider); + return `${newVal} ${selectedPowerOfTen.unit}` + } + }, + splitLine: { + show: false, + } + } + ], + series: [ + { + name: 'Hashrate', + showSymbol: false, + data: data.hashrates, + type: 'line', lineStyle: { - type: 'dotted', - color: '#ffffff66', - opacity: 0.25, + width: 2, + }, + }, + { + yAxisIndex: 1, + name: 'Difficulty', + showSymbol: false, + data: data.difficulty, + type: 'line', + lineStyle: { + width: 3, } - }, - }, - series: { - showSymbol: false, - data: data, - type: 'line', - smooth: false, - lineStyle: { - width: 2, - }, - }, + } + ], dataZoom: this.widget ? null : [{ type: 'inside', realtime: true, diff --git a/frontend/src/app/components/mempool-blocks/mempool-blocks.component.html b/frontend/src/app/components/mempool-blocks/mempool-blocks.component.html index 2eff96bca..112b35df2 100644 --- a/frontend/src/app/components/mempool-blocks/mempool-blocks.component.html +++ b/frontend/src/app/components/mempool-blocks/mempool-blocks.component.html @@ -13,7 +13,7 @@ {{ projectedBlock.feeRange[0] | number:feeRounding }} - {{ projectedBlock.feeRange[projectedBlock.feeRange.length - 1] | number:feeRounding }} sat/vB
- +
diff --git a/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts b/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts index 6fe2a155c..05735c0be 100644 --- a/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts +++ b/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts @@ -34,7 +34,6 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy { network = ''; now = new Date().getTime(); showMiningInfo = false; - blockSubsidy = 50; blockWidth = 125; blockPadding = 30; @@ -111,7 +110,6 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy { if (this.stateService.network === '') { block.blink = specialBlocks[block.height] ? true : false; } - this.setBlockSubsidy(block.height); }); const stringifiedBlocks = JSON.stringify(mempoolBlocks); @@ -212,18 +210,6 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy { return block.index; } - setBlockSubsidy(blockHeight) { - if (!['', 'testnet', 'signet'].includes(this.stateService.network)) { - return; - } - this.blockSubsidy = 50; - let halvenings = Math.floor(blockHeight / 210000); - while (halvenings > 0) { - this.blockSubsidy = this.blockSubsidy / 2; - halvenings--; - } - } - reduceMempoolBlocksToFitScreen(blocks: MempoolBlock[]): MempoolBlock[] { const innerWidth = this.stateService.env.BASE_MODULE !== 'liquid' && window.innerWidth <= 767.98 ? window.innerWidth : window.innerWidth / 2; const blocksAmount = Math.min(this.stateService.env.MEMPOOL_BLOCKS_AMOUNT, Math.floor(innerWidth / (this.blockWidth + this.blockPadding))); diff --git a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.html b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.html index 43c5c378c..aa7affc1e 100644 --- a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.html +++ b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.html @@ -5,11 +5,13 @@
-
-
Mining Pools Share (1w)
+
@@ -18,22 +20,12 @@
-
Hashrate (1y)
+
+ + Hashrate (1y) + +
- -
-
-
- - -
-
-
-
Difficulty (1y)
- -
diff --git a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss index c2fb37e8c..e575a405b 100644 --- a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss +++ b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss @@ -16,22 +16,17 @@ } .card-title { - color: #4a68b9; font-size: 1rem; } +.card-title > a { + color: #4a68b9; +} -.card-wrapper { - .card { - height: auto !important; - } - .card-body { - display: flex; - flex: inherit; - text-align: center; - flex-direction: column; - justify-content: space-around; - padding: 22px 20px; - } +.card-body { + padding: 1.25rem 1rem 0.75rem 1rem; +} +.card-body.pool-ranking { + padding: 1.25rem 0.25rem 0.75rem 0.25rem; } #blockchain-container { diff --git a/frontend/src/app/components/pool-ranking/pool-ranking.component.html b/frontend/src/app/components/pool-ranking/pool-ranking.component.html index a687e41c7..5e6068866 100644 --- a/frontend/src/app/components/pool-ranking/pool-ranking.component.html +++ b/frontend/src/app/components/pool-ranking/pool-ranking.component.html @@ -1,11 +1,12 @@
-
+
-
+