Merge branch 'master' into update_mainnet_tests

This commit is contained in:
softsimon
2023-02-18 19:45:50 +07:00
committed by GitHub
82 changed files with 2751 additions and 1552 deletions

View File

@@ -68,8 +68,8 @@
.community-sponsor {
img {
width: 67px;
height: 67px;
width: 57px;
height: 57px;
}
}

View File

@@ -1,4 +1,4 @@
<div class="container-xl">
<div class="container-xl" [class.liquid-address]="network === 'liquid' || network === 'liquidtestnet'">
<div class="title-address">
<h1 i18n="shared.address">Address</h1>
<div class="tx-link">
@@ -15,7 +15,7 @@
<div class="row">
<div class="col-md">
<table class="table table-borderless table-striped">
<table class="table table-borderless table-striped address-table">
<tbody>
<tr *ngIf="addressInfo && addressInfo.unconfidential">
<td i18n="address.unconfidential">Unconfidential</td>

View File

@@ -91,3 +91,20 @@ h1 {
margin-bottom: 10px;
}
}
.liquid-address {
.address-table {
table-layout: fixed;
tr td:first-child {
width: 170px;
}
tr td:last-child {
width: 80%;
}
}
.qrcode-col {
flex-grow: 0.5;
}
}

View File

@@ -1,5 +1,5 @@
<ng-container *ngIf="!noFiat && (viewFiat$ | async) && (conversions$ | async) as conversions; else viewFiatVin">
<span class="fiat">{{ addPlus && satoshis >= 0 ? '+' : '' }}{{ conversions.USD * (satoshis / 100000000) | currency:'USD':'symbol':'1.2-2' }}</span>
<span class="fiat">{{ addPlus && satoshis >= 0 ? '+' : '' }}{{ (conversions ? conversions[currency] : 0) * satoshis / 100000000 | fiatCurrency : digitsInfo : currency }}</span>
</ng-container>
<ng-template #viewFiatVin>
<ng-template [ngIf]="(network === 'liquid' || network === 'liquidtestnet') && (satoshis === undefined || satoshis === null)" [ngIfElse]="default">

View File

