diff --git a/backend/src/api/mining/mining-routes.ts b/backend/src/api/mining/mining-routes.ts index b6ce3ba70..bdfc83d43 100644 --- a/backend/src/api/mining/mining-routes.ts +++ b/backend/src/api/mining/mining-routes.ts @@ -227,10 +227,8 @@ class MiningRoutes { throw new Error('from must be less than to'); } const blockFees = await mining.$getBlockFeesTimespan(parseInt(req.query.from as string, 10), parseInt(req.query.to as string, 10)); - const blockCount = await BlocksRepository.$blockCount(null, null); res.header('Pragma', 'public'); res.header('Cache-control', 'public'); - res.header('X-total-count', blockCount.toString()); res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); res.json(blockFees); } catch (e) { diff --git a/frontend/src/app/components/block-fees-subsidy-graph/block-fees-subsidy-graph.component.ts b/frontend/src/app/components/block-fees-subsidy-graph/block-fees-subsidy-graph.component.ts index 3fd7c5f46..a6fca2469 100644 --- a/frontend/src/app/components/block-fees-subsidy-graph/block-fees-subsidy-graph.component.ts +++ b/frontend/src/app/components/block-fees-subsidy-graph/block-fees-subsidy-graph.component.ts @@ -48,7 +48,7 @@ export class BlockFeesSubsidyGraphComponent implements OnInit { formatNumber = formatNumber; timespan = ''; chartInstance: any = undefined; - showFiat = false; + displayMode: 'normal' | 'fiat' | 'percentage' = 'normal'; updateZoom = false; zoomSpan = 100; zoomTimeSpan = ''; @@ -106,8 +106,10 @@ export class BlockFeesSubsidyGraphComponent implements OnInit { blockHeight: response.body.map(val => val.avgHeight), blockFees: response.body.map(val => val.avgFees / 100_000_000), blockFeesFiat: response.body.filter(val => val['USD'] > 0).map(val => val.avgFees / 100_000_000 * val['USD']), - blockSubsidy: response.body.map(val => this.subsidies[Math.floor(Math.min(val.avgHeight / 210000, 33))] / 100_000_000), - blockSubsidyFiat: response.body.filter(val => val['USD'] > 0).map(val => this.subsidies[Math.floor(Math.min(val.avgHeight / 210000, 33))] / 100_000_000 * val['USD']), + blockFeesPercent: response.body.map(val => val.avgFees / (val.avgFees + this.subsidyAt(val.avgHeight)) * 100), + blockSubsidy: response.body.map(val => this.subsidyAt(val.avgHeight) / 100_000_000), + blockSubsidyFiat: response.body.filter(val => val['USD'] > 0).map(val => this.subsidyAt(val.avgHeight) / 100_000_000 * val['USD']), + blockSubsidyPercent: response.body.map(val => this.subsidyAt(val.avgHeight) / (val.avgFees + this.subsidyAt(val.avgHeight)) * 100), }; this.prepareChartOptions(); @@ -157,9 +159,9 @@ export class BlockFeesSubsidyGraphComponent implements OnInit { axisPointer: { type: 'line' }, - backgroundColor: 'color-mix(in srgb, var(--active-bg) 95%, transparent)', + backgroundColor: 'rgba(17, 19, 31, 1)', borderRadius: 4, - shadowColor: 'color-mix(in srgb, var(--active-bg) 95%, transparent)', + shadowColor: 'rgba(0, 0, 0, 0.5)', textStyle: { color: 'var(--tooltip-grey)', align: 'left', @@ -172,11 +174,13 @@ export class BlockFeesSubsidyGraphComponent implements OnInit { let tooltip = `${formatterXAxis(this.locale, this.zoomTimeSpan, parseInt(this.data.timestamp[data[0].dataIndex], 10))}
`; for (let i = data.length - 1; i >= 0; i--) { const tick = data[i]; - if (!this.showFiat) tooltip += `${tick.marker} ${tick.seriesName}: ${formatNumber(tick.data, this.locale, '1.0-3')} BTC
`; - else tooltip += `${tick.marker} ${tick.seriesName}: ${this.fiatCurrencyPipe.transform(tick.data, null, 'USD') }
`; + tooltip += `${tick.marker} ${tick.seriesName.split(' ')[0]}: `; + if (this.displayMode === 'normal') tooltip += `${formatNumber(tick.data, this.locale, '1.0-3')} BTC
`; + else if (this.displayMode === 'fiat') tooltip += `${this.fiatCurrencyPipe.transform(tick.data, null, 'USD') }
`; + else tooltip += `${formatNumber(tick.data, this.locale, '1.0-2')}%
`; } - if (!this.showFiat) tooltip += `
${formatNumber(data.reduce((acc, val) => acc + val.data, 0), this.locale, '1.0-3')} BTC
`; - else tooltip += `
${this.fiatCurrencyPipe.transform(data.reduce((acc, val) => acc + val.data, 0), null, 'USD')}
`; + if (this.displayMode === 'normal') tooltip += `
${formatNumber(data.reduce((acc, val) => acc + val.data, 0), this.locale, '1.0-3')} BTC
`; + else if (this.displayMode === 'fiat') tooltip += `
${this.fiatCurrencyPipe.transform(data.reduce((acc, val) => acc + val.data, 0), null, 'USD')}
`; if (['24h', '3d'].includes(this.zoomTimeSpan)) { tooltip += `` + $localize`At block ${data[0].axisValue}` + ``; } else { @@ -250,12 +254,30 @@ export class BlockFeesSubsidyGraphComponent implements OnInit { }, icon: 'roundRect', }, + { + name: 'Subsidy (%)', + inactiveColor: 'var(--grey)', + textStyle: { + color: 'white', + }, + icon: 'roundRect', + }, + { + name: 'Fees (%)', + inactiveColor: 'var(--grey)', + textStyle: { + color: 'white', + }, + icon: 'roundRect', + }, ], selected: { - 'Subsidy (USD)': this.showFiat, - 'Fees (USD)': this.showFiat, - 'Subsidy': !this.showFiat, - 'Fees': !this.showFiat, + 'Subsidy (USD)': this.displayMode === 'fiat', + 'Fees (USD)': this.displayMode === 'fiat', + 'Subsidy': this.displayMode === 'normal', + 'Fees': this.displayMode === 'normal', + 'Subsidy (%)': this.displayMode === 'percentage', + 'Fees (%)': this.displayMode === 'percentage', }, }, yAxis: this.data.blockFees.length === 0 ? undefined : [ @@ -264,10 +286,15 @@ export class BlockFeesSubsidyGraphComponent implements OnInit { axisLabel: { color: 'var(--grey)', formatter: (val) => { - return `${val} BTC`; + return `${val}${this.displayMode === 'percentage' ? '%' : ' BTC'}`; } }, min: 0, + max: (value) => { + if (this.displayMode === 'percentage') { + return 100; + } + }, splitLine: { lineStyle: { type: 'dotted', @@ -295,6 +322,7 @@ export class BlockFeesSubsidyGraphComponent implements OnInit { name: 'Subsidy', yAxisIndex: 0, type: 'bar', + barWidth: '90%', stack: 'total', data: this.data.blockSubsidy, }, @@ -302,6 +330,7 @@ export class BlockFeesSubsidyGraphComponent implements OnInit { name: 'Fees', yAxisIndex: 0, type: 'bar', + barWidth: '90%', stack: 'total', data: this.data.blockFees, }, @@ -309,6 +338,7 @@ export class BlockFeesSubsidyGraphComponent implements OnInit { name: 'Subsidy (USD)', yAxisIndex: 1, type: 'bar', + barWidth: '90%', stack: 'total', data: this.data.blockSubsidyFiat, }, @@ -316,9 +346,26 @@ export class BlockFeesSubsidyGraphComponent implements OnInit { name: 'Fees (USD)', yAxisIndex: 1, type: 'bar', + barWidth: '90%', stack: 'total', data: this.data.blockFeesFiat, }, + { + name: 'Subsidy (%)', + yAxisIndex: 0, + type: 'bar', + barWidth: '90%', + stack: 'total', + data: this.data.blockSubsidyPercent, + }, + { + name: 'Fees (%)', + yAxisIndex: 0, + type: 'bar', + barWidth: '90%', + stack: 'total', + data: this.data.blockFeesPercent, + }, ], dataZoom: this.data.blockFees.length === 0 ? undefined : [{ type: 'inside', @@ -349,22 +396,31 @@ export class BlockFeesSubsidyGraphComponent implements OnInit { this.chartInstance = ec; this.chartInstance.on('legendselectchanged', (params) => { - const isFiat = params.name.includes('USD'); - if (isFiat === this.showFiat) return; + if (this.isLoading) { + return; + } + + let mode: 'normal' | 'fiat' | 'percentage'; + if (params.name.includes('USD')) { + mode = 'fiat'; + } else if (params.name.includes('%')) { + mode = 'percentage'; + } else { + mode = 'normal'; + } + + if (this.displayMode === mode) return; const isActivation = params.selected[params.name]; - if (isFiat === isActivation) { - this.showFiat = true; - this.chartInstance.dispatchAction({ type: 'legendUnSelect', name: 'Subsidy' }); - this.chartInstance.dispatchAction({ type: 'legendUnSelect', name: 'Fees' }); - this.chartInstance.dispatchAction({ type: 'legendSelect', name: 'Subsidy (USD)' }); - this.chartInstance.dispatchAction({ type: 'legendSelect', name: 'Fees (USD)' }); - } else { - this.showFiat = false; - this.chartInstance.dispatchAction({ type: 'legendSelect', name: 'Subsidy' }); - this.chartInstance.dispatchAction({ type: 'legendSelect', name: 'Fees' }); - this.chartInstance.dispatchAction({ type: 'legendUnSelect', name: 'Subsidy (USD)' }); - this.chartInstance.dispatchAction({ type: 'legendUnSelect', name: 'Fees (USD)' }); + + if (isActivation) { + this.displayMode = mode; + this.chartInstance.dispatchAction({ type: this.displayMode === 'normal' ? 'legendSelect' : 'legendUnSelect', name: 'Subsidy' }); + this.chartInstance.dispatchAction({ type: this.displayMode === 'normal' ? 'legendSelect' : 'legendUnSelect', name: 'Fees' }); + this.chartInstance.dispatchAction({ type: this.displayMode === 'fiat' ? 'legendSelect' : 'legendUnSelect', name: 'Subsidy (USD)' }); + this.chartInstance.dispatchAction({ type: this.displayMode === 'fiat' ? 'legendSelect' : 'legendUnSelect', name: 'Fees (USD)' }); + this.chartInstance.dispatchAction({ type: this.displayMode === 'percentage' ? 'legendSelect' : 'legendUnSelect', name: 'Subsidy (%)' }); + this.chartInstance.dispatchAction({ type: this.displayMode === 'percentage' ? 'legendSelect' : 'legendUnSelect', name: 'Fees (%)' }); } }); @@ -411,6 +467,10 @@ export class BlockFeesSubsidyGraphComponent implements OnInit { return subsidies; } + subsidyAt(height: number): number { + return this.subsidies[Math.floor(Math.min(height / 210000, 33))]; + } + onZoom() { const option = this.chartInstance.getOption(); const timestamps = option.xAxis[1].data; @@ -432,12 +492,16 @@ export class BlockFeesSubsidyGraphComponent implements OnInit { this.data.blockHeight.splice(startIndex, endIndex - startIndex, ...response.body.map(val => val.avgHeight)); this.data.blockFees.splice(startIndex, endIndex - startIndex, ...response.body.map(val => val.avgFees / 100_000_000)); this.data.blockFeesFiat.splice(startIndex, endIndex - startIndex, ...response.body.filter(val => val['USD'] > 0).map(val => val.avgFees / 100_000_000 * val['USD'])); - this.data.blockSubsidy.splice(startIndex, endIndex - startIndex, ...response.body.map(val => this.subsidies[Math.floor(Math.min(val.avgHeight / 210000, 33))] / 100_000_000)); - this.data.blockSubsidyFiat.splice(startIndex, endIndex - startIndex, ...response.body.filter(val => val['USD'] > 0).map(val => this.subsidies[Math.floor(Math.min(val.avgHeight / 210000, 33))] / 100_000_000 * val['USD'])); + this.data.blockFeesPercent.splice(startIndex, endIndex - startIndex, ...response.body.map(val => val.avgFees / (val.avgFees + this.subsidyAt(val.avgHeight)) * 100)); + this.data.blockSubsidy.splice(startIndex, endIndex - startIndex, ...response.body.map(val => this.subsidyAt(val.avgHeight) / 100_000_000)); + this.data.blockSubsidyFiat.splice(startIndex, endIndex - startIndex, ...response.body.filter(val => val['USD'] > 0).map(val => this.subsidyAt(val.avgHeight) / 100_000_000 * val['USD'])); + this.data.blockSubsidyPercent.splice(startIndex, endIndex - startIndex, ...response.body.map(val => this.subsidyAt(val.avgHeight) / (val.avgFees + this.subsidyAt(val.avgHeight)) * 100)); option.series[0].data = this.data.blockSubsidy; option.series[1].data = this.data.blockFees; option.series[2].data = this.data.blockSubsidyFiat; option.series[3].data = this.data.blockFeesFiat; + option.series[4].data = this.data.blockSubsidyPercent; + option.series[5].data = this.data.blockFeesPercent; option.xAxis[0].data = this.data.blockHeight; option.xAxis[1].data = this.data.timestamp; this.chartInstance.setOption(option, true);