Merge pull request #1556 from mempool/nymkappa/feature/update-block-api
Update block API
This commit is contained in:
commit
f879887fc5
@ -134,7 +134,7 @@ class Blocks {
|
||||
blockExtended.extras.avgFeeRate = stats.avgfeerate;
|
||||
}
|
||||
|
||||
if (Common.indexingEnabled()) {
|
||||
if (['mainnet', 'testnet', 'signet', 'regtest'].includes(config.MEMPOOL.NETWORK)) {
|
||||
let pool: PoolTag;
|
||||
if (blockExtended.extras?.coinbaseTx !== undefined) {
|
||||
pool = await this.$findBlockMiner(blockExtended.extras?.coinbaseTx);
|
||||
@ -143,7 +143,8 @@ class Blocks {
|
||||
}
|
||||
|
||||
if (!pool) { // We should never have this situation in practise
|
||||
logger.warn(`Cannot assign pool to block ${blockExtended.height} and 'unknown' pool does not exist. Check your "pools" table entries`);
|
||||
logger.warn(`Cannot assign pool to block ${blockExtended.height} and 'unknown' pool does not exist. ` +
|
||||
`Check your "pools" table entries`);
|
||||
return blockExtended;
|
||||
}
|
||||
|
||||
@ -389,6 +390,45 @@ class Blocks {
|
||||
return prepareBlock(blockExtended);
|
||||
}
|
||||
|
||||
/**
|
||||
* Index a block by hash if it's missing from the database. Returns the block after indexing
|
||||
*/
|
||||
public async $getBlock(hash: string): Promise<BlockExtended | IEsploraApi.Block> {
|
||||
// Check the memory cache
|
||||
const blockByHash = this.getBlocks().find((b) => b.id === hash);
|
||||
if (blockByHash) {
|
||||
return blockByHash;
|
||||
}
|
||||
|
||||
// Block has already been indexed
|
||||
if (Common.indexingEnabled()) {
|
||||
const dbBlock = await blocksRepository.$getBlockByHash(hash);
|
||||
if (dbBlock != null) {
|
||||
logger.info('GET BLOCK: already indexed');
|
||||
return prepareBlock(dbBlock);
|
||||
}
|
||||
}
|
||||
|
||||
const block = await bitcoinApi.$getBlock(hash);
|
||||
|
||||
// Not Bitcoin network, return the block as it
|
||||
if (['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) === false) {
|
||||
logger.info('GET BLOCK: using bitcoin backend');
|
||||
return block;
|
||||
}
|
||||
|
||||
// Bitcoin network, add our custom data on top
|
||||
logger.info('GET BLOCK: index block on the fly');
|
||||
const transactions = await this.$getTransactionsExtended(hash, block.height, true);
|
||||
const blockExtended = await this.$getBlockExtended(block, transactions);
|
||||
if (Common.indexingEnabled()) {
|
||||
delete(blockExtended['coinbaseTx']);
|
||||
await blocksRepository.$saveBlockInDatabase(blockExtended);
|
||||
}
|
||||
|
||||
return blockExtended;
|
||||
}
|
||||
|
||||
public async $getBlocksExtras(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.
|
||||
|
@ -172,9 +172,9 @@ export class Common {
|
||||
|
||||
static indexingEnabled(): boolean {
|
||||
return (
|
||||
['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) &&
|
||||
['mainnet', 'testnet', 'signet', 'regtest'].includes(config.MEMPOOL.NETWORK) &&
|
||||
config.DATABASE.ENABLED === true &&
|
||||
config.MEMPOOL.INDEXING_BLOCKS_AMOUNT != 0
|
||||
config.MEMPOOL.INDEXING_BLOCKS_AMOUNT !== 0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -350,7 +350,8 @@ class Server {
|
||||
|
||||
this.app
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'blocks-extras', routes.getBlocksExtras)
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'blocks-extras/:height', routes.getBlocksExtras);
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'blocks-extras/:height', routes.getBlocksExtras)
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash', routes.getBlock);
|
||||
|
||||
if (config.MEMPOOL.BACKEND !== 'esplora') {
|
||||
this.app
|
||||
@ -362,7 +363,6 @@ class Server {
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'tx/:txId/hex', routes.getRawTransaction)
|
||||
.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 + 'block/:hash', routes.getBlock)
|
||||
.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)
|
||||
|
@ -287,6 +287,7 @@ class BlocksRepository {
|
||||
return null;
|
||||
}
|
||||
|
||||
rows[0].fee_span = JSON.parse(rows[0].fee_span);
|
||||
return rows[0];
|
||||
} catch (e) {
|
||||
logger.err(`Cannot get indexed block ${height}. Reason: ` + (e instanceof Error ? e.message : e));
|
||||
@ -294,6 +295,34 @@ class BlocksRepository {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get one block by hash
|
||||
*/
|
||||
public async $getBlockByHash(hash: string): Promise<object | null> {
|
||||
try {
|
||||
const query = `
|
||||
SELECT *, UNIX_TIMESTAMP(blocks.blockTimestamp) as blockTimestamp, hash as id,
|
||||
pools.id as pool_id, pools.name as pool_name, pools.link as pool_link, pools.slug as pool_slug,
|
||||
pools.addresses as pool_addresses, pools.regexes as pool_regexes,
|
||||
previous_block_hash as previousblockhash
|
||||
FROM blocks
|
||||
JOIN pools ON blocks.pool_id = pools.id
|
||||
WHERE hash = '${hash}';
|
||||
`;
|
||||
const [rows]: any[] = await DB.query(query);
|
||||
|
||||
if (rows.length <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
rows[0].fee_span = JSON.parse(rows[0].fee_span);
|
||||
return rows[0];
|
||||
} catch (e) {
|
||||
logger.err(`Cannot get indexed block ${hash}. Reason: ` + (e instanceof Error ? e.message : e));
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return blocks difficulty
|
||||
*/
|
||||
|
@ -702,8 +702,8 @@ class Routes {
|
||||
|
||||
public async getBlock(req: Request, res: Response) {
|
||||
try {
|
||||
const result = await bitcoinApi.$getBlock(req.params.hash);
|
||||
res.json(result);
|
||||
const block = await blocks.$getBlock(req.params.hash);
|
||||
res.json(block);
|
||||
} catch (e) {
|
||||
res.status(500).send(e instanceof Error ? e.message : e);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { BlockExtended } from "../mempool.interfaces";
|
||||
import { BlockExtended } from '../mempool.interfaces';
|
||||
|
||||
export function prepareBlock(block: any): BlockExtended {
|
||||
return <BlockExtended>{
|
||||
@ -17,9 +17,11 @@ export function prepareBlock(block: any): BlockExtended {
|
||||
extras: {
|
||||
coinbaseRaw: block.coinbase_raw ?? block.extras.coinbaseRaw,
|
||||
medianFee: block.medianFee ?? block.median_fee ?? block.extras?.medianFee,
|
||||
feeRange: block.feeRange ?? block.fee_range ?? block?.extras?.feeSpan,
|
||||
feeRange: block.feeRange ?? block.fee_span,
|
||||
reward: block.reward ?? block?.extras?.reward,
|
||||
totalFees: block.totalFees ?? block?.fees ?? block?.extras.totalFees,
|
||||
totalFees: block.totalFees ?? block?.fees ?? block?.extras?.totalFees,
|
||||
avgFee: block?.extras?.avgFee ?? block.avg_fee,
|
||||
avgFeeRate: block?.avgFeeRate ?? block.avg_fee_rate,
|
||||
pool: block?.extras?.pool ?? (block?.pool_id ? {
|
||||
id: block.pool_id,
|
||||
name: block.pool_name,
|
||||
|
@ -40,7 +40,6 @@ import { AssetComponent } from './components/asset/asset.component';
|
||||
import { AssetsComponent } from './components/assets/assets.component';
|
||||
import { AssetsNavComponent } from './components/assets/assets-nav/assets-nav.component';
|
||||
import { StatusViewComponent } from './components/status-view/status-view.component';
|
||||
import { MinerComponent } from './components/miner/miner.component';
|
||||
import { SharedModule } from './shared/shared.module';
|
||||
import { NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { FeesBoxComponent } from './components/fees-box/fees-box.component';
|
||||
@ -108,7 +107,6 @@ import { BlockSizesWeightsGraphComponent } from './components/block-sizes-weight
|
||||
LbtcPegsGraphComponent,
|
||||
AssetComponent,
|
||||
AssetsComponent,
|
||||
MinerComponent,
|
||||
StatusViewComponent,
|
||||
FeesBoxComponent,
|
||||
DashboardComponent,
|
||||
|
@ -81,15 +81,26 @@
|
||||
<ng-template [ngIf]="fees !== undefined" [ngIfElse]="loadingFees">
|
||||
<tr>
|
||||
<td i18n="block.total-fees|Total fees in a block">Total fees</td>
|
||||
<td *ngIf="network !== 'liquid' && network !== 'liquidtestnet'; else liquidTotalFees"><app-amount [satoshis]="fees * 100000000" digitsInfo="1.2-2" [noFiat]="true"></app-amount> <span class="fiat"><app-fiat [value]="fees * 100000000" digitsInfo="1.0-0"></app-fiat></span></td>
|
||||
<td *ngIf="network !== 'liquid' && network !== 'liquidtestnet'; else liquidTotalFees">
|
||||
<app-amount [satoshis]="block.extras.totalFees" digitsInfo="1.2-3" [noFiat]="true"></app-amount>
|
||||
<span class="fiat">
|
||||
<app-fiat [value]="block.extras.totalFees" digitsInfo="1.0-0"></app-fiat>
|
||||
</span>
|
||||
</td>
|
||||
<ng-template #liquidTotalFees>
|
||||
<td><app-amount [satoshis]="fees * 100000000" digitsInfo="1.2-2" [noFiat]="true"></app-amount> <app-fiat [value]="fees * 100000000" digitsInfo="1.2-2"></app-fiat></td>
|
||||
<td>
|
||||
<app-amount [satoshis]="fees * 100000000" digitsInfo="1.2-2" [noFiat]="true"></app-amount> <app-fiat
|
||||
[value]="fees * 100000000" digitsInfo="1.2-2"></app-fiat>
|
||||
</td>
|
||||
</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>
|
||||
<app-amount [satoshis]="(blockSubsidy + fees) * 100000000" digitsInfo="1.2-2" [noFiat]="true"></app-amount> <span class="fiat"><app-fiat [value]="(blockSubsidy + fees) * 100000000" digitsInfo="1.0-0"></app-fiat></span>
|
||||
<app-amount [satoshis]="block.extras.reward" digitsInfo="1.2-3" [noFiat]="true"></app-amount>
|
||||
<span class="fiat">
|
||||
<app-fiat [value]="(blockSubsidy + fees) * 100000000" digitsInfo="1.0-0"></app-fiat>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
@ -105,7 +116,18 @@
|
||||
</ng-template>
|
||||
<tr>
|
||||
<td i18n="block.miner">Miner</td>
|
||||
<td><app-miner [coinbaseTransaction]="coinbaseTx"></app-miner></td>
|
||||
<td *ngIf="stateService.env.MINING_DASHBOARD">
|
||||
<a placement="bottom" [routerLink]="['/mining/pool' | relativeUrl, block.extras.pool.slug]" class="badge"
|
||||
[class]="block.extras.pool.name === 'Unknown' ? 'badge-secondary' : 'badge-primary'">
|
||||
{{ block.extras.pool.name }}
|
||||
</a>
|
||||
</td>
|
||||
<td *ngIf="!stateService.env.MINING_DASHBOARD && stateService.env.BASE_MODULE === 'mempool'">
|
||||
<span placement="bottom" class="badge"
|
||||
[class]="block.extras.pool.name === 'Unknown' ? 'badge-secondary' : 'badge-primary'">
|
||||
{{ block.extras.pool.name }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -10,6 +10,7 @@ import { SeoService } from 'src/app/services/seo.service';
|
||||
import { WebsocketService } from 'src/app/services/websocket.service';
|
||||
import { RelativeUrlPipe } from 'src/app/shared/pipes/relative-url/relative-url.pipe';
|
||||
import { BlockExtended } from 'src/app/interfaces/node-api.interface';
|
||||
import { ApiService } from 'src/app/services/api.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-block',
|
||||
@ -31,7 +32,6 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||
blockSubsidy: number;
|
||||
fees: number;
|
||||
paginationMaxSize: number;
|
||||
coinbaseTx: Transaction;
|
||||
page = 1;
|
||||
itemsPerPage: number;
|
||||
txsLoadingStatus$: Observable<number>;
|
||||
@ -50,10 +50,11 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||
private location: Location,
|
||||
private router: Router,
|
||||
private electrsApiService: ElectrsApiService,
|
||||
private stateService: StateService,
|
||||
public stateService: StateService,
|
||||
private seoService: SeoService,
|
||||
private websocketService: WebsocketService,
|
||||
private relativeUrlPipe: RelativeUrlPipe,
|
||||
private apiService: ApiService
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
@ -88,7 +89,6 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||
const blockHash: string = params.get('id') || '';
|
||||
this.block = undefined;
|
||||
this.page = 1;
|
||||
this.coinbaseTx = undefined;
|
||||
this.error = undefined;
|
||||
this.fees = undefined;
|
||||
this.stateService.markBlock$.next({});
|
||||
@ -124,7 +124,7 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||
this.location.replaceState(
|
||||
this.router.createUrlTree([(this.network ? '/' + this.network : '') + '/block/', hash]).toString()
|
||||
);
|
||||
return this.electrsApiService.getBlock$(hash);
|
||||
return this.apiService.getBlock$(hash);
|
||||
})
|
||||
);
|
||||
}
|
||||
@ -134,7 +134,7 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||
return of(blockInCache);
|
||||
}
|
||||
|
||||
return this.electrsApiService.getBlock$(blockHash);
|
||||
return this.apiService.getBlock$(blockHash);
|
||||
}
|
||||
}),
|
||||
tap((block: BlockExtended) => {
|
||||
@ -145,7 +145,6 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||
|
||||
this.seoService.setTitle($localize`:@@block.component.browser-title:Block ${block.height}:BLOCK_HEIGHT:: ${block.id}:BLOCK_ID:`);
|
||||
this.isLoadingBlock = false;
|
||||
this.coinbaseTx = block?.extras?.coinbaseTx;
|
||||
this.setBlockSubsidy();
|
||||
if (block?.extras?.reward !== undefined) {
|
||||
this.fees = block.extras.reward / 100000000 - this.blockSubsidy;
|
||||
@ -167,9 +166,6 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||
if (this.fees === undefined && transactions[0]) {
|
||||
this.fees = transactions[0].vout.reduce((acc: number, curr: Vout) => acc + curr.value, 0) / 100000000 - this.blockSubsidy;
|
||||
}
|
||||
if (!this.coinbaseTx && transactions[0]) {
|
||||
this.coinbaseTx = transactions[0];
|
||||
}
|
||||
this.transactions = transactions;
|
||||
this.isLoadingTransactions = false;
|
||||
},
|
||||
@ -212,13 +208,10 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||
this.queryParamsSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
// TODO - Refactor this.fees/this.reward for liquid because it is not
|
||||
// used anymore on Bitcoin networks (we use block.extras directly)
|
||||
setBlockSubsidy() {
|
||||
if (this.network === 'liquid' || this.network === 'liquidtestnet') {
|
||||
this.blockSubsidy = 0;
|
||||
return;
|
||||
}
|
||||
const halvings = Math.floor(this.block.height / 210000);
|
||||
this.blockSubsidy = 50 * 2 ** -halvings;
|
||||
}
|
||||
|
||||
pageChange(page: number, target: HTMLElement) {
|
||||
|
@ -1,12 +0,0 @@
|
||||
<ng-template [ngIf]="loading" [ngIfElse]="done">
|
||||
<span class="skeleton-loader"></span>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #done>
|
||||
<ng-template [ngIf]="miner" [ngIfElse]="unknownMiner">
|
||||
<a placement="bottom" [ngbTooltip]="title" [href]="url" [target]="target" class="badge badge-primary">{{ miner }}</a>
|
||||
</ng-template>
|
||||
<ng-template #unknownMiner>
|
||||
<span class="badge badge-secondary" i18n="miner.tag.unknown-miner">Unknown</span>
|
||||
</ng-template>
|
||||
</ng-template>
|
@ -1,3 +0,0 @@
|
||||
.badge {
|
||||
font-size: 14px;
|
||||
}
|
@ -1,94 +0,0 @@
|
||||
import { Component, Input, OnChanges, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
|
||||
import { AssetsService } from 'src/app/services/assets.service';
|
||||
import { Transaction } from 'src/app/interfaces/electrs.interface';
|
||||
import { StateService } from 'src/app/services/state.service';
|
||||
import { RelativeUrlPipe } from 'src/app/shared/pipes/relative-url/relative-url.pipe';
|
||||
|
||||
@Component({
|
||||
selector: 'app-miner',
|
||||
templateUrl: './miner.component.html',
|
||||
styleUrls: ['./miner.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class MinerComponent implements OnChanges {
|
||||
@Input() coinbaseTransaction: Transaction;
|
||||
miner = '';
|
||||
title = '';
|
||||
url = '';
|
||||
target = '_blank';
|
||||
loading = true;
|
||||
|
||||
constructor(
|
||||
private assetsService: AssetsService,
|
||||
private cd: ChangeDetectorRef,
|
||||
public stateService: StateService,
|
||||
private relativeUrlPipe: RelativeUrlPipe,
|
||||
) { }
|
||||
|
||||
ngOnChanges() {
|
||||
this.miner = '';
|
||||
if (this.stateService.env.MINING_DASHBOARD) {
|
||||
this.miner = 'Unknown';
|
||||
this.url = this.relativeUrlPipe.transform(`/mining/pool/unknown`);
|
||||
this.target = '';
|
||||
}
|
||||
this.loading = true;
|
||||
this.findMinerFromCoinbase();
|
||||
}
|
||||
|
||||
findMinerFromCoinbase() {
|
||||
if (this.coinbaseTransaction == null || this.coinbaseTransaction.vin == null || this.coinbaseTransaction.vin.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
this.assetsService.getMiningPools$.subscribe((pools) => {
|
||||
for (const vout of this.coinbaseTransaction.vout) {
|
||||
if (!vout.scriptpubkey_address) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pools.payout_addresses[vout.scriptpubkey_address]) {
|
||||
this.miner = pools.payout_addresses[vout.scriptpubkey_address].name;
|
||||
this.title = $localize`:@@miner-identified-by-payout:Identified by payout address: '${vout.scriptpubkey_address}:PAYOUT_ADDRESS:'`;
|
||||
const pool = pools.payout_addresses[vout.scriptpubkey_address];
|
||||
if (this.stateService.env.MINING_DASHBOARD && pools.slugs && pools.slugs[pool.name] !== undefined) {
|
||||
this.url = this.relativeUrlPipe.transform(`/mining/pool/${pools.slugs[pool.name]}`);
|
||||
this.target = '';
|
||||
} else {
|
||||
this.url = pool.link;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
for (const tag in pools.coinbase_tags) {
|
||||
if (pools.coinbase_tags.hasOwnProperty(tag)) {
|
||||
const coinbaseAscii = this.hex2ascii(this.coinbaseTransaction.vin[0].scriptsig);
|
||||
if (coinbaseAscii.indexOf(tag) > -1) {
|
||||
const pool = pools.coinbase_tags[tag];
|
||||
this.miner = pool.name;
|
||||
this.title = $localize`:@@miner-identified-by-coinbase:Identified by coinbase tag: '${tag}:TAG:'`;
|
||||
if (this.stateService.env.MINING_DASHBOARD && pools.slugs && pools.slugs[pool.name] !== undefined) {
|
||||
this.url = this.relativeUrlPipe.transform(`/mining/pool/${pools.slugs[pool.name]}`);
|
||||
this.target = '';
|
||||
} else {
|
||||
this.url = pool.link;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.loading = false;
|
||||
this.cd.markForCheck();
|
||||
});
|
||||
}
|
||||
|
||||
hex2ascii(hex: string) {
|
||||
let str = '';
|
||||
for (let i = 0; i < hex.length; i += 2) {
|
||||
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
|
||||
}
|
||||
return str;
|
||||
}
|
||||
}
|
@ -154,6 +154,10 @@ export class ApiService {
|
||||
);
|
||||
}
|
||||
|
||||
getBlock$(hash: string): Observable<BlockExtended> {
|
||||
return this.httpClient.get<BlockExtended>(this.apiBaseUrl + this.apiBasePath + '/api/v1/block/' + hash);
|
||||
}
|
||||
|
||||
getHistoricalHashrate$(interval: string | undefined): Observable<any> {
|
||||
return this.httpClient.get<any[]>(
|
||||
this.apiBaseUrl + this.apiBasePath + `/api/v1/mining/hashrate` +
|
||||
|
@ -14,7 +14,6 @@ export class AssetsService {
|
||||
|
||||
getAssetsJson$: Observable<{ array: AssetExtended[]; objects: any}>;
|
||||
getAssetsMinimalJson$: Observable<any>;
|
||||
getMiningPools$: Observable<any>;
|
||||
|
||||
constructor(
|
||||
private httpClient: HttpClient,
|
||||
@ -66,6 +65,5 @@ export class AssetsService {
|
||||
}),
|
||||
shareReplay(1),
|
||||
);
|
||||
this.getMiningPools$ = this.httpClient.get(apiBaseUrl + '/resources/pools.json').pipe(shareReplay(1));
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user