@@ -1,4 +1,4 @@
import { Component, OnInit, OnDestroy, Input, ChangeDetectionStrategy } from '@angular/core';
import { Component, OnInit, OnDestroy, Input, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
import { StateService } from '../../services/state.service';
import { Observable, Subscription } from 'rxjs';
@@ -10,10 +10,12 @@ import { Observable, Subscription } from 'rxjs';
})
export class AmountComponent implements OnInit, OnDestroy {
conversions$: Observable<any>;
currency: string;
viewFiat$: Observable<boolean>;
network = '';
stateSubscription: Subscription;
currencySubscription: Subscription;
@Input() satoshis: number;
@Input() digitsInfo = '1.8-8';
@@ -22,7 +24,13 @@ export class AmountComponent implements OnInit, OnDestroy {
constructor(
private stateService: StateService,
) { }
private cd: ChangeDetectorRef,
) {
this.currencySubscription = this.stateService.fiatCurrency$.subscribe((fiat) => {
this.currency = fiat;
this.cd.markForCheck();
});
}
ngOnInit() {
this.viewFiat$ = this.stateService.viewFiat$.asObservable();
@@ -34,6 +42,7 @@ export class AmountComponent implements OnInit, OnDestroy {
if (this.stateSubscription) {
this.stateSubscription.unsubscribe();
}
this.currencySubscription.unsubscribe();
}
}

View File

@@ -1,16 +1,19 @@
import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit } from '@angular/core';
import { EChartsOption, graphic } from 'echarts';
import { Observable } from 'rxjs';
import { Observable, Subscription } from 'rxjs';
import { map, share, startWith, switchMap, tap } from 'rxjs/operators';
import { ApiService } from '../../services/api.service';
import { SeoService } from '../../services/seo.service';
import { formatCurrency, formatNumber, getCurrencySymbol } from '@angular/common';
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
import { download, formatterXAxis, formatterXAxisLabel, formatterXAxisTimeCategory } from '../../shared/graphs.utils';
import { StateService } from '../../services/state.service';
import { StorageService } from '../../services/storage.service';
import { MiningService } from '../../services/mining.service';
import { ActivatedRoute } from '@angular/router';
import { FiatShortenerPipe } from '../../shared/pipes/fiat-shortener.pipe';
import { FiatCurrencyPipe } from '../../shared/pipes/fiat-currency.pipe';
import { fiatCurrencies } from '../../app.constants';
@Component({
selector: 'app-block-fees-graph',
@@ -44,6 +47,9 @@ export class BlockFeesGraphComponent implements OnInit {
timespan = '';
chartInstance: any = undefined;
currencySubscription: Subscription;
currency: string;
constructor(
@Inject(LOCALE_ID) public locale: string,
private seoService: SeoService,
@@ -51,11 +57,21 @@ export class BlockFeesGraphComponent implements OnInit {
private formBuilder: UntypedFormBuilder,
private storageService: StorageService,
private miningService: MiningService,
private stateService: StateService,
private route: ActivatedRoute,
private fiatShortenerPipe: FiatShortenerPipe,
private fiatCurrencyPipe: FiatCurrencyPipe,
) {
this.radioGroupForm = this.formBuilder.group({ dateSpan: '1y' });
this.radioGroupForm.controls.dateSpan.setValue('1y');
this.currencySubscription = this.stateService.fiatCurrency$.subscribe((fiat) => {
if (fiat && fiatCurrencies[fiat]?.indexed) {
this.currency = fiat;
} else {
this.currency = 'USD';
}
});
}
ngOnInit(): void {
@@ -84,7 +100,7 @@ export class BlockFeesGraphComponent implements OnInit {
tap((response) => {
this.prepareChartOptions({
blockFees: response.body.map(val => [val.timestamp * 1000, val.avgFees / 100000000, val.avgHeight]),
blockFeesUSD: response.body.filter(val => val.USD > 0).map(val => [val.timestamp * 1000, val.avgFees / 100000000 * val.USD, val.avgHeight]),
blockFeesFiat: response.body.filter(val => val[this.currency] > 0).map(val => [val.timestamp * 1000, val.avgFees / 100000000 * val[this.currency], val.avgHeight]),
});
this.isLoading = false;
}),
@@ -157,7 +173,7 @@ export class BlockFeesGraphComponent implements OnInit {
if (tick.seriesIndex === 0) {
tooltip += `${tick.marker} ${tick.seriesName}: ${formatNumber(tick.data[1], this.locale, '1.3-3')} BTC<br>`;
} else if (tick.seriesIndex === 1) {
tooltip += `${tick.marker} ${tick.seriesName}: ${formatCurrency(tick.data[1], this.locale, getCurrencySymbol('USD', 'narrow'), 'USD', '1.0-0')}<br>`;
tooltip += `${tick.marker} ${tick.seriesName}: ${this.fiatCurrencyPipe.transform(tick.data[1], null, this.currency) }<br>`;
}
}
@@ -184,7 +200,7 @@ export class BlockFeesGraphComponent implements OnInit {
icon: 'roundRect',
},
{
name: 'Fees USD',
name: 'Fees ' + this.currency,
inactiveColor: 'rgb(110, 112, 121)',
textStyle: {
color: 'white',
@@ -216,7 +232,7 @@ export class BlockFeesGraphComponent implements OnInit {
axisLabel: {
color: 'rgb(110, 112, 121)',
formatter: function(val) {
return this.fiatShortenerPipe.transform(val);
return this.fiatShortenerPipe.transform(val, null, this.currency);
}.bind(this)
},
splitLine: {
@@ -243,8 +259,8 @@ export class BlockFeesGraphComponent implements OnInit {
legendHoverLink: false,
zlevel: 1,
yAxisIndex: 1,
name: 'Fees USD',
data: data.blockFeesUSD,
name: 'Fees ' + this.currency,
data: data.blockFeesFiat,
type: 'line',
smooth: 0.25,
symbol: 'none',

View File

@@ -1,16 +1,19 @@
import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit } from '@angular/core';
import { EChartsOption, graphic } from 'echarts';
import { Observable } from 'rxjs';
import { Observable, Subscription } from 'rxjs';
import { map, share, startWith, switchMap, tap } from 'rxjs/operators';
import { ApiService } from '../../services/api.service';
import { SeoService } from '../../services/seo.service';
import { formatCurrency, formatNumber, getCurrencySymbol } from '@angular/common';
import { formatNumber } from '@angular/common';
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
import { download, formatterXAxis, formatterXAxisLabel, formatterXAxisTimeCategory } from '../../shared/graphs.utils';
import { MiningService } from '../../services/mining.service';
import { StateService } from '../../services/state.service';
import { StorageService } from '../../services/storage.service';
import { ActivatedRoute } from '@angular/router';
import { FiatShortenerPipe } from '../../shared/pipes/fiat-shortener.pipe';
import { FiatCurrencyPipe } from '../../shared/pipes/fiat-currency.pipe';
import { fiatCurrencies } from '../../app.constants';
@Component({
selector: 'app-block-rewards-graph',
@@ -44,16 +47,28 @@ export class BlockRewardsGraphComponent implements OnInit {
timespan = '';
chartInstance: any = undefined;
currencySubscription: Subscription;
currency: string;
constructor(
@Inject(LOCALE_ID) public locale: string,
private seoService: SeoService,
private apiService: ApiService,
private formBuilder: UntypedFormBuilder,
private miningService: MiningService,
private stateService: StateService,
private storageService: StorageService,
private route: ActivatedRoute,
private fiatShortenerPipe: FiatShortenerPipe,
private fiatCurrencyPipe: FiatCurrencyPipe,
) {
this.currencySubscription = this.stateService.fiatCurrency$.subscribe((fiat) => {
if (fiat && fiatCurrencies[fiat]?.indexed) {
this.currency = fiat;
} else {
this.currency = 'USD';
}
});
}
ngOnInit(): void {
@@ -82,7 +97,7 @@ export class BlockRewardsGraphComponent implements OnInit {
tap((response) => {
this.prepareChartOptions({
blockRewards: response.body.map(val => [val.timestamp * 1000, val.avgRewards / 100000000, val.avgHeight]),
blockRewardsUSD: response.body.filter(val => val.USD > 0).map(val => [val.timestamp * 1000, val.avgRewards / 100000000 * val.USD, val.avgHeight]),
blockRewardsFiat: response.body.filter(val => val[this.currency] > 0).map(val => [val.timestamp * 1000, val.avgRewards / 100000000 * val[this.currency], val.avgHeight]),
});
this.isLoading = false;
}),
@@ -157,7 +172,7 @@ export class BlockRewardsGraphComponent implements OnInit {
if (tick.seriesIndex === 0) {
tooltip += `${tick.marker} ${tick.seriesName}: ${formatNumber(tick.data[1], this.locale, '1.3-3')} BTC<br>`;
} else if (tick.seriesIndex === 1) {
tooltip += `${tick.marker} ${tick.seriesName}: ${formatCurrency(tick.data[1], this.locale, getCurrencySymbol('USD', 'narrow'), 'USD', '1.0-0')}<br>`;
tooltip += `${tick.marker} ${tick.seriesName}: ${this.fiatCurrencyPipe.transform(tick.data[1], null, this.currency)}<br>`;
}
}
@@ -184,7 +199,7 @@ export class BlockRewardsGraphComponent implements OnInit {
icon: 'roundRect',
},
{
name: 'Rewards USD',
name: 'Rewards ' + this.currency,
inactiveColor: 'rgb(110, 112, 121)',
textStyle: {
color: 'white',
@@ -228,7 +243,7 @@ export class BlockRewardsGraphComponent implements OnInit {
axisLabel: {
color: 'rgb(110, 112, 121)',
formatter: function(val) {
return this.fiatShortenerPipe.transform(val);
return this.fiatShortenerPipe.transform(val, null, this.currency);
}.bind(this)
},
splitLine: {
@@ -251,8 +266,8 @@ export class BlockRewardsGraphComponent implements OnInit {
legendHoverLink: false,
zlevel: 1,
yAxisIndex: 1,
name: 'Rewards USD',
data: data.blockRewardsUSD,
name: 'Rewards ' + this.currency,
data: data.blockRewardsFiat,
type: 'line',
smooth: 0.25,
symbol: 'none',

View File

@@ -143,7 +143,7 @@
</ng-template>
</tr>
<tr *ngIf="network !== 'liquid' && network !== 'liquidtestnet'">
<td i18n="block.subsidy-and-fees|Total subsidy and fees in a block">Subsidy + fees:</td>
<td i18n="block.subsidy-and-fees|Total subsidy and fees in a block">Subsidy + fees</td>
<td>
<app-amount [satoshis]="block.extras.reward" digitsInfo="1.2-3" [noFiat]="true"></app-amount>
<span class="fiat">
@@ -158,7 +158,7 @@
<td style="width: 75%;"><span class="skeleton-loader"></span></td>
</tr>
<tr *ngIf="network !== 'liquid' && network !== 'liquidtestnet'">
<td i18n="block.subsidy-and-fees|Total subsidy and fees in a block">Subsidy + fees:</td>
<td i18n="block.subsidy-and-fees|Total subsidy and fees in a block">Subsidy + fees</td>
<td><span class="skeleton-loader"></span></td>
</tr>
</ng-template>

View File

@@ -57,7 +57,7 @@ export class BlockComponent implements OnInit, OnDestroy {
transactionsError: any = null;
overviewError: any = null;
webGlEnabled = true;
indexingAvailable = false;
auditSupported: boolean = this.stateService.env.AUDIT && this.stateService.env.BASE_MODULE === 'mempool' && this.stateService.env.MINING_DASHBOARD === true;
auditModeEnabled: boolean = !this.stateService.hideAudit.value;
auditAvailable = true;
showAudit: boolean;
@@ -109,13 +109,14 @@ export class BlockComponent implements OnInit, OnDestroy {
this.timeLtr = !!ltr;
});
this.indexingAvailable = (this.stateService.env.BASE_MODULE === 'mempool' && this.stateService.env.MINING_DASHBOARD === true);
this.setAuditAvailable(this.indexingAvailable);
this.setAuditAvailable(this.auditSupported);
this.auditPrefSubscription = this.stateService.hideAudit.subscribe((hide) => {
this.auditModeEnabled = !hide;
this.showAudit = this.auditAvailable && this.auditModeEnabled;
});
if (this.auditSupported) {
this.auditPrefSubscription = this.stateService.hideAudit.subscribe((hide) => {
this.auditModeEnabled = !hide;
this.showAudit = this.auditAvailable && this.auditModeEnabled;
});
}
this.txsLoadingStatus$ = this.route.paramMap
.pipe(
@@ -221,7 +222,9 @@ export class BlockComponent implements OnInit, OnDestroy {
setTimeout(() => {
this.nextBlockSubscription = this.apiService.getBlock$(block.previousblockhash).subscribe();
this.nextBlockTxListSubscription = this.electrsApiService.getBlockTransactions$(block.previousblockhash).subscribe();
this.apiService.getBlockAudit$(block.previousblockhash);
if (this.auditSupported) {
this.apiService.getBlockAudit$(block.previousblockhash);
}
}, 100);
}
this.updateAuditAvailableFromBlockHeight(block.height);
@@ -269,7 +272,7 @@ export class BlockComponent implements OnInit, OnDestroy {
this.isLoadingOverview = false;
});
if (!this.indexingAvailable) {
if (!this.auditSupported) {
this.overviewSubscription = block$.pipe(
startWith(null),
pairwise(),
@@ -300,7 +303,7 @@ export class BlockComponent implements OnInit, OnDestroy {
});
}
if (this.indexingAvailable) {
if (this.auditSupported) {
this.auditSubscription = block$.pipe(
startWith(null),
pairwise(),
@@ -605,7 +608,7 @@ export class BlockComponent implements OnInit, OnDestroy {
setAuditAvailable(available: boolean): void {
this.auditAvailable = available;
this.showAudit = this.auditAvailable && this.auditModeEnabled;
this.showAudit = this.auditAvailable && this.auditModeEnabled && this.auditSupported;
}
toggleAuditMode(): void {
@@ -613,6 +616,9 @@ export class BlockComponent implements OnInit, OnDestroy {
}
updateAuditAvailableFromBlockHeight(blockHeight: number): void {
if (!this.auditSupported) {
this.setAuditAvailable(false);
}
switch (this.stateService.network) {
case 'testnet':
if (blockHeight < this.stateService.env.TESTNET_BLOCK_AUDIT_START_HEIGHT) {

View File

@@ -14,7 +14,7 @@
i18n-ngbTooltip="mining.pool-name" ngbTooltip="Pool" placement="bottom" #miningpool [disableTooltip]="!isEllipsisActive(miningpool)">Pool</th>
<th class="timestamp" i18n="latest-blocks.timestamp" *ngIf="!widget" [class]="indexingAvailable ? '' : 'legacy'">Timestamp</th>
<th class="mined" i18n="latest-blocks.mined" *ngIf="!widget" [class]="indexingAvailable ? '' : 'legacy'">Mined</th>
<th *ngIf="indexingAvailable" class="health text-right" i18n="latest-blocks.health" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}"
<th *ngIf="auditAvailable" class="health text-right" i18n="latest-blocks.health" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}"
i18n-ngbTooltip="latest-blocks.health" ngbTooltip="Health" placement="bottom" #health [disableTooltip]="!isEllipsisActive(health)">Health</th>
<th *ngIf="indexingAvailable" class="reward text-right" i18n="latest-blocks.reward" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}"
i18n-ngbTooltip="latest-blocks.reward" ngbTooltip="Reward" placement="bottom" #reward [disableTooltip]="!isEllipsisActive(reward)">Reward</th>
@@ -45,7 +45,7 @@
<td class="mined" *ngIf="!widget" [class]="indexingAvailable ? '' : 'legacy'">
<app-time-since [time]="block.timestamp" [fastRender]="true"></app-time-since>
</td>
<td *ngIf="indexingAvailable" class="health text-right" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}">
<td *ngIf="auditAvailable" class="health text-right" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}">
<a
class="health-badge badge"
[class.badge-success]="auditScores[block.id] >= 99"
@@ -98,7 +98,7 @@
<td class="mined" *ngIf="!widget" [class]="indexingAvailable ? '' : 'legacy'">
<span class="skeleton-loader" style="max-width: 125px"></span>
</td>
<td *ngIf="indexingAvailable" class="health text-left" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}">
<td *ngIf="auditAvailable" class="health text-left" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}">
<span class="skeleton-loader" style="max-width: 75px"></span>
</td>
<td *ngIf="indexingAvailable" class="reward text-right" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}">

View File

@@ -22,6 +22,7 @@ export class BlocksList implements OnInit, OnDestroy {
latestScoreSubscription: Subscription;
indexingAvailable = false;
auditAvailable = false;
isLoading = true;
loadingScores = true;
fromBlockHeight = undefined;
@@ -44,6 +45,7 @@ export class BlocksList implements OnInit, OnDestroy {
ngOnInit(): void {
this.indexingAvailable = (this.stateService.env.BASE_MODULE === 'mempool' &&
this.stateService.env.MINING_DASHBOARD === true);
this.auditAvailable = this.indexingAvailable && this.stateService.env.AUDIT;
if (!this.widget) {
this.websocketService.want(['blocks']);
@@ -111,7 +113,7 @@ export class BlocksList implements OnInit, OnDestroy {
}, [])
);
if (this.indexingAvailable) {
if (this.indexingAvailable && this.auditAvailable) {
this.auditScoreSubscription = this.fromHeightSubject.pipe(
switchMap((fromBlockHeight) => {
this.loadingScores = true;

View File

@@ -0,0 +1,5 @@
<div [formGroup]="fiatForm" class="text-small text-center">
<select formControlName="fiat" class="custom-select custom-select-sm form-control-secondary form-control mx-auto" style="width: 200px;" (change)="changeFiat()">
<option *ngFor="let currency of currencies" [value]="currency[1].code">{{ currency[1].name + " (" + currency[1].code + ")" }}</option>
</select>
</div>

View File

@@ -0,0 +1,45 @@
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
import { StorageService } from '../../services/storage.service';
import { fiatCurrencies } from '../../app.constants';
import { StateService } from '../../services/state.service';
@Component({
selector: 'app-fiat-selector',
templateUrl: './fiat-selector.component.html',
styleUrls: ['./fiat-selector.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class FiatSelectorComponent implements OnInit {
fiatForm: UntypedFormGroup;
currencies = Object.entries(fiatCurrencies).sort((a: any, b: any) => {
if (a[1].name < b[1].name) {
return -1;
}
if (a[1].name > b[1].name) {
return 1;
}
return 0;
});
constructor(
private formBuilder: UntypedFormBuilder,
private stateService: StateService,
private storageService: StorageService,
) { }
ngOnInit() {
this.fiatForm = this.formBuilder.group({
fiat: ['USD']
});
this.stateService.fiatCurrency$.subscribe((fiat) => {
this.fiatForm.get('fiat')?.setValue(fiat);
});
}
changeFiat() {
const newFiat = this.fiatForm.get('fiat')?.value;
this.storageService.setValue('fiat-preference', newFiat);
this.stateService.fiatCurrency$.next(newFiat);
}
}

View File

@@ -22,7 +22,7 @@
i18n="mining.block-rewards">Block Rewards</a>
<a class="dropdown-item" routerLinkActive="active"
[routerLink]="['/graphs/mining/block-sizes-weights' | relativeUrl]" i18n="mining.block-sizes-weights">Block Sizes and Weights</a>
<a class="dropdown-item" routerLinkActive="active"
<a *ngIf="stateService.env.AUDIT" class="dropdown-item" routerLinkActive="active"
[routerLink]="['/graphs/mining/block-prediction' | relativeUrl]" i18n="mining.block-prediction-accuracy">Block Prediction Accuracy</a>
</div>
</div>

View File

@@ -1,5 +1,5 @@
<div [formGroup]="languageForm" class="text-small text-center">
<select formControlName="language" class="custom-select custom-select-sm form-control-secondary form-control mx-auto" style="width: 130px;" (change)="changeLanguage()">
<select formControlName="language" class="custom-select custom-select-sm form-control-secondary form-control mx-auto" style="width: 200px;" (change)="changeLanguage()">
<option *ngFor="let lang of languages" [value]="lang.code">{{ lang.name }}</option>
</select>
</div>

View File

@@ -74,4 +74,24 @@
</div>
</div>
<div class="pref-selectors">
<div class="selector">
<app-language-selector></app-language-selector>
</div>
<div class="selector">
<app-fiat-selector></app-fiat-selector>
</div>
</div>
<div class="terms-of-service">
<a [routerLink]="['/terms-of-service']" i18n="shared.terms-of-service|Terms of Service">Terms of Service</a>
|
<a [routerLink]="['/privacy-policy']" i18n="shared.privacy-policy|Privacy Policy">Privacy Policy</a>
|
<a [routerLink]="['/tx/push' | relativeUrl]" i18n="shared.broadcast-transaction|Broadcast Transaction">Broadcast Transaction</a>
</div>
<br>
</div>

View File

@@ -103,4 +103,23 @@
margin-bottom: 10px;
text-decoration: none;
color: inherit;
}
.terms-of-service {
margin-top: 1rem;
}
.pref-selectors {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
.selector {
margin-left: .5em;
margin-bottom: .5em;
&:first {
margin-left: 0;
}
}
}

View File

@@ -235,10 +235,10 @@
</span>
</td>
<td class="reward text-right">
<app-amount [satoshis]="block.extras.reward" digitsInfo="1.2-2"></app-amount>
<app-amount [satoshis]="block.extras.reward" digitsInfo="1.2-2" [noFiat]="true"></app-amount>
</td>
<td class="fees text-right">
<app-amount [satoshis]="block.extras.totalFees" digitsInfo="1.2-2"></app-amount>
<app-amount [satoshis]="block.extras.totalFees" digitsInfo="1.2-2" [noFiat]="true"></app-amount>
</td>
<td class="txs text-right">
{{ block.tx_count | number }}