mirror of
https://github.com/mempool/mempool.git
synced 2024-12-26 16:24:26 +01:00
Merge branch 'master' into lighthouse-parents-2
This commit is contained in:
commit
989d5b3263
@ -2,7 +2,7 @@ import { IEsploraApi } from './esplora-api.interface';
|
||||
|
||||
export interface AbstractBitcoinApi {
|
||||
$getRawMempool(): Promise<IEsploraApi.Transaction['txid'][]>;
|
||||
$getRawTransaction(txId: string, skipConversion?: boolean, addPrevout?: boolean): Promise<IEsploraApi.Transaction>;
|
||||
$getRawTransaction(txId: string, skipConversion?: boolean, addPrevout?: boolean, lazyPrevouts?: boolean): Promise<IEsploraApi.Transaction>;
|
||||
$getBlockHeightTip(): Promise<number>;
|
||||
$getTxIdsForBlock(hash: string): Promise<string[]>;
|
||||
$getBlockHash(height: number): Promise<string>;
|
||||
|
@ -31,7 +31,8 @@ class BitcoinApi implements AbstractBitcoinApi {
|
||||
};
|
||||
}
|
||||
|
||||
$getRawTransaction(txId: string, skipConversion = false, addPrevout = false): Promise<IEsploraApi.Transaction> {
|
||||
|
||||
$getRawTransaction(txId: string, skipConversion = false, addPrevout = false, lazyPrevouts = false): Promise<IEsploraApi.Transaction> {
|
||||
// If the transaction is in the mempool we already converted and fetched the fee. Only prevouts are missing
|
||||
const txInMempool = mempool.getMempool()[txId];
|
||||
if (txInMempool && addPrevout) {
|
||||
@ -46,7 +47,7 @@ class BitcoinApi implements AbstractBitcoinApi {
|
||||
});
|
||||
return transaction;
|
||||
}
|
||||
return this.$convertTransaction(transaction, addPrevout);
|
||||
return this.$convertTransaction(transaction, addPrevout, lazyPrevouts);
|
||||
})
|
||||
.catch((e: Error) => {
|
||||
if (e.message.startsWith('The genesis block coinbase')) {
|
||||
@ -126,7 +127,7 @@ class BitcoinApi implements AbstractBitcoinApi {
|
||||
const outSpends: IEsploraApi.Outspend[] = [];
|
||||
const tx = await this.$getRawTransaction(txId, true, false);
|
||||
for (let i = 0; i < tx.vout.length; i++) {
|
||||
if (tx.status && tx.status.block_height == 0) {
|
||||
if (tx.status && tx.status.block_height === 0) {
|
||||
outSpends.push({
|
||||
spent: false
|
||||
});
|
||||
@ -145,7 +146,7 @@ class BitcoinApi implements AbstractBitcoinApi {
|
||||
return this.bitcoindClient.getNetworkHashPs(120, blockHeight);
|
||||
}
|
||||
|
||||
protected async $convertTransaction(transaction: IBitcoinApi.Transaction, addPrevout: boolean): Promise<IEsploraApi.Transaction> {
|
||||
protected async $convertTransaction(transaction: IBitcoinApi.Transaction, addPrevout: boolean, lazyPrevouts = false): Promise<IEsploraApi.Transaction> {
|
||||
let esploraTransaction: IEsploraApi.Transaction = {
|
||||
txid: transaction.txid,
|
||||
version: transaction.version,
|
||||
@ -192,7 +193,7 @@ class BitcoinApi implements AbstractBitcoinApi {
|
||||
}
|
||||
|
||||
if (addPrevout) {
|
||||
esploraTransaction = await this.$calculateFeeFromInputs(esploraTransaction);
|
||||
esploraTransaction = await this.$calculateFeeFromInputs(esploraTransaction, false, lazyPrevouts);
|
||||
} else if (!transaction.confirmations) {
|
||||
esploraTransaction = await this.$appendMempoolFeeData(esploraTransaction);
|
||||
}
|
||||
@ -268,20 +269,30 @@ class BitcoinApi implements AbstractBitcoinApi {
|
||||
return this.bitcoindClient.getRawMemPool(true);
|
||||
}
|
||||
|
||||
private async $calculateFeeFromInputs(transaction: IEsploraApi.Transaction): Promise<IEsploraApi.Transaction> {
|
||||
|
||||
private async $calculateFeeFromInputs(transaction: IEsploraApi.Transaction, addPrevout: boolean, lazyPrevouts: boolean): Promise<IEsploraApi.Transaction> {
|
||||
if (transaction.vin[0].is_coinbase) {
|
||||
transaction.fee = 0;
|
||||
return transaction;
|
||||
}
|
||||
let totalIn = 0;
|
||||
for (const vin of transaction.vin) {
|
||||
const innerTx = await this.$getRawTransaction(vin.txid, false, false);
|
||||
vin.prevout = innerTx.vout[vin.vout];
|
||||
this.addInnerScriptsToVin(vin);
|
||||
totalIn += innerTx.vout[vin.vout].value;
|
||||
|
||||
for (let i = 0; i < transaction.vin.length; i++) {
|
||||
if (lazyPrevouts && i > 12) {
|
||||
transaction.vin[i].lazy = true;
|
||||
continue;
|
||||
}
|
||||
const innerTx = await this.$getRawTransaction(transaction.vin[i].txid, false, false);
|
||||
transaction.vin[i].prevout = innerTx.vout[transaction.vin[i].vout];
|
||||
this.addInnerScriptsToVin(transaction.vin[i]);
|
||||
totalIn += innerTx.vout[transaction.vin[i].vout].value;
|
||||
}
|
||||
if (lazyPrevouts && transaction.vin.length > 12) {
|
||||
transaction.fee = -1;
|
||||
} else {
|
||||
const totalOut = transaction.vout.reduce((p, output) => p + output.value, 0);
|
||||
transaction.fee = parseFloat((totalIn - totalOut).toFixed(8));
|
||||
}
|
||||
const totalOut = transaction.vout.reduce((p, output) => p + output.value, 0);
|
||||
transaction.fee = parseFloat((totalIn - totalOut).toFixed(8));
|
||||
return transaction;
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,8 @@ export namespace IEsploraApi {
|
||||
// Elements
|
||||
is_pegin?: boolean;
|
||||
issuance?: Issuance;
|
||||
// Custom
|
||||
lazy?: boolean;
|
||||
}
|
||||
|
||||
interface Issuance {
|
||||
|
@ -15,6 +15,7 @@ import BitcoinApi from './bitcoin/bitcoin-api';
|
||||
import { prepareBlock } from '../utils/blocks-utils';
|
||||
import BlocksRepository from '../repositories/BlocksRepository';
|
||||
import HashratesRepository from '../repositories/HashratesRepository';
|
||||
import indexer from '../indexer';
|
||||
|
||||
class Blocks {
|
||||
private blocks: BlockExtended[] = [];
|
||||
@ -23,9 +24,6 @@ class Blocks {
|
||||
private lastDifficultyAdjustmentTime = 0;
|
||||
private previousDifficultyRetarget = 0;
|
||||
private newBlockCallbacks: ((block: BlockExtended, txIds: string[], transactions: TransactionExtended[]) => void)[] = [];
|
||||
private blockIndexingStarted = false;
|
||||
public blockIndexingCompleted = false;
|
||||
public reindexFlag = false;
|
||||
|
||||
constructor() { }
|
||||
|
||||
@ -197,24 +195,15 @@ class Blocks {
|
||||
* [INDEXING] Index all blocks metadata for the mining dashboard
|
||||
*/
|
||||
public async $generateBlockDatabase() {
|
||||
if (this.blockIndexingStarted && !this.reindexFlag) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.reindexFlag = false;
|
||||
|
||||
const blockchainInfo = await bitcoinClient.getBlockchainInfo();
|
||||
if (blockchainInfo.blocks !== blockchainInfo.headers) { // Wait for node to sync
|
||||
return;
|
||||
}
|
||||
|
||||
this.blockIndexingStarted = true;
|
||||
this.blockIndexingCompleted = false;
|
||||
|
||||
try {
|
||||
let currentBlockHeight = blockchainInfo.blocks;
|
||||
|
||||
let indexingBlockAmount = config.MEMPOOL.INDEXING_BLOCKS_AMOUNT;
|
||||
let indexingBlockAmount = Math.min(config.MEMPOOL.INDEXING_BLOCKS_AMOUNT, blockchainInfo.blocks);
|
||||
if (indexingBlockAmount <= -1) {
|
||||
indexingBlockAmount = currentBlockHeight + 1;
|
||||
}
|
||||
@ -275,14 +264,14 @@ class Blocks {
|
||||
loadingIndicators.setProgress('block-indexing', 100);
|
||||
} catch (e) {
|
||||
logger.err('Block indexing failed. Trying again later. Reason: ' + (e instanceof Error ? e.message : e));
|
||||
this.blockIndexingStarted = false;
|
||||
loadingIndicators.setProgress('block-indexing', 100);
|
||||
return;
|
||||
}
|
||||
|
||||
const chainValid = await BlocksRepository.$validateChain();
|
||||
this.reindexFlag = !chainValid;
|
||||
this.blockIndexingCompleted = chainValid;
|
||||
if (!chainValid) {
|
||||
indexer.reindex();
|
||||
}
|
||||
}
|
||||
|
||||
public async $updateBlocks() {
|
||||
@ -299,6 +288,8 @@ class Blocks {
|
||||
logger.info(`${blockHeightTip - this.currentBlockHeight} blocks since tip. Fast forwarding to the ${config.MEMPOOL.INITIAL_BLOCKS_AMOUNT} recent blocks`);
|
||||
this.currentBlockHeight = blockHeightTip - config.MEMPOOL.INITIAL_BLOCKS_AMOUNT;
|
||||
fastForwarded = true;
|
||||
logger.info(`Re-indexing skipped blocks and corresponding hashrates data`);
|
||||
indexer.reindex(); // Make sure to index the skipped blocks #1619
|
||||
}
|
||||
|
||||
if (!this.lastDifficultyAdjustmentTime) {
|
||||
@ -426,10 +417,7 @@ class Blocks {
|
||||
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.
|
||||
// I'll need to fix it before we refactor the block(s) related pages
|
||||
public async $getBlocks(fromHeight?: number, limit: number = 15): Promise<BlockExtended[]> {
|
||||
try {
|
||||
let currentHeight = fromHeight !== undefined ? fromHeight : this.getCurrentBlockHeight();
|
||||
const returnBlocks: BlockExtended[] = [];
|
||||
|
@ -4,14 +4,11 @@ import PoolsRepository from '../repositories/PoolsRepository';
|
||||
import HashratesRepository from '../repositories/HashratesRepository';
|
||||
import bitcoinClient from './bitcoin/bitcoin-client';
|
||||
import logger from '../logger';
|
||||
import blocks from './blocks';
|
||||
import { Common } from './common';
|
||||
import loadingIndicators from './loading-indicators';
|
||||
import { escape } from 'mysql2';
|
||||
|
||||
class Mining {
|
||||
hashrateIndexingStarted = false;
|
||||
weeklyHashrateIndexingStarted = false;
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
@ -110,7 +107,7 @@ class Mining {
|
||||
public async $getPoolStat(slug: string): Promise<object> {
|
||||
const pool = await PoolsRepository.$getPool(slug);
|
||||
if (!pool) {
|
||||
throw new Error(`This mining pool does not exist`);
|
||||
throw new Error('This mining pool does not exist ' + escape(slug));
|
||||
}
|
||||
|
||||
const blockCount: number = await BlocksRepository.$blockCount(pool.id);
|
||||
@ -152,14 +149,9 @@ class Mining {
|
||||
* [INDEXING] Generate weekly mining pool hashrate history
|
||||
*/
|
||||
public async $generatePoolHashrateHistory(): Promise<void> {
|
||||
if (!blocks.blockIndexingCompleted || this.hashrateIndexingStarted || this.weeklyHashrateIndexingStarted) {
|
||||
return;
|
||||
}
|
||||
|
||||
const now = new Date();
|
||||
|
||||
try {
|
||||
this.weeklyHashrateIndexingStarted = true;
|
||||
const lastestRunDate = await HashratesRepository.$getLatestRun('last_weekly_hashrates_indexing');
|
||||
|
||||
// Run only if:
|
||||
@ -167,11 +159,9 @@ class Mining {
|
||||
// * we started a new week (around Monday midnight)
|
||||
const runIndexing = lastestRunDate === 0 || now.getUTCDay() === 1 && lastestRunDate !== now.getUTCDate();
|
||||
if (!runIndexing) {
|
||||
this.weeklyHashrateIndexingStarted = false;
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
this.weeklyHashrateIndexingStarted = false;
|
||||
throw e;
|
||||
}
|
||||
|
||||
@ -191,6 +181,7 @@ class Mining {
|
||||
const startedAt = new Date().getTime() / 1000;
|
||||
let timer = new Date().getTime() / 1000;
|
||||
|
||||
logger.debug(`Indexing weekly mining pool hashrate`);
|
||||
loadingIndicators.setProgress('weekly-hashrate-indexing', 0);
|
||||
|
||||
while (toTimestamp > genesisTimestamp) {
|
||||
@ -255,7 +246,6 @@ class Mining {
|
||||
++indexedThisRun;
|
||||
++totalIndexed;
|
||||
}
|
||||
this.weeklyHashrateIndexingStarted = false;
|
||||
await HashratesRepository.$setLatestRun('last_weekly_hashrates_indexing', new Date().getUTCDate());
|
||||
if (newlyIndexed > 0) {
|
||||
logger.info(`Indexed ${newlyIndexed} pools weekly hashrate`);
|
||||
@ -263,7 +253,6 @@ class Mining {
|
||||
loadingIndicators.setProgress('weekly-hashrate-indexing', 100);
|
||||
} catch (e) {
|
||||
loadingIndicators.setProgress('weekly-hashrate-indexing', 100);
|
||||
this.weeklyHashrateIndexingStarted = false;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
@ -272,22 +261,14 @@ class Mining {
|
||||
* [INDEXING] Generate daily hashrate data
|
||||
*/
|
||||
public async $generateNetworkHashrateHistory(): Promise<void> {
|
||||
if (!blocks.blockIndexingCompleted || this.hashrateIndexingStarted) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.hashrateIndexingStarted = true;
|
||||
|
||||
// We only run this once a day around midnight
|
||||
const latestRunDate = await HashratesRepository.$getLatestRun('last_hashrates_indexing');
|
||||
const now = new Date().getUTCDate();
|
||||
if (now === latestRunDate) {
|
||||
this.hashrateIndexingStarted = false;
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
this.hashrateIndexingStarted = false;
|
||||
throw e;
|
||||
}
|
||||
|
||||
@ -305,6 +286,7 @@ class Mining {
|
||||
const startedAt = new Date().getTime() / 1000;
|
||||
let timer = new Date().getTime() / 1000;
|
||||
|
||||
logger.debug(`Indexing daily network hashrate`);
|
||||
loadingIndicators.setProgress('daily-hashrate-indexing', 0);
|
||||
|
||||
while (toTimestamp > genesisTimestamp) {
|
||||
@ -376,14 +358,12 @@ class Mining {
|
||||
await HashratesRepository.$saveHashrates(hashrates);
|
||||
|
||||
await HashratesRepository.$setLatestRun('last_hashrates_indexing', new Date().getUTCDate());
|
||||
this.hashrateIndexingStarted = false;
|
||||
if (newlyIndexed > 0) {
|
||||
logger.info(`Indexed ${newlyIndexed} day of network hashrate`);
|
||||
}
|
||||
loadingIndicators.setProgress('daily-hashrate-indexing', 100);
|
||||
} catch (e) {
|
||||
loadingIndicators.setProgress('daily-hashrate-indexing', 100);
|
||||
this.hashrateIndexingStarted = false;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
@ -21,8 +21,8 @@ class TransactionUtils {
|
||||
};
|
||||
}
|
||||
|
||||
public async $getTransactionExtended(txId: string, addPrevouts = false): Promise<TransactionExtended> {
|
||||
const transaction: IEsploraApi.Transaction = await bitcoinApi.$getRawTransaction(txId, false, addPrevouts);
|
||||
public async $getTransactionExtended(txId: string, addPrevouts = false, lazyPrevouts = false): Promise<TransactionExtended> {
|
||||
const transaction: IEsploraApi.Transaction = await bitcoinApi.$getRawTransaction(txId, false, addPrevouts, lazyPrevouts);
|
||||
return this.extendTransaction(transaction);
|
||||
}
|
||||
|
||||
|
@ -27,8 +27,8 @@ import icons from './api/liquid/icons';
|
||||
import { Common } from './api/common';
|
||||
import mining from './api/mining';
|
||||
import HashratesRepository from './repositories/HashratesRepository';
|
||||
import BlocksRepository from './repositories/BlocksRepository';
|
||||
import poolsUpdater from './tasks/pools-updater';
|
||||
import indexer from './indexer';
|
||||
|
||||
class Server {
|
||||
private wss: WebSocket.Server | undefined;
|
||||
@ -99,7 +99,7 @@ class Server {
|
||||
}
|
||||
await databaseMigration.$initializeOrMigrateDatabase();
|
||||
if (Common.indexingEnabled()) {
|
||||
await this.$resetHashratesIndexingState();
|
||||
await indexer.$resetHashratesIndexingState();
|
||||
}
|
||||
} catch (e) {
|
||||
throw new Error(e instanceof Error ? e.message : 'Error');
|
||||
@ -154,7 +154,7 @@ class Server {
|
||||
await poolsUpdater.updatePoolsJson();
|
||||
await blocks.$updateBlocks();
|
||||
await memPool.$updateMempool();
|
||||
this.$runIndexingWhenReady();
|
||||
indexer.$run();
|
||||
|
||||
setTimeout(this.runMainUpdateLoop.bind(this), config.MEMPOOL.POLL_RATE_MS);
|
||||
this.currentBackendRetryInterval = 5;
|
||||
@ -173,29 +173,6 @@ class Server {
|
||||
}
|
||||
}
|
||||
|
||||
async $resetHashratesIndexingState() {
|
||||
try {
|
||||
await HashratesRepository.$setLatestRun('last_hashrates_indexing', 0);
|
||||
await HashratesRepository.$setLatestRun('last_weekly_hashrates_indexing', 0);
|
||||
} catch (e) {
|
||||
logger.err(`Cannot reset hashrate indexing timestamps. Reason: ` + (e instanceof Error ? e.message : e));
|
||||
}
|
||||
}
|
||||
|
||||
async $runIndexingWhenReady() {
|
||||
if (!Common.indexingEnabled() || mempool.hasPriority()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await blocks.$generateBlockDatabase();
|
||||
await mining.$generateNetworkHashrateHistory();
|
||||
await mining.$generatePoolHashrateHistory();
|
||||
} catch (e) {
|
||||
logger.err(`Indexing failed, trying again later. Reason: ` + (e instanceof Error ? e.message : e));
|
||||
}
|
||||
}
|
||||
|
||||
setUpWebsocketHandling() {
|
||||
if (this.wss) {
|
||||
websocketHandler.setWebsocketServer(this.wss);
|
||||
@ -337,8 +314,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', routes.getBlocks.bind(routes))
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'blocks/:height', routes.getBlocks.bind(routes))
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash', routes.getBlock);
|
||||
|
||||
if (config.MEMPOOL.BACKEND !== 'esplora') {
|
||||
@ -352,8 +329,6 @@ class Server {
|
||||
.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/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 + 'block/:hash/txs', routes.getBlockTransactions)
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash/txs/:index', routes.getBlockTransactions)
|
||||
|
52
backend/src/indexer.ts
Normal file
52
backend/src/indexer.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import { Common } from './api/common';
|
||||
import blocks from './api/blocks';
|
||||
import mempool from './api/mempool';
|
||||
import mining from './api/mining';
|
||||
import logger from './logger';
|
||||
import HashratesRepository from './repositories/HashratesRepository';
|
||||
|
||||
class Indexer {
|
||||
runIndexer = true;
|
||||
indexerRunning = false;
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
public reindex() {
|
||||
this.runIndexer = true;
|
||||
}
|
||||
|
||||
public async $run() {
|
||||
if (!Common.indexingEnabled() || this.runIndexer === false ||
|
||||
this.indexerRunning === true || mempool.hasPriority()
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.runIndexer = false;
|
||||
this.indexerRunning = true;
|
||||
|
||||
try {
|
||||
await blocks.$generateBlockDatabase();
|
||||
await this.$resetHashratesIndexingState();
|
||||
await mining.$generateNetworkHashrateHistory();
|
||||
await mining.$generatePoolHashrateHistory();
|
||||
} catch (e) {
|
||||
this.reindex();
|
||||
logger.err(`Indexer failed, trying again later. Reason: ` + (e instanceof Error ? e.message : e));
|
||||
}
|
||||
|
||||
this.indexerRunning = false;
|
||||
}
|
||||
|
||||
async $resetHashratesIndexingState() {
|
||||
try {
|
||||
await HashratesRepository.$setLatestRun('last_hashrates_indexing', 0);
|
||||
await HashratesRepository.$setLatestRun('last_weekly_hashrates_indexing', 0);
|
||||
} catch (e) {
|
||||
logger.err(`Cannot reset hashrate indexing timestamps. Reason: ` + (e instanceof Error ? e.message : e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new Indexer();
|
@ -81,7 +81,7 @@ export interface TransactionStripped {
|
||||
|
||||
export interface BlockExtension {
|
||||
totalFees?: number;
|
||||
medianFee?: number; // Actually the median fee rate that we compute ourself
|
||||
medianFee?: number;
|
||||
feeRange?: number[];
|
||||
reward?: number;
|
||||
coinbaseTx?: TransactionMinerInfo;
|
||||
|
@ -5,6 +5,7 @@ import { Common } from '../api/common';
|
||||
import { prepareBlock } from '../utils/blocks-utils';
|
||||
import PoolsRepository from './PoolsRepository';
|
||||
import HashratesRepository from './HashratesRepository';
|
||||
import { escape } from 'mysql2';
|
||||
|
||||
class BlocksRepository {
|
||||
/**
|
||||
@ -235,7 +236,7 @@ class BlocksRepository {
|
||||
public async $getBlocksByPool(slug: string, startHeight?: number): Promise<object[]> {
|
||||
const pool = await PoolsRepository.$getPool(slug);
|
||||
if (!pool) {
|
||||
throw new Error(`This mining pool does not exist`);
|
||||
throw new Error('This mining pool does not exist ' + escape(slug));
|
||||
}
|
||||
|
||||
const params: any[] = [];
|
||||
@ -474,9 +475,9 @@ class BlocksRepository {
|
||||
public async $getHistoricalBlockFees(div: number, interval: string | null): Promise<any> {
|
||||
try {
|
||||
let query = `SELECT
|
||||
CAST(AVG(height) as INT) as avg_height,
|
||||
CAST(AVG(height) as INT) as avgHeight,
|
||||
CAST(AVG(UNIX_TIMESTAMP(blockTimestamp)) as INT) as timestamp,
|
||||
CAST(AVG(fees) as INT) as avg_fees
|
||||
CAST(AVG(fees) as INT) as avgFees
|
||||
FROM blocks`;
|
||||
|
||||
if (interval !== null) {
|
||||
@ -499,9 +500,9 @@ class BlocksRepository {
|
||||
public async $getHistoricalBlockRewards(div: number, interval: string | null): Promise<any> {
|
||||
try {
|
||||
let query = `SELECT
|
||||
CAST(AVG(height) as INT) as avg_height,
|
||||
CAST(AVG(height) as INT) as avgHeight,
|
||||
CAST(AVG(UNIX_TIMESTAMP(blockTimestamp)) as INT) as timestamp,
|
||||
CAST(AVG(reward) as INT) as avg_rewards
|
||||
CAST(AVG(reward) as INT) as avgRewards
|
||||
FROM blocks`;
|
||||
|
||||
if (interval !== null) {
|
||||
@ -524,15 +525,15 @@ class BlocksRepository {
|
||||
public async $getHistoricalBlockFeeRates(div: number, interval: string | null): Promise<any> {
|
||||
try {
|
||||
let query = `SELECT
|
||||
CAST(AVG(height) as INT) as avg_height,
|
||||
CAST(AVG(height) as INT) as avgHeight,
|
||||
CAST(AVG(UNIX_TIMESTAMP(blockTimestamp)) as INT) as timestamp,
|
||||
CAST(AVG(JSON_EXTRACT(fee_span, '$[0]')) as INT) as avg_fee_0,
|
||||
CAST(AVG(JSON_EXTRACT(fee_span, '$[1]')) as INT) as avg_fee_10,
|
||||
CAST(AVG(JSON_EXTRACT(fee_span, '$[2]')) as INT) as avg_fee_25,
|
||||
CAST(AVG(JSON_EXTRACT(fee_span, '$[3]')) as INT) as avg_fee_50,
|
||||
CAST(AVG(JSON_EXTRACT(fee_span, '$[4]')) as INT) as avg_fee_75,
|
||||
CAST(AVG(JSON_EXTRACT(fee_span, '$[5]')) as INT) as avg_fee_90,
|
||||
CAST(AVG(JSON_EXTRACT(fee_span, '$[6]')) as INT) as avg_fee_100
|
||||
CAST(AVG(JSON_EXTRACT(fee_span, '$[0]')) as INT) as avgFee_0,
|
||||
CAST(AVG(JSON_EXTRACT(fee_span, '$[1]')) as INT) as avgFee_10,
|
||||
CAST(AVG(JSON_EXTRACT(fee_span, '$[2]')) as INT) as avgFee_25,
|
||||
CAST(AVG(JSON_EXTRACT(fee_span, '$[3]')) as INT) as avgFee_50,
|
||||
CAST(AVG(JSON_EXTRACT(fee_span, '$[4]')) as INT) as avgFee_75,
|
||||
CAST(AVG(JSON_EXTRACT(fee_span, '$[5]')) as INT) as avgFee_90,
|
||||
CAST(AVG(JSON_EXTRACT(fee_span, '$[6]')) as INT) as avgFee_100
|
||||
FROM blocks`;
|
||||
|
||||
if (interval !== null) {
|
||||
@ -555,9 +556,9 @@ class BlocksRepository {
|
||||
public async $getHistoricalBlockSizes(div: number, interval: string | null): Promise<any> {
|
||||
try {
|
||||
let query = `SELECT
|
||||
CAST(AVG(height) as INT) as avg_height,
|
||||
CAST(AVG(height) as INT) as avgHeight,
|
||||
CAST(AVG(UNIX_TIMESTAMP(blockTimestamp)) as INT) as timestamp,
|
||||
CAST(AVG(size) as INT) as avg_size
|
||||
CAST(AVG(size) as INT) as avgSize
|
||||
FROM blocks`;
|
||||
|
||||
if (interval !== null) {
|
||||
@ -580,9 +581,9 @@ class BlocksRepository {
|
||||
public async $getHistoricalBlockWeights(div: number, interval: string | null): Promise<any> {
|
||||
try {
|
||||
let query = `SELECT
|
||||
CAST(AVG(height) as INT) as avg_height,
|
||||
CAST(AVG(height) as INT) as avgHeight,
|
||||
CAST(AVG(UNIX_TIMESTAMP(blockTimestamp)) as INT) as timestamp,
|
||||
CAST(AVG(weight) as INT) as avg_weight
|
||||
CAST(AVG(weight) as INT) as avgWeight
|
||||
FROM blocks`;
|
||||
|
||||
if (interval !== null) {
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { escape } from 'mysql2';
|
||||
import { Common } from '../api/common';
|
||||
import DB from '../database';
|
||||
import logger from '../logger';
|
||||
@ -105,7 +106,7 @@ class HashratesRepository {
|
||||
public async $getPoolWeeklyHashrate(slug: string): Promise<any[]> {
|
||||
const pool = await PoolsRepository.$getPool(slug);
|
||||
if (!pool) {
|
||||
throw new Error(`This mining pool does not exist`);
|
||||
throw new Error('This mining pool does not exist ' + escape(slug));
|
||||
}
|
||||
|
||||
// Find hashrate boundaries
|
||||
|
@ -78,7 +78,6 @@ class PoolsRepository {
|
||||
const [rows]: any[] = await DB.query(query, [slug]);
|
||||
|
||||
if (rows.length < 1) {
|
||||
logger.debug(`This slug does not match any known pool`);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -720,20 +720,22 @@ class Routes {
|
||||
}
|
||||
}
|
||||
|
||||
public async getBlocksExtras(req: Request, res: Response) {
|
||||
public async getBlocks(req: Request, res: Response) {
|
||||
try {
|
||||
const height = req.params.height === undefined ? undefined : parseInt(req.params.height, 10);
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||
res.json(await blocks.$getBlocksExtras(height, 15));
|
||||
if (['mainnet', 'testnet', 'signet', 'regtest'].includes(config.MEMPOOL.NETWORK)) { // Bitcoin
|
||||
const height = req.params.height === undefined ? undefined : parseInt(req.params.height, 10);
|
||||
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) {
|
||||
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 {
|
||||
loadingIndicators.setProgress('blocks', 0);
|
||||
|
||||
const returnBlocks: IEsploraApi.Block[] = [];
|
||||
const fromHeight = parseInt(req.params.height, 10) || blocks.getCurrentBlockHeight();
|
||||
|
||||
@ -757,16 +759,15 @@ class Routes {
|
||||
returnBlocks.push(block);
|
||||
nextHash = block.previousblockhash;
|
||||
}
|
||||
loadingIndicators.setProgress('blocks', i / 10 * 100);
|
||||
}
|
||||
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||
res.json(returnBlocks);
|
||||
} catch (e) {
|
||||
loadingIndicators.setProgress('blocks', 100);
|
||||
res.status(500).send(e instanceof Error ? e.message : e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public async getBlockTransactions(req: Request, res: Response) {
|
||||
try {
|
||||
loadingIndicators.setProgress('blocktxs-' + req.params.hash, 0);
|
||||
@ -778,9 +779,9 @@ class Routes {
|
||||
const endIndex = Math.min(startingIndex + 10, txIds.length);
|
||||
for (let i = startingIndex; i < endIndex; i++) {
|
||||
try {
|
||||
const transaction = await transactionUtils.$getTransactionExtended(txIds[i], true);
|
||||
const transaction = await transactionUtils.$getTransactionExtended(txIds[i], true, true);
|
||||
transactions.push(transaction);
|
||||
loadingIndicators.setProgress('blocktxs-' + req.params.hash, (i + 1) / endIndex * 100);
|
||||
loadingIndicators.setProgress('blocktxs-' + req.params.hash, (i - startingIndex + 1) / (endIndex - startingIndex) * 100);
|
||||
} catch (e) {
|
||||
logger.debug('getBlockTransactions error: ' + (e instanceof Error ? e.message : e));
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ export function prepareBlock(block: any): BlockExtended {
|
||||
weight: block.weight,
|
||||
previousblockhash: block.previousblockhash,
|
||||
extras: {
|
||||
coinbaseRaw: block.coinbase_raw ?? block.extras.coinbaseRaw,
|
||||
coinbaseRaw: block.coinbase_raw ?? block.extras?.coinbaseRaw,
|
||||
medianFee: block.medianFee ?? block.median_fee ?? block.extras?.medianFee,
|
||||
feeRange: block.feeRange ?? block.fee_span,
|
||||
reward: block.reward ?? block?.extras?.reward,
|
||||
|
277
frontend/package-lock.json
generated
277
frontend/package-lock.json
generated
@ -3900,9 +3900,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@socket.io/component-emitter": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.0.0.tgz",
|
||||
"integrity": "sha512-2pTGuibAXJswAPJjaKisthqS/NOK5ypG4LYT6tEAV0S/mxW0zOIvYvGK0V8w8+SHxAm6vRMSjqSalFXeBAqs+Q==",
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
|
||||
"integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@tootallnate/once": {
|
||||
@ -4755,15 +4755,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/async": {
|
||||
"version": "1.5.2",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
|
||||
"integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=",
|
||||
"dev": true
|
||||
"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/async-each-series": {
|
||||
"version": "0.1.1",
|
||||
"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,
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
@ -4996,12 +4998,6 @@
|
||||
"@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": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||
@ -5254,13 +5250,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/browser-sync": {
|
||||
"version": "2.27.9",
|
||||
"resolved": "https://registry.npmjs.org/browser-sync/-/browser-sync-2.27.9.tgz",
|
||||
"integrity": "sha512-3zBtggcaZIeU9so4ja9yxk7/CZu9B3DOL6zkxFpzHCHsQmkGBPVXg61jItbeoa+WXgNLnr1sYES/2yQwyEZ2+w==",
|
||||
"version": "2.27.10",
|
||||
"resolved": "https://registry.npmjs.org/browser-sync/-/browser-sync-2.27.10.tgz",
|
||||
"integrity": "sha512-xKm+6KJmJu6RuMWWbFkKwOCSqQOxYe3nOrFkKI5Tr/ZzjPxyU3pFShKK3tWnazBo/3lYQzN7fzjixG8fwJh1Xw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"browser-sync-client": "^2.27.9",
|
||||
"browser-sync-ui": "^2.27.9",
|
||||
"browser-sync-client": "^2.27.10",
|
||||
"browser-sync-ui": "^2.27.10",
|
||||
"bs-recipes": "1.3.4",
|
||||
"bs-snippet-injector": "^2.0.1",
|
||||
"chokidar": "^3.5.1",
|
||||
@ -5277,7 +5273,7 @@
|
||||
"localtunnel": "^2.0.1",
|
||||
"micromatch": "^4.0.2",
|
||||
"opn": "5.3.0",
|
||||
"portscanner": "2.1.1",
|
||||
"portscanner": "2.2.0",
|
||||
"qs": "6.2.3",
|
||||
"raw-body": "^2.3.2",
|
||||
"resp-modifier": "6.0.2",
|
||||
@ -5298,15 +5294,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/browser-sync-client": {
|
||||
"version": "2.27.9",
|
||||
"resolved": "https://registry.npmjs.org/browser-sync-client/-/browser-sync-client-2.27.9.tgz",
|
||||
"integrity": "sha512-FHW8kydp7FXo6jnX3gXJCpHAHtWNLK0nx839nnK+boMfMI1n4KZd0+DmTxHBsHsF3OHud4V4jwoN8U5HExMIdQ==",
|
||||
"version": "2.27.10",
|
||||
"resolved": "https://registry.npmjs.org/browser-sync-client/-/browser-sync-client-2.27.10.tgz",
|
||||
"integrity": "sha512-KCFKA1YDj6cNul0VsA28apohtBsdk5Wv8T82ClOZPZMZWxPj4Ny5AUbrj9UlAb/k6pdxE5HABrWDhP9+cjt4HQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"etag": "1.8.1",
|
||||
"fresh": "0.5.2",
|
||||
"mitt": "^1.1.3",
|
||||
"rxjs": "^5.5.6"
|
||||
"rxjs": "^5.5.6",
|
||||
"typescript": "^4.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
@ -5333,10 +5330,23 @@
|
||||
"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": {
|
||||
"version": "2.27.9",
|
||||
"resolved": "https://registry.npmjs.org/browser-sync-ui/-/browser-sync-ui-2.27.9.tgz",
|
||||
"integrity": "sha512-rsduR2bRIwFvM8CX6iY/Nu5aWub0WB9zfSYg9Le/RV5N5DEyxJYey0VxdfWCnzDOoelassTDzYQo+r0iJno3qw==",
|
||||
"version": "2.27.10",
|
||||
"resolved": "https://registry.npmjs.org/browser-sync-ui/-/browser-sync-ui-2.27.10.tgz",
|
||||
"integrity": "sha512-elbJILq4Uo6OQv6gsvS3Y9vRAJlWu+h8j0JDkF0X/ua+3S6SVbbiWnZc8sNOFlG7yvVGIwBED3eaYQ0iBo1Dtw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"async-each-series": "0.1.1",
|
||||
@ -7891,20 +7901,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io-client": {
|
||||
"version": "6.1.1",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.1.1.tgz",
|
||||
"integrity": "sha512-V05mmDo4gjimYW+FGujoGmmmxRaDsrVr7AXA3ZIfa04MWM1jOfZfUwou0oNqhNwy/votUDvGDt4JA4QF4e0b4g==",
|
||||
"version": "6.2.2",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.2.2.tgz",
|
||||
"integrity": "sha512-8ZQmx0LQGRTYkHuogVZuGSpDqYZtCM/nv8zQ68VZ+JkOpazJ7ICdsSpaO6iXwvaU30oFg5QJOJWj8zWqhbKjkQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.0.0",
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.1",
|
||||
"engine.io-parser": "~5.0.0",
|
||||
"has-cors": "1.1.0",
|
||||
"parseqs": "0.0.6",
|
||||
"parseuri": "0.0.6",
|
||||
"engine.io-parser": "~5.0.3",
|
||||
"ws": "~8.2.3",
|
||||
"xmlhttprequest-ssl": "~2.0.0",
|
||||
"yeast": "0.1.2"
|
||||
"xmlhttprequest-ssl": "~2.0.0"
|
||||
}
|
||||
},
|
||||
"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",
|
||||
"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": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
@ -13181,18 +13181,6 @@
|
||||
"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": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||
@ -13366,14 +13354,6 @@
|
||||
"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": {
|
||||
"version": "3.2.7",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
|
||||
@ -13383,12 +13363,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/portscanner": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/portscanner/-/portscanner-2.1.1.tgz",
|
||||
"integrity": "sha1-6rtAnk3iSVD1oqUW01rnaTQ/u5Y=",
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/portscanner/-/portscanner-2.2.0.tgz",
|
||||
"integrity": "sha512-IFroCz/59Lqa2uBvzK3bKDbDDIEaAY8XJ1jFxcLWTqosrsc32//P4VuSB2vZXoHiHqOmx8B5L5hnKOxL/7FlPw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"async": "1.5.2",
|
||||
"async": "^2.6.0",
|
||||
"is-number-like": "^1.0.3"
|
||||
},
|
||||
"engines": {
|
||||
@ -15098,29 +15078,27 @@
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/socket.io-client": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.4.1.tgz",
|
||||
"integrity": "sha512-N5C/L5fLNha5Ojd7Yeb/puKcPWWcoB/A09fEjjNsg91EDVr5twk/OEyO6VT9dlLSUNY85NpW6KBhVMvaLKQ3vQ==",
|
||||
"version": "4.5.1",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.5.1.tgz",
|
||||
"integrity": "sha512-e6nLVgiRYatS+AHXnOnGi4ocOpubvOUCGhyWw8v+/FxW8saHkinG6Dfhi9TU0Kt/8mwJIAASxvw6eujQmjdZVA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.0.0",
|
||||
"backo2": "~1.0.2",
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.2",
|
||||
"engine.io-client": "~6.1.1",
|
||||
"parseuri": "0.0.6",
|
||||
"socket.io-parser": "~4.1.1"
|
||||
"engine.io-client": "~6.2.1",
|
||||
"socket.io-parser": "~4.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-client/node_modules/socket.io-parser": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.1.2.tgz",
|
||||
"integrity": "sha512-j3kk71QLJuyQ/hh5F/L2t1goqzdTL0gvDzuhTuNSwihfuFUrcSji0qFZmJJPtG6Rmug153eOPsUizeirf1IIog==",
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.0.tgz",
|
||||
"integrity": "sha512-tLfmEwcEwnlQTxFB7jibL/q2+q8dlVQzj4JdRLJ/W/G1+Fu9VSxCx1Lo+n1HvXxKnM//dUuD0xgiA7tQf57Vng==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.0.0",
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.1"
|
||||
},
|
||||
"engines": {
|
||||
@ -17264,12 +17242,6 @@
|
||||
"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": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||
@ -20020,9 +19992,9 @@
|
||||
"devOptional": true
|
||||
},
|
||||
"@socket.io/component-emitter": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.0.0.tgz",
|
||||
"integrity": "sha512-2pTGuibAXJswAPJjaKisthqS/NOK5ypG4LYT6tEAV0S/mxW0zOIvYvGK0V8w8+SHxAm6vRMSjqSalFXeBAqs+Q==",
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
|
||||
"integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==",
|
||||
"dev": true
|
||||
},
|
||||
"@tootallnate/once": {
|
||||
@ -20794,15 +20766,17 @@
|
||||
"optional": true
|
||||
},
|
||||
"async": {
|
||||
"version": "1.5.2",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
|
||||
"integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=",
|
||||
"dev": true
|
||||
"version": "2.6.4",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
|
||||
"integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
|
||||
"requires": {
|
||||
"lodash": "^4.17.14"
|
||||
}
|
||||
},
|
||||
"async-each-series": {
|
||||
"version": "0.1.1",
|
||||
"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
|
||||
},
|
||||
"asynckit": {
|
||||
@ -20976,12 +20950,6 @@
|
||||
"@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": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||
@ -21210,13 +21178,13 @@
|
||||
}
|
||||
},
|
||||
"browser-sync": {
|
||||
"version": "2.27.9",
|
||||
"resolved": "https://registry.npmjs.org/browser-sync/-/browser-sync-2.27.9.tgz",
|
||||
"integrity": "sha512-3zBtggcaZIeU9so4ja9yxk7/CZu9B3DOL6zkxFpzHCHsQmkGBPVXg61jItbeoa+WXgNLnr1sYES/2yQwyEZ2+w==",
|
||||
"version": "2.27.10",
|
||||
"resolved": "https://registry.npmjs.org/browser-sync/-/browser-sync-2.27.10.tgz",
|
||||
"integrity": "sha512-xKm+6KJmJu6RuMWWbFkKwOCSqQOxYe3nOrFkKI5Tr/ZzjPxyU3pFShKK3tWnazBo/3lYQzN7fzjixG8fwJh1Xw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"browser-sync-client": "^2.27.9",
|
||||
"browser-sync-ui": "^2.27.9",
|
||||
"browser-sync-client": "^2.27.10",
|
||||
"browser-sync-ui": "^2.27.10",
|
||||
"bs-recipes": "1.3.4",
|
||||
"bs-snippet-injector": "^2.0.1",
|
||||
"chokidar": "^3.5.1",
|
||||
@ -21233,7 +21201,7 @@
|
||||
"localtunnel": "^2.0.1",
|
||||
"micromatch": "^4.0.2",
|
||||
"opn": "5.3.0",
|
||||
"portscanner": "2.1.1",
|
||||
"portscanner": "2.2.0",
|
||||
"qs": "6.2.3",
|
||||
"raw-body": "^2.3.2",
|
||||
"resp-modifier": "6.0.2",
|
||||
@ -21343,15 +21311,16 @@
|
||||
}
|
||||
},
|
||||
"browser-sync-client": {
|
||||
"version": "2.27.9",
|
||||
"resolved": "https://registry.npmjs.org/browser-sync-client/-/browser-sync-client-2.27.9.tgz",
|
||||
"integrity": "sha512-FHW8kydp7FXo6jnX3gXJCpHAHtWNLK0nx839nnK+boMfMI1n4KZd0+DmTxHBsHsF3OHud4V4jwoN8U5HExMIdQ==",
|
||||
"version": "2.27.10",
|
||||
"resolved": "https://registry.npmjs.org/browser-sync-client/-/browser-sync-client-2.27.10.tgz",
|
||||
"integrity": "sha512-KCFKA1YDj6cNul0VsA28apohtBsdk5Wv8T82ClOZPZMZWxPj4Ny5AUbrj9UlAb/k6pdxE5HABrWDhP9+cjt4HQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"etag": "1.8.1",
|
||||
"fresh": "0.5.2",
|
||||
"mitt": "^1.1.3",
|
||||
"rxjs": "^5.5.6"
|
||||
"rxjs": "^5.5.6",
|
||||
"typescript": "^4.6.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"rxjs": {
|
||||
@ -21368,13 +21337,19 @@
|
||||
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz",
|
||||
"integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=",
|
||||
"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": {
|
||||
"version": "2.27.9",
|
||||
"resolved": "https://registry.npmjs.org/browser-sync-ui/-/browser-sync-ui-2.27.9.tgz",
|
||||
"integrity": "sha512-rsduR2bRIwFvM8CX6iY/Nu5aWub0WB9zfSYg9Le/RV5N5DEyxJYey0VxdfWCnzDOoelassTDzYQo+r0iJno3qw==",
|
||||
"version": "2.27.10",
|
||||
"resolved": "https://registry.npmjs.org/browser-sync-ui/-/browser-sync-ui-2.27.10.tgz",
|
||||
"integrity": "sha512-elbJILq4Uo6OQv6gsvS3Y9vRAJlWu+h8j0JDkF0X/ua+3S6SVbbiWnZc8sNOFlG7yvVGIwBED3eaYQ0iBo1Dtw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"async-each-series": "0.1.1",
|
||||
@ -23395,20 +23370,16 @@
|
||||
}
|
||||
},
|
||||
"engine.io-client": {
|
||||
"version": "6.1.1",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.1.1.tgz",
|
||||
"integrity": "sha512-V05mmDo4gjimYW+FGujoGmmmxRaDsrVr7AXA3ZIfa04MWM1jOfZfUwou0oNqhNwy/votUDvGDt4JA4QF4e0b4g==",
|
||||
"version": "6.2.2",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.2.2.tgz",
|
||||
"integrity": "sha512-8ZQmx0LQGRTYkHuogVZuGSpDqYZtCM/nv8zQ68VZ+JkOpazJ7ICdsSpaO6iXwvaU30oFg5QJOJWj8zWqhbKjkQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@socket.io/component-emitter": "~3.0.0",
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.1",
|
||||
"engine.io-parser": "~5.0.0",
|
||||
"has-cors": "1.1.0",
|
||||
"parseqs": "0.0.6",
|
||||
"parseuri": "0.0.6",
|
||||
"engine.io-parser": "~5.0.3",
|
||||
"ws": "~8.2.3",
|
||||
"xmlhttprequest-ssl": "~2.0.0",
|
||||
"yeast": "0.1.2"
|
||||
"xmlhttprequest-ssl": "~2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ws": {
|
||||
@ -24622,12 +24593,6 @@
|
||||
"resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz",
|
||||
"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": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
@ -27435,18 +27400,6 @@
|
||||
"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": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||
@ -27576,14 +27529,6 @@
|
||||
"mkdirp": "^0.5.5"
|
||||
},
|
||||
"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": {
|
||||
"version": "3.2.7",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
|
||||
@ -27595,12 +27540,12 @@
|
||||
}
|
||||
},
|
||||
"portscanner": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/portscanner/-/portscanner-2.1.1.tgz",
|
||||
"integrity": "sha1-6rtAnk3iSVD1oqUW01rnaTQ/u5Y=",
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/portscanner/-/portscanner-2.2.0.tgz",
|
||||
"integrity": "sha512-IFroCz/59Lqa2uBvzK3bKDbDDIEaAY8XJ1jFxcLWTqosrsc32//P4VuSB2vZXoHiHqOmx8B5L5hnKOxL/7FlPw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"async": "1.5.2",
|
||||
"async": "^2.6.0",
|
||||
"is-number-like": "^1.0.3"
|
||||
}
|
||||
},
|
||||
@ -28879,26 +28824,24 @@
|
||||
"devOptional": true
|
||||
},
|
||||
"socket.io-client": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.4.1.tgz",
|
||||
"integrity": "sha512-N5C/L5fLNha5Ojd7Yeb/puKcPWWcoB/A09fEjjNsg91EDVr5twk/OEyO6VT9dlLSUNY85NpW6KBhVMvaLKQ3vQ==",
|
||||
"version": "4.5.1",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.5.1.tgz",
|
||||
"integrity": "sha512-e6nLVgiRYatS+AHXnOnGi4ocOpubvOUCGhyWw8v+/FxW8saHkinG6Dfhi9TU0Kt/8mwJIAASxvw6eujQmjdZVA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@socket.io/component-emitter": "~3.0.0",
|
||||
"backo2": "~1.0.2",
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.2",
|
||||
"engine.io-client": "~6.1.1",
|
||||
"parseuri": "0.0.6",
|
||||
"socket.io-parser": "~4.1.1"
|
||||
"engine.io-client": "~6.2.1",
|
||||
"socket.io-parser": "~4.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"socket.io-parser": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.1.2.tgz",
|
||||
"integrity": "sha512-j3kk71QLJuyQ/hh5F/L2t1goqzdTL0gvDzuhTuNSwihfuFUrcSji0qFZmJJPtG6Rmug153eOPsUizeirf1IIog==",
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.0.tgz",
|
||||
"integrity": "sha512-tLfmEwcEwnlQTxFB7jibL/q2+q8dlVQzj4JdRLJ/W/G1+Fu9VSxCx1Lo+n1HvXxKnM//dUuD0xgiA7tQf57Vng==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@socket.io/component-emitter": "~3.0.0",
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.1"
|
||||
}
|
||||
}
|
||||
@ -30543,12 +30486,6 @@
|
||||
"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": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||
|
@ -7,7 +7,6 @@ import { AddressComponent } from './components/address/address.component';
|
||||
import { MasterPageComponent } from './components/master-page/master-page.component';
|
||||
import { AboutComponent } from './components/about/about.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 { PrivacyPolicyComponent } from './components/privacy-policy/privacy-policy.component';
|
||||
import { TrademarkPolicyComponent } from './components/trademark-policy/trademark-policy.component';
|
||||
@ -36,19 +35,20 @@ let routes: Routes = [
|
||||
component: MasterPageComponent,
|
||||
children: [
|
||||
{
|
||||
path: 'tx/push',
|
||||
component: PushTransactionComponent,
|
||||
path: 'mining/blocks',
|
||||
redirectTo: 'blocks',
|
||||
pathMatch: 'full'
|
||||
},
|
||||
{
|
||||
path: 'blocks',
|
||||
component: LatestBlocksComponent,
|
||||
path: 'tx/push',
|
||||
component: PushTransactionComponent,
|
||||
},
|
||||
{
|
||||
path: 'about',
|
||||
component: AboutComponent,
|
||||
},
|
||||
{
|
||||
path: 'mining/blocks',
|
||||
path: 'blocks',
|
||||
component: BlocksList,
|
||||
},
|
||||
{
|
||||
@ -115,6 +115,11 @@ let routes: Routes = [
|
||||
{
|
||||
path: 'signet',
|
||||
children: [
|
||||
{
|
||||
path: 'mining/blocks',
|
||||
redirectTo: 'blocks',
|
||||
pathMatch: 'full'
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
pathMatch: 'full',
|
||||
@ -128,16 +133,12 @@ let routes: Routes = [
|
||||
path: 'tx/push',
|
||||
component: PushTransactionComponent,
|
||||
},
|
||||
{
|
||||
path: 'blocks',
|
||||
component: LatestBlocksComponent,
|
||||
},
|
||||
{
|
||||
path: 'about',
|
||||
component: AboutComponent,
|
||||
},
|
||||
{
|
||||
path: 'mining/blocks',
|
||||
path: 'blocks',
|
||||
component: BlocksList,
|
||||
},
|
||||
{
|
||||
@ -211,19 +212,20 @@ let routes: Routes = [
|
||||
component: MasterPageComponent,
|
||||
children: [
|
||||
{
|
||||
path: 'tx/push',
|
||||
component: PushTransactionComponent,
|
||||
path: 'mining/blocks',
|
||||
redirectTo: 'blocks',
|
||||
pathMatch: 'full'
|
||||
},
|
||||
{
|
||||
path: 'blocks',
|
||||
component: LatestBlocksComponent,
|
||||
path: 'tx/push',
|
||||
component: PushTransactionComponent,
|
||||
},
|
||||
{
|
||||
path: 'about',
|
||||
component: AboutComponent,
|
||||
},
|
||||
{
|
||||
path: 'mining/blocks',
|
||||
path: 'blocks',
|
||||
component: BlocksList,
|
||||
},
|
||||
{
|
||||
@ -321,16 +323,12 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') {
|
||||
path: 'tx/push',
|
||||
component: PushTransactionComponent,
|
||||
},
|
||||
{
|
||||
path: 'blocks',
|
||||
component: LatestBlocksComponent,
|
||||
},
|
||||
{
|
||||
path: 'about',
|
||||
component: AboutComponent,
|
||||
},
|
||||
{
|
||||
path: 'mining/blocks',
|
||||
path: 'blocks',
|
||||
component: BlocksList,
|
||||
},
|
||||
{
|
||||
@ -429,16 +427,12 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') {
|
||||
path: 'tx/push',
|
||||
component: PushTransactionComponent,
|
||||
},
|
||||
{
|
||||
path: 'blocks',
|
||||
component: LatestBlocksComponent,
|
||||
},
|
||||
{
|
||||
path: 'about',
|
||||
component: AboutComponent,
|
||||
},
|
||||
{
|
||||
path: 'mining/blocks',
|
||||
path: 'blocks',
|
||||
component: BlocksList,
|
||||
},
|
||||
{
|
||||
|
@ -250,6 +250,10 @@
|
||||
<img class="image" src="/resources/profile/marina.svg" />
|
||||
<span>Marina</span>
|
||||
</a>
|
||||
<a href="https://github.com/bitcoin-wallet/bitcoin-wallet/" target="_blank" title="Bitcoin Wallet (Schildbach)">
|
||||
<img class="image" src="/resources/profile/schildbach.svg" />
|
||||
<span>Schildbach</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -89,13 +89,13 @@ export class BlockFeeRatesGraphComponent implements OnInit {
|
||||
};
|
||||
for (const rate of data.blockFeeRates) {
|
||||
const timestamp = rate.timestamp * 1000;
|
||||
seriesData['Min'].push([timestamp, rate.avg_fee_0, rate.avg_height]);
|
||||
seriesData['10th'].push([timestamp, rate.avg_fee_10, rate.avg_height]);
|
||||
seriesData['25th'].push([timestamp, rate.avg_fee_25, rate.avg_height]);
|
||||
seriesData['Median'].push([timestamp, rate.avg_fee_50, rate.avg_height]);
|
||||
seriesData['75th'].push([timestamp, rate.avg_fee_75, rate.avg_height]);
|
||||
seriesData['90th'].push([timestamp, rate.avg_fee_90, rate.avg_height]);
|
||||
seriesData['Max'].push([timestamp, rate.avg_fee_100, rate.avg_height]);
|
||||
seriesData['Min'].push([timestamp, rate.avgFee_0, rate.avgHeight]);
|
||||
seriesData['10th'].push([timestamp, rate.avgFee_10, rate.avgHeight]);
|
||||
seriesData['25th'].push([timestamp, rate.avgFee_25, rate.avgHeight]);
|
||||
seriesData['Median'].push([timestamp, rate.avgFee_50, rate.avgHeight]);
|
||||
seriesData['75th'].push([timestamp, rate.avgFee_75, rate.avgHeight]);
|
||||
seriesData['90th'].push([timestamp, rate.avgFee_90, rate.avgHeight]);
|
||||
seriesData['Max'].push([timestamp, rate.avgFee_100, rate.avgHeight]);
|
||||
}
|
||||
|
||||
// Prepare chart
|
||||
|
@ -71,7 +71,7 @@ export class BlockFeesGraphComponent implements OnInit {
|
||||
.pipe(
|
||||
tap((response) => {
|
||||
this.prepareChartOptions({
|
||||
blockFees: response.body.map(val => [val.timestamp * 1000, val.avg_fees / 100000000]),
|
||||
blockFees: response.body.map(val => [val.timestamp * 1000, val.avgFees / 100000000]),
|
||||
});
|
||||
this.isLoading = false;
|
||||
}),
|
||||
|
@ -69,7 +69,7 @@ export class BlockRewardsGraphComponent implements OnInit {
|
||||
.pipe(
|
||||
tap((response) => {
|
||||
this.prepareChartOptions({
|
||||
blockRewards: response.body.map(val => [val.timestamp * 1000, val.avg_rewards / 100000000]),
|
||||
blockRewards: response.body.map(val => [val.timestamp * 1000, val.avgRewards / 100000000]),
|
||||
});
|
||||
this.isLoading = false;
|
||||
}),
|
||||
|
@ -83,8 +83,8 @@ export class BlockSizesWeightsGraphComponent implements OnInit {
|
||||
tap((response) => {
|
||||
const data = response.body;
|
||||
this.prepareChartOptions({
|
||||
sizes: data.sizes.map(val => [val.timestamp * 1000, val.avg_size / 1000000, val.avg_height]),
|
||||
weights: data.weights.map(val => [val.timestamp * 1000, val.avg_weight / 1000000, val.avg_height]),
|
||||
sizes: data.sizes.map(val => [val.timestamp * 1000, val.avgSize / 1000000, val.avgHeight]),
|
||||
weights: data.weights.map(val => [val.timestamp * 1000, val.avgWeight / 1000000, val.avgHeight]),
|
||||
});
|
||||
this.isLoading = false;
|
||||
}),
|
||||
|
@ -197,7 +197,18 @@
|
||||
|
||||
<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">
|
||||
|
||||
<ng-container *ngIf="(txsLoadingStatus$ | async) as txsLoadingStatus; else headerLoader">
|
||||
@ -271,9 +282,9 @@
|
||||
|
||||
<ng-template [ngIf]="error">
|
||||
<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>
|
||||
<i>{{ error.error }}</i>
|
||||
<i>{{ error.code }}: {{ error.error }}</i>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
|
@ -38,6 +38,7 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||
showDetails = false;
|
||||
showPreviousBlocklink = true;
|
||||
showNextBlocklink = true;
|
||||
transactionsError: any = null;
|
||||
|
||||
subscription: Subscription;
|
||||
keyNavigationSubscription: Subscription;
|
||||
@ -152,12 +153,13 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||
this.stateService.markBlock$.next({ blockHeight: this.blockHeight });
|
||||
this.isLoadingTransactions = true;
|
||||
this.transactions = null;
|
||||
this.transactionsError = null;
|
||||
}),
|
||||
debounceTime(300),
|
||||
switchMap((block) => this.electrsApiService.getBlockTransactions$(block.id)
|
||||
.pipe(
|
||||
catchError((err) => {
|
||||
console.log(err);
|
||||
this.transactionsError = err;
|
||||
return of([]);
|
||||
}))
|
||||
),
|
||||
@ -218,9 +220,16 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||
const start = (page - 1) * this.itemsPerPage;
|
||||
this.isLoadingTransactions = true;
|
||||
this.transactions = null;
|
||||
this.transactionsError = null;
|
||||
target.scrollIntoView(); // works for chrome
|
||||
|
||||
this.electrsApiService.getBlockTransactions$(this.block.id, start)
|
||||
.pipe(
|
||||
catchError((err) => {
|
||||
this.transactionsError = err;
|
||||
return of([]);
|
||||
})
|
||||
)
|
||||
.subscribe((transactions) => {
|
||||
this.transactions = transactions;
|
||||
this.isLoadingTransactions = false;
|
||||
|
@ -1,6 +1,6 @@
|
||||
<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>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
@ -8,22 +8,22 @@
|
||||
<div style="min-height: 295px">
|
||||
<table class="table table-borderless">
|
||||
<thead>
|
||||
<th class="height text-left" [class]="widget ? 'widget' : ''" i18n="latest-blocks.height">Height</th>
|
||||
<th class="pool text-left" [class]="widget ? 'widget' : ''" i18n="mining.pool-name">Pool</th>
|
||||
<th class="timestamp" i18n="latest-blocks.timestamp" *ngIf="!widget">Timestamp</th>
|
||||
<th class="mined" i18n="latest-blocks.mined" *ngIf="!widget">Mined</th>
|
||||
<th class="reward text-right" i18n="latest-blocks.reward" [class]="widget ? 'widget' : ''">Reward</th>
|
||||
<th class="fees text-right" i18n="latest-blocks.fees" *ngIf="!widget">Fees</th>
|
||||
<th class="txs text-right" i18n="dashboard.txs" [class]="widget ? 'widget' : ''">TXs</th>
|
||||
<th class="size" i18n="latest-blocks.size" *ngIf="!widget">Size</th>
|
||||
<th class="height text-left" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}" i18n="latest-blocks.height">Height</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" [class]="indexingAvailable ? '' : 'legacy'">Timestamp</th>
|
||||
<th class="mined" i18n="latest-blocks.mined" *ngIf="!widget" [class]="indexingAvailable ? '' : 'legacy'">Mined</th>
|
||||
<th *ngIf="indexingAvailable" class="reward text-right" i18n="latest-blocks.reward" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}">Reward</th>
|
||||
<th *ngIf="indexingAvailable && !widget" class="fees text-right" i18n="latest-blocks.fees" [class]="indexingAvailable ? '' : 'legacy'">Fees</th>
|
||||
<th *ngIf="indexingAvailable" class="txs text-right" i18n="dashboard.txs" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}">TXs</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>
|
||||
<tbody *ngIf="blocks$ | async as blocks; else skeleton" [style]="isLoading ? 'opacity: 0.75' : ''">
|
||||
<tr *ngFor="let block of blocks; let i= index; trackBy: trackByBlock">
|
||||
<td class="text-left" [class]="widget ? 'widget' : ''">
|
||||
<a [routerLink]="['/block' | relativeUrl, block.height]">{{ block.height
|
||||
}}</a>
|
||||
<a [routerLink]="['/block' | relativeUrl, block.height]">{{ block.height }}</a>
|
||||
</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">
|
||||
<a class="clear-link" [routerLink]="['/mining/pool' | relativeUrl, block.extras.pool.slug]">
|
||||
<img width="22" height="22" src="{{ block.extras.pool['logo'] }}"
|
||||
@ -36,19 +36,19 @@
|
||||
<td class="timestamp" *ngIf="!widget">
|
||||
‎{{ block.timestamp * 1000 | date:'yyyy-MM-dd HH:mm' }}
|
||||
</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>
|
||||
</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>
|
||||
</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>
|
||||
</td>
|
||||
<td class="txs text-right" [class]="widget ? 'widget' : ''">
|
||||
<td class="txs text-right" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}">
|
||||
{{ block.tx_count | number }}
|
||||
</td>
|
||||
<td class="size" *ngIf="!widget">
|
||||
<td class="size" *ngIf="!widget" [class]="indexingAvailable ? '' : 'legacy'">
|
||||
<div class="progress">
|
||||
<div class="progress-bar progress-mempool" role="progressbar"
|
||||
[ngStyle]="{'width': (block.weight / stateService.env.BLOCK_WEIGHT_UNITS)*100 + '%' }"></div>
|
||||
@ -60,29 +60,29 @@
|
||||
<ng-template #skeleton>
|
||||
<tbody>
|
||||
<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>
|
||||
</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">
|
||||
<span class="skeleton-loader" style="max-width: 125px"></span>
|
||||
</td>
|
||||
<td class="timestamp" *ngIf="!widget">
|
||||
<td class="timestamp" *ngIf="!widget" [class]="indexingAvailable ? '' : 'legacy'">
|
||||
<span class="skeleton-loader" style="max-width: 150px"></span>
|
||||
</td>
|
||||
<td class="mined" *ngIf="!widget">
|
||||
<td class="mined" *ngIf="!widget" [class]="indexingAvailable ? '' : 'legacy'">
|
||||
<span class="skeleton-loader" style="max-width: 125px"></span>
|
||||
</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>
|
||||
</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>
|
||||
</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>
|
||||
</td>
|
||||
<td class="size" *ngIf="!widget">
|
||||
<td class="size" *ngIf="!widget" [class]="indexingAvailable ? '' : 'legacy'">
|
||||
<span class="skeleton-loader"></span>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -6,6 +6,9 @@
|
||||
padding-left: 0px;
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
.container-xl.legacy {
|
||||
max-width: 1140px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 100%;
|
||||
@ -58,6 +61,9 @@ tr, td, th {
|
||||
width: 10%;
|
||||
}
|
||||
}
|
||||
.height.legacy {
|
||||
width: 15%;
|
||||
}
|
||||
|
||||
.timestamp {
|
||||
width: 18%;
|
||||
@ -65,6 +71,9 @@ tr, td, th {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.timestamp.legacy {
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
.mined {
|
||||
width: 13%;
|
||||
@ -72,6 +81,12 @@ tr, td, th {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.mined.legacy {
|
||||
width: 15%;
|
||||
@media (max-width: 576px) {
|
||||
display: table-cell;
|
||||
}
|
||||
}
|
||||
|
||||
.txs {
|
||||
padding-right: 40px;
|
||||
@ -88,6 +103,10 @@ tr, td, th {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.txs.legacy {
|
||||
padding-right: 80px;
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
.fees {
|
||||
width: 10%;
|
||||
@ -126,6 +145,12 @@ tr, td, th {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.size.legacy {
|
||||
width: 20%;
|
||||
@media (max-width: 576px) {
|
||||
display: table-cell;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tooltip text */
|
||||
.tooltip-custom {
|
||||
|
@ -17,6 +17,7 @@ export class BlocksList implements OnInit {
|
||||
|
||||
blocks$: Observable<any[]> = undefined;
|
||||
|
||||
indexingAvailable = false;
|
||||
isLoading = true;
|
||||
fromBlockHeight = undefined;
|
||||
paginationMaxSize: number;
|
||||
@ -35,6 +36,9 @@ export class BlocksList implements OnInit {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.indexingAvailable = (this.stateService.env.BASE_MODULE === 'mempool' &&
|
||||
this.stateService.env.MINING_DASHBOARD === true);
|
||||
|
||||
if (!this.widget) {
|
||||
this.websocketService.want(['blocks']);
|
||||
}
|
||||
@ -55,17 +59,19 @@ export class BlocksList implements OnInit {
|
||||
this.isLoading = false;
|
||||
}),
|
||||
map(blocks => {
|
||||
for (const block of blocks) {
|
||||
// @ts-ignore: Need to add an extra field for the template
|
||||
block.extras.pool.logo = `./resources/mining-pools/` +
|
||||
block.extras.pool.name.toLowerCase().replace(' ', '').replace('.', '') + '.svg';
|
||||
if (this.indexingAvailable) {
|
||||
for (const block of blocks) {
|
||||
// @ts-ignore: Need to add an extra field for the template
|
||||
block.extras.pool.logo = `./resources/mining-pools/` +
|
||||
block.extras.pool.name.toLowerCase().replace(' ', '').replace('.', '') + '.svg';
|
||||
}
|
||||
}
|
||||
if (this.widget) {
|
||||
return blocks.slice(0, 6);
|
||||
}
|
||||
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];
|
||||
}
|
||||
this.blocksCount = Math.max(this.blocksCount, blocks[1][0].height) + 1;
|
||||
// @ts-ignore: Need to add an extra field for the template
|
||||
blocks[1][0].extras.pool.logo = `./resources/mining-pools/` +
|
||||
blocks[1][0].extras.pool.name.toLowerCase().replace(' ', '').replace('.', '') + '.svg';
|
||||
if (this.stateService.env.MINING_DASHBOARD) {
|
||||
// @ts-ignore: Need to add an extra field for the template
|
||||
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 = acc.slice(0, this.widget ? 6 : 15);
|
||||
return acc;
|
||||
|
@ -1,4 +1,4 @@
|
||||
<div style="min-height: 295px">
|
||||
<div style="min-height: 335px">
|
||||
<table class="table latest-adjustments">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<div class="mb-3 d-flex menu" style="padding: 0px 35px;">
|
||||
<div *ngIf="stateService.env.MINING_DASHBOARD" class="mb-3 d-flex menu" style="padding: 0px 35px;">
|
||||
<a routerLinkActive="active" class="btn btn-primary w-50 mr-1"
|
||||
[routerLink]="['/graphs/mempool' | relativeUrl]">Mempool</a>
|
||||
<div ngbDropdown *ngIf="stateService.env.MINING_DASHBOARD" class="w-50">
|
||||
<div ngbDropdown class="w-50">
|
||||
<button class="btn btn-primary w-100" id="dropdownBasic1" ngbDropdownToggle i18n="mining">Mining</button>
|
||||
<div ngbDropdownMenu aria-labelledby="dropdownBasic1">
|
||||
<a class="dropdown-item" routerLinkActive="active" [routerLink]="['/graphs/mining/pools' | relativeUrl]"
|
||||
|
@ -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">‎{{ 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>
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -51,7 +51,7 @@
|
||||
<div class="card-body">
|
||||
<h5 class="card-title" i18n="dashboard.latest-blocks">Latest blocks</h5>
|
||||
<app-blocks-list [widget]=true></app-blocks-list>
|
||||
<div><a [routerLink]="['/mining/blocks' | relativeUrl]" i18n="dashboard.view-more">View more »</a></div>
|
||||
<div><a [routerLink]="['/blocks' | relativeUrl]" i18n="dashboard.view-more">View more »</a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -8,7 +8,7 @@
|
||||
<app-amount [satoshis]="rewardStats.totalReward" digitsInfo="1.2-2" [noFiat]="true"></app-amount>
|
||||
</div>
|
||||
<span class="fiat">
|
||||
<app-fiat [value]="rewardStats.totalReward"></app-fiat>
|
||||
<app-fiat [value]="rewardStats.totalReward" digitsInfo="1.0-0" ></app-fiat>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -125,7 +125,7 @@ export class TransactionComponent implements OnInit, OnDestroy {
|
||||
}),
|
||||
switchMap(() => {
|
||||
let transactionObservable$: Observable<Transaction>;
|
||||
if (history.state.data) {
|
||||
if (history.state.data && history.state.data.fee !== -1) {
|
||||
transactionObservable$ = of(history.state.data);
|
||||
} else {
|
||||
transactionObservable$ = this.electrsApiService
|
||||
|
@ -60,7 +60,10 @@
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchDefault>
|
||||
<ng-template [ngIf]="!vin.prevout" [ngIfElse]="defaultAddress">
|
||||
<span>{{ vin.issuance ? 'Issuance' : 'UNKNOWN' }}</span>
|
||||
<span *ngIf="vin.lazy; else defaultNoPrevout" class="skeleton-loader"></span>
|
||||
<ng-template #defaultNoPrevout>
|
||||
<span>{{ vin.issuance ? 'Issuance' : 'UNKNOWN' }}</span>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
<ng-template #defaultAddress>
|
||||
<a [routerLink]="['/address/' | relativeUrl, vin.prevout.scriptpubkey_address]" title="{{ vin.prevout.scriptpubkey_address }}">
|
||||
@ -87,6 +90,7 @@
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
<ng-template #defaultOutput>
|
||||
<span *ngIf="vin.lazy" class="skeleton-loader"></span>
|
||||
<app-amount *ngIf="vin.prevout" [satoshis]="vin.prevout.value"></app-amount>
|
||||
</ng-template>
|
||||
</td>
|
||||
@ -141,7 +145,7 @@
|
||||
</ng-template>
|
||||
<tr *ngIf="tx.vin.length > 12 && tx['@vinLimit']">
|
||||
<td colspan="3" class="text-center">
|
||||
<button class="btn btn-sm btn-primary mt-2" (click)="tx['@vinLimit'] = false;"><span i18n="show-all">Show all</span> ({{ tx.vin.length - 10 }})</button>
|
||||
<button class="btn btn-sm btn-primary mt-2" (click)="loadMoreInputs(tx);"><span i18n="show-all">Show all</span> ({{ tx.vin.length - 10 }})</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@ -261,9 +265,10 @@
|
||||
</div>
|
||||
|
||||
<div class="summary">
|
||||
<div class="float-left mt-2-5" *ngIf="!transactionPage && !tx.vin[0].is_coinbase">
|
||||
<div class="float-left mt-2-5" *ngIf="!transactionPage && !tx.vin[0].is_coinbase && tx.fee !== -1">
|
||||
{{ tx.fee / (tx.weight / 4) | feeRounding }} <span class="symbol" i18n="shared.sat-vbyte|sat/vB">sat/vB</span> <span class="d-none d-sm-inline-block"> – {{ tx.fee | number }} <span class="symbol" i18n="shared.sat|sat">sat</span> <span class="fiat"><app-fiat [value]="tx.fee"></app-fiat></span></span>
|
||||
</div>
|
||||
<div class="float-left mt-2-5 grey-info-text" *ngIf="tx.fee === -1" i18n="transactions-list.load-to-reveal-fee-info">Show all inputs to reveal fee data</div>
|
||||
|
||||
<div class="float-right">
|
||||
<ng-container *ngIf="showConfirmations && latestBlock$ | async as latestBlock">
|
||||
|
@ -139,4 +139,10 @@ h2 {
|
||||
|
||||
.addr-right {
|
||||
font-family: monospace;
|
||||
}
|
||||
}
|
||||
|
||||
.grey-info-text {
|
||||
color:#6c757d;
|
||||
font-style: italic;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
@ -175,6 +175,17 @@ export class TransactionsListComponent implements OnInit, OnChanges {
|
||||
}
|
||||
}
|
||||
|
||||
loadMoreInputs(tx: Transaction) {
|
||||
tx['@vinLimit'] = false;
|
||||
|
||||
this.electrsApiService.getTransaction$(tx.txid)
|
||||
.subscribe((newTx) => {
|
||||
tx.vin = newTx.vin;
|
||||
tx.fee = newTx.fee;
|
||||
this.ref.markForCheck();
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.outspendsSubscription.unsubscribe();
|
||||
}
|
||||
|
@ -136,7 +136,7 @@
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class=""><a href="" [routerLink]="[(stateService.env.MINING_DASHBOARD ? '/mining/blocks' : '/blocks') | relativeUrl]" i18n="dashboard.view-more">View more »</a></div>
|
||||
<div class=""><a href="" [routerLink]="['/blocks' | relativeUrl]" i18n="dashboard.view-more">View more »</a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -198,9 +198,7 @@
|
||||
display: none;
|
||||
text-align: right;
|
||||
width: 20%;
|
||||
@media (min-width: 376px) {
|
||||
display: table-cell;
|
||||
}
|
||||
display: table-cell;
|
||||
}
|
||||
.table-cell-size {
|
||||
display: none;
|
||||
|
@ -13,6 +13,7 @@ const showJsExamplesDefault = { "": true, "testnet": true, "signet": true, "liqu
|
||||
const showJsExamplesDefaultFalse = { "": false, "testnet": false, "signet": false, "liquid": false, "liquidtestnet": false, "bisq": false };
|
||||
|
||||
export const wsApiDocsData = {
|
||||
showJsExamples: showJsExamplesDefault,
|
||||
codeTemplate: {
|
||||
curl: `/api/v1/ws`,
|
||||
commonJS: `
|
||||
@ -2967,7 +2968,7 @@ export const restApiDocsData = [
|
||||
"weight": 3931749,
|
||||
"previousblockhash": "00000000000000000002b5b2afc1c62e61e53f966b965a9a8ce99112e24066ae"
|
||||
},
|
||||
...
|
||||
...
|
||||
]`,
|
||||
},
|
||||
codeSampleTestnet: {
|
||||
@ -3047,7 +3048,7 @@ export const restApiDocsData = [
|
||||
"emptyBlocks": 0,
|
||||
"slug": "antpool"
|
||||
},
|
||||
...
|
||||
...
|
||||
"oldestIndexedBlockTimestamp": 1231006505,
|
||||
"blockCount": 1005,
|
||||
"lastEstimatedHashrate": 230086716765559200000
|
||||
@ -3833,34 +3834,34 @@ export const restApiDocsData = [
|
||||
curl: [`1w`],
|
||||
response: `[
|
||||
{
|
||||
"avg_height": 735644,
|
||||
"avgHeight": 735644,
|
||||
"timestamp": 1652119111,
|
||||
"avg_fees": 24212890
|
||||
"avgFees": 24212890
|
||||
},
|
||||
{
|
||||
"avg_height": 735646,
|
||||
"avgHeight": 735646,
|
||||
"timestamp": 1652120252,
|
||||
"avg_fees": 21655996
|
||||
"avgFees": 21655996
|
||||
},
|
||||
{
|
||||
"avg_height": 735648,
|
||||
"avgHeight": 735648,
|
||||
"timestamp": 1652121214,
|
||||
"avg_fees": 20678859
|
||||
"avgFees": 20678859
|
||||
},
|
||||
{
|
||||
"avg_height": 735649,
|
||||
"avgHeight": 735649,
|
||||
"timestamp": 1652121757,
|
||||
"avg_fees": 21020140
|
||||
"avgFees": 21020140
|
||||
},
|
||||
{
|
||||
"avg_height": 735650,
|
||||
"avgHeight": 735650,
|
||||
"timestamp": 1652122367,
|
||||
"avg_fees": 23064200
|
||||
"avgFees": 23064200
|
||||
},
|
||||
{
|
||||
"avg_height": 735652,
|
||||
"avgHeight": 735652,
|
||||
"timestamp": 1652122893,
|
||||
"avg_fees": 17620723
|
||||
"avgFees": 17620723
|
||||
},
|
||||
...
|
||||
]`
|
||||
@ -3871,14 +3872,14 @@ export const restApiDocsData = [
|
||||
curl: [`1w`],
|
||||
response: `[
|
||||
{
|
||||
"avg_height": 2224253,
|
||||
"avgHeight": 2224253,
|
||||
"timestamp": 1652346420,
|
||||
"avg_fees": 211686
|
||||
"avgFees": 211686
|
||||
},
|
||||
{
|
||||
"avg_height": 2224254,
|
||||
"avgHeight": 2224254,
|
||||
"timestamp": 1652346850,
|
||||
"avg_fees": 2565952
|
||||
"avgFees": 2565952
|
||||
},
|
||||
...
|
||||
]`
|
||||
@ -3889,14 +3890,14 @@ export const restApiDocsData = [
|
||||
curl: [`1w`],
|
||||
response: `[
|
||||
{
|
||||
"avg_height": 89978,
|
||||
"avgHeight": 89978,
|
||||
"timestamp": 1652346573,
|
||||
"avg_fees": 1071
|
||||
"avgFees": 1071
|
||||
},
|
||||
{
|
||||
"avg_height": 89979,
|
||||
"avgHeight": 89979,
|
||||
"timestamp": 1652346970,
|
||||
"avg_fees": 1224
|
||||
"avgFees": 1224
|
||||
},
|
||||
...
|
||||
]`
|
||||
@ -3932,29 +3933,29 @@ export const restApiDocsData = [
|
||||
curl: [`1d`],
|
||||
response: `[
|
||||
{
|
||||
"avg_height": 599992,
|
||||
"avgHeight": 599992,
|
||||
"timestamp": 1571438412,
|
||||
"avg_rewards": 1260530933
|
||||
"avgRewards": 1260530933
|
||||
},
|
||||
{
|
||||
"avg_height": 600000,
|
||||
"avgHeight": 600000,
|
||||
"timestamp": 1571443398,
|
||||
"avg_rewards": 1264314538
|
||||
"avgRewards": 1264314538
|
||||
},
|
||||
{
|
||||
"avg_height": 725441,
|
||||
"avgHeight": 725441,
|
||||
"timestamp": 1646139035,
|
||||
"avg_rewards": 637067563
|
||||
"avgRewards": 637067563
|
||||
},
|
||||
{
|
||||
"avg_height": 725585,
|
||||
"avgHeight": 725585,
|
||||
"timestamp": 1646222444,
|
||||
"avg_rewards": 646519104
|
||||
"avgRewards": 646519104
|
||||
},
|
||||
{
|
||||
"avg_height": 725727,
|
||||
"avgHeight": 725727,
|
||||
"timestamp": 1646308374,
|
||||
"avg_rewards": 638709605
|
||||
"avgRewards": 638709605
|
||||
},
|
||||
...
|
||||
]`
|
||||
@ -3965,14 +3966,14 @@ export const restApiDocsData = [
|
||||
curl: [`1d`],
|
||||
response: `[
|
||||
{
|
||||
"avg_height": 12,
|
||||
"avgHeight": 12,
|
||||
"timestamp": 1296689648,
|
||||
"avg_rewards": 5000000000
|
||||
"avgRewards": 5000000000
|
||||
},
|
||||
{
|
||||
"avg_height": 269,
|
||||
"avgHeight": 269,
|
||||
"timestamp": 1296717674,
|
||||
"avg_rewards": 5000091820
|
||||
"avgRewards": 5000091820
|
||||
},
|
||||
...
|
||||
]`
|
||||
@ -3983,14 +3984,14 @@ export const restApiDocsData = [
|
||||
curl: [`1d`],
|
||||
response: `[
|
||||
{
|
||||
"avg_height": 183,
|
||||
"avgHeight": 183,
|
||||
"timestamp": 1598962247,
|
||||
"avg_rewards": 5000000000
|
||||
"avgRewards": 5000000000
|
||||
},
|
||||
{
|
||||
"avg_height": 576,
|
||||
"avgHeight": 576,
|
||||
"timestamp": 1599047892,
|
||||
"avg_rewards": 5000000000
|
||||
"avgRewards": 5000000000
|
||||
},
|
||||
...
|
||||
]`
|
||||
@ -4028,37 +4029,37 @@ export const restApiDocsData = [
|
||||
"oldestIndexedBlockTimestamp": 1571434851,
|
||||
"blockFeeRates": [
|
||||
{
|
||||
"avg_height": 732152,
|
||||
"avgHeight": 732152,
|
||||
"timestamp": 1650132959,
|
||||
"avg_fee_0": 1,
|
||||
"avg_fee_10": 2,
|
||||
"avg_fee_25": 2,
|
||||
"avg_fee_50": 3,
|
||||
"avg_fee_75": 4,
|
||||
"avg_fee_90": 8,
|
||||
"avg_fee_100": 393
|
||||
"avgFee_0": 1,
|
||||
"avgFee_10": 2,
|
||||
"avgFee_25": 2,
|
||||
"avgFee_50": 3,
|
||||
"avgFee_75": 4,
|
||||
"avgFee_90": 8,
|
||||
"avgFee_100": 393
|
||||
},
|
||||
{
|
||||
"avg_height": 732158,
|
||||
"avgHeight": 732158,
|
||||
"timestamp": 1650134432,
|
||||
"avg_fee_0": 1,
|
||||
"avg_fee_10": 1,
|
||||
"avg_fee_25": 2,
|
||||
"avg_fee_50": 4,
|
||||
"avg_fee_75": 6,
|
||||
"avg_fee_90": 10,
|
||||
"avg_fee_100": 240
|
||||
"avgFee_0": 1,
|
||||
"avgFee_10": 1,
|
||||
"avgFee_25": 2,
|
||||
"avgFee_50": 4,
|
||||
"avgFee_75": 6,
|
||||
"avgFee_90": 10,
|
||||
"avgFee_100": 240
|
||||
},
|
||||
{
|
||||
"avg_height": 732161,
|
||||
"avgHeight": 732161,
|
||||
"timestamp": 1650135818,
|
||||
"avg_fee_0": 1,
|
||||
"avg_fee_10": 1,
|
||||
"avg_fee_25": 1,
|
||||
"avg_fee_50": 2,
|
||||
"avg_fee_75": 5,
|
||||
"avg_fee_90": 8,
|
||||
"avg_fee_100": 251
|
||||
"avgFee_0": 1,
|
||||
"avgFee_10": 1,
|
||||
"avgFee_25": 1,
|
||||
"avgFee_50": 2,
|
||||
"avgFee_75": 5,
|
||||
"avgFee_90": 8,
|
||||
"avgFee_100": 251
|
||||
},
|
||||
...
|
||||
]
|
||||
@ -4072,26 +4073,26 @@ export const restApiDocsData = [
|
||||
"oldestIndexedBlockTimestamp": 1296688602,
|
||||
"blockFeeRates": [
|
||||
{
|
||||
"avg_height": 2196306,
|
||||
"avgHeight": 2196306,
|
||||
"timestamp": 1650360168,
|
||||
"avg_fee_0": 1,
|
||||
"avg_fee_10": 1,
|
||||
"avg_fee_25": 1,
|
||||
"avg_fee_50": 1,
|
||||
"avg_fee_75": 2,
|
||||
"avg_fee_90": 28,
|
||||
"avg_fee_100": 2644
|
||||
"avgFee_0": 1,
|
||||
"avgFee_10": 1,
|
||||
"avgFee_25": 1,
|
||||
"avgFee_50": 1,
|
||||
"avgFee_75": 2,
|
||||
"avgFee_90": 28,
|
||||
"avgFee_100": 2644
|
||||
},
|
||||
{
|
||||
"avg_height": 2196308,
|
||||
"avgHeight": 2196308,
|
||||
"timestamp": 1650361209,
|
||||
"avg_fee_0": 1,
|
||||
"avg_fee_10": 1,
|
||||
"avg_fee_25": 1,
|
||||
"avg_fee_50": 4,
|
||||
"avg_fee_75": 12,
|
||||
"avg_fee_90": 65,
|
||||
"avg_fee_100": 102
|
||||
"avgFee_0": 1,
|
||||
"avgFee_10": 1,
|
||||
"avgFee_25": 1,
|
||||
"avgFee_50": 4,
|
||||
"avgFee_75": 12,
|
||||
"avgFee_90": 65,
|
||||
"avgFee_100": 102
|
||||
},
|
||||
...
|
||||
]
|
||||
@ -4105,26 +4106,26 @@ export const restApiDocsData = [
|
||||
"oldestIndexedBlockTimestamp": 1598918400,
|
||||
"blockFeeRates": [
|
||||
{
|
||||
"avg_height": 86620,
|
||||
"avgHeight": 86620,
|
||||
"timestamp": 1650360010,
|
||||
"avg_fee_0": 1,
|
||||
"avg_fee_10": 1,
|
||||
"avg_fee_25": 1,
|
||||
"avg_fee_50": 1,
|
||||
"avg_fee_75": 1,
|
||||
"avg_fee_90": 1,
|
||||
"avg_fee_100": 1
|
||||
"avgFee_0": 1,
|
||||
"avgFee_10": 1,
|
||||
"avgFee_25": 1,
|
||||
"avgFee_50": 1,
|
||||
"avgFee_75": 1,
|
||||
"avgFee_90": 1,
|
||||
"avgFee_100": 1
|
||||
},
|
||||
{
|
||||
"avg_height": 86623,
|
||||
"avgHeight": 86623,
|
||||
"timestamp": 1650361330,
|
||||
"avg_fee_0": 1,
|
||||
"avg_fee_10": 1,
|
||||
"avg_fee_25": 1,
|
||||
"avg_fee_50": 1,
|
||||
"avg_fee_75": 1,
|
||||
"avg_fee_90": 1,
|
||||
"avg_fee_100": 1
|
||||
"avgFee_0": 1,
|
||||
"avgFee_10": 1,
|
||||
"avgFee_25": 1,
|
||||
"avgFee_50": 1,
|
||||
"avgFee_75": 1,
|
||||
"avgFee_90": 1,
|
||||
"avgFee_100": 1
|
||||
},
|
||||
...
|
||||
]
|
||||
@ -4162,47 +4163,47 @@ export const restApiDocsData = [
|
||||
response: `{
|
||||
"sizes": [
|
||||
{
|
||||
"avg_height": 576650,
|
||||
"avgHeight": 576650,
|
||||
"timestamp": 1558212081,
|
||||
"avg_size": 1271404
|
||||
"avgSize": 1271404
|
||||
},
|
||||
{
|
||||
"avg_height": 576715,
|
||||
"avgHeight": 576715,
|
||||
"timestamp": 1558246272,
|
||||
"avg_size": 1105893
|
||||
"avgSize": 1105893
|
||||
},
|
||||
{
|
||||
"avg_height": 576797,
|
||||
"avgHeight": 576797,
|
||||
"timestamp": 1558289379,
|
||||
"avg_size": 1141071
|
||||
"avgSize": 1141071
|
||||
},
|
||||
{
|
||||
"avg_height": 576885,
|
||||
"avgHeight": 576885,
|
||||
"timestamp": 1558330184,
|
||||
"avg_size": 1108166
|
||||
"avgSize": 1108166
|
||||
},
|
||||
...
|
||||
],
|
||||
"weights": [
|
||||
{
|
||||
"avg_height": 576650,
|
||||
"avgHeight": 576650,
|
||||
"timestamp": 1558212081,
|
||||
"avg_weight": 3994002
|
||||
"avgWeight": 3994002
|
||||
},
|
||||
{
|
||||
"avg_height": 576715,
|
||||
"avgHeight": 576715,
|
||||
"timestamp": 1558246272,
|
||||
"avg_weight": 3756312
|
||||
"avgWeight": 3756312
|
||||
},
|
||||
{
|
||||
"avg_height": 576797,
|
||||
"avgHeight": 576797,
|
||||
"timestamp": 1558289379,
|
||||
"avg_weight": 3719625
|
||||
"avgWeight": 3719625
|
||||
},
|
||||
{
|
||||
"avg_height": 576885,
|
||||
"avgHeight": 576885,
|
||||
"timestamp": 1558330184,
|
||||
"avg_weight": 3631381
|
||||
"avgWeight": 3631381
|
||||
},
|
||||
...
|
||||
]
|
||||
@ -4215,27 +4216,27 @@ export const restApiDocsData = [
|
||||
response: `{
|
||||
"sizes": [
|
||||
{
|
||||
"avg_height": 1517188,
|
||||
"avgHeight": 1517188,
|
||||
"timestamp": 1558262730,
|
||||
"avg_size": 25089
|
||||
"avgSize": 25089
|
||||
},
|
||||
{
|
||||
"avg_height": 1517275,
|
||||
"avgHeight": 1517275,
|
||||
"timestamp": 1558290933,
|
||||
"avg_size": 21679
|
||||
"avgSize": 21679
|
||||
},
|
||||
...
|
||||
],
|
||||
"weights": [
|
||||
{
|
||||
"avg_height": 1517188,
|
||||
"avgHeight": 1517188,
|
||||
"timestamp": 1558262730,
|
||||
"avg_weight": 74921
|
||||
"avgWeight": 74921
|
||||
},
|
||||
{
|
||||
"avg_height": 1517275,
|
||||
"avgHeight": 1517275,
|
||||
"timestamp": 1558290933,
|
||||
"avg_weight": 65164
|
||||
"avgWeight": 65164
|
||||
},
|
||||
...
|
||||
]
|
||||
@ -4248,27 +4249,27 @@ export const restApiDocsData = [
|
||||
response: `{
|
||||
"sizes": [
|
||||
{
|
||||
"avg_height": 83,
|
||||
"avgHeight": 83,
|
||||
"timestamp": 1598937527,
|
||||
"avg_size": 329
|
||||
"avgSize": 329
|
||||
},
|
||||
{
|
||||
"avg_height": 266,
|
||||
"avgHeight": 266,
|
||||
"timestamp": 1598982991,
|
||||
"avg_size": 330
|
||||
"avgSize": 330
|
||||
},
|
||||
...
|
||||
],
|
||||
"weights": [
|
||||
{
|
||||
"avg_height": 83,
|
||||
"avgHeight": 83,
|
||||
"timestamp": 1598937527,
|
||||
"avg_weight": 1209
|
||||
"avgWeight": 1209
|
||||
},
|
||||
{
|
||||
"avg_height": 266,
|
||||
"avgHeight": 266,
|
||||
"timestamp": 1598982991,
|
||||
"avg_weight": 1212
|
||||
"avgWeight": 1212
|
||||
},
|
||||
...
|
||||
]
|
||||
|
@ -101,7 +101,7 @@
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n="api-docs.websocket.websocket">Default push: <code>{{ '{' }} action: 'want', data: ['blocks', ...] {{ '}' }}</code> to express what you want pushed. Available: <code>blocks</code>, <code>mempool-blocks</code>, <code>live-2h-chart</code>, and <code>stats</code>.<br><br>Push transactions related to address: <code>{{ '{' }} 'track-address': '3PbJ...bF9B' {{ '}' }}</code> to receive all new transactions containing that address as input or output. Returns an array of transactions. <code>address-transactions</code> for new mempool transactions, and <code>block-transactions</code> for new block confirmed transactions.</div>
|
||||
</div>
|
||||
<app-code-template [method]="'websocket'" [hostname]="hostname" [code]="wsDocs" [network]="network.val" ></app-code-template>
|
||||
<app-code-template [method]="'websocket'" [hostname]="hostname" [code]="wsDocs" [network]="network.val" [showCodeExample]="wsDocs.showJsExamples"></app-code-template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -54,6 +54,8 @@ export interface Vin {
|
||||
// Elements
|
||||
is_pegin?: boolean;
|
||||
issuance?: Issuance;
|
||||
// Custom
|
||||
lazy?: boolean;
|
||||
}
|
||||
|
||||
interface Issuance {
|
||||
|
@ -149,7 +149,7 @@ export class ApiService {
|
||||
|
||||
getBlocks$(from: number): Observable<BlockExtended[]> {
|
||||
return this.httpClient.get<BlockExtended[]>(
|
||||
this.apiBaseUrl + this.apiBasePath + `/api/v1/blocks-extras` +
|
||||
this.apiBaseUrl + this.apiBasePath + `/api/v1/blocks` +
|
||||
(from !== undefined ? `/${from}` : ``)
|
||||
);
|
||||
}
|
||||
|
@ -48,7 +48,6 @@ import { TransactionsListComponent } from '../components/transactions-list/trans
|
||||
import { BlockComponent } from '../components/block/block.component';
|
||||
import { AddressComponent } from '../components/address/address.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 { FooterComponent } from '../components/footer/footer.component';
|
||||
import { TimeSpanComponent } from '../components/time-span/time-span.component';
|
||||
@ -113,7 +112,6 @@ import { IndexingProgressComponent } from '../components/indexing-progress/index
|
||||
BlockComponent,
|
||||
TransactionsListComponent,
|
||||
AddressComponent,
|
||||
LatestBlocksComponent,
|
||||
SearchFormComponent,
|
||||
TimeSpanComponent,
|
||||
AddressLabelsComponent,
|
||||
@ -208,7 +206,6 @@ import { IndexingProgressComponent } from '../components/indexing-progress/index
|
||||
BlockComponent,
|
||||
TransactionsListComponent,
|
||||
AddressComponent,
|
||||
LatestBlocksComponent,
|
||||
SearchFormComponent,
|
||||
TimeSpanComponent,
|
||||
AddressLabelsComponent,
|
||||
|
1
frontend/src/resources/profile/schildbach.svg
Normal file
1
frontend/src/resources/profile/schildbach.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="-0.015 -0.015 1.03 1.03" preserveAspectRatio="xMidYMid"><defs><filter id="b"><feGaussianBlur in="SourceAlpha" result="blur-out" stdDeviation=".5"/><feMerge><feMergeNode/><feMergeNode in="SourceGraphic"/></feMerge></filter><filter id="c"><feGaussianBlur in="SourceAlpha" result="blur-out" stdDeviation="1.3"/><feMerge><feMergeNode/><feMergeNode in="SourceGraphic"/></feMerge></filter><linearGradient id="a" x1="0%" y1="0%" x2="0%" y2="100%"><stop offset="0%" style="stop-color:#f9b949"/><stop offset="100%" style="stop-color:#f7931a"/></linearGradient></defs><path d="M63.036 39.741c-4.274 17.143-21.637 27.576-38.782 23.301C7.116 58.768-3.317 41.404.959 24.262 5.23 7.117 22.594-3.317 39.734.957c17.144 4.274 27.576 21.64 23.302 38.784z" style="fill:url(#a)" filter="url(#b)" transform="scale(.01563)"/><path d="M46.1 27.441c.638-4.258-2.604-6.547-7.037-8.074l1.438-5.768-3.511-.875-1.4 5.616c-.923-.23-1.871-.447-2.813-.662l1.41-5.653-3.51-.875-1.438 5.766c-.764-.174-1.514-.346-2.242-.527l.004-.018-4.842-1.209-.934 3.75s2.605.597 2.55.634c1.422.355 1.679 1.296 1.636 2.042l-1.638 6.571c.098.025.225.061.365.117l-.371-.092-2.296 9.205c-.174.432-.615 1.08-1.61.834.036.051-2.551-.637-2.551-.637l-1.743 4.019 4.569 1.139c.85.213 1.683.436 2.503.646l-1.453 5.834 3.507.875 1.439-5.772c.958.26 1.888.5 2.798.726l-1.434 5.745 3.51.875 1.454-5.823c5.987 1.133 10.489.676 12.384-4.739 1.527-4.36-.076-6.875-3.226-8.515 2.294-.529 4.022-2.038 4.483-5.155zM38.08 38.69c-1.085 4.36-8.426 2.003-10.806 1.412l1.928-7.729c2.38.594 10.012 1.77 8.878 6.317zm1.086-11.312c-.99 3.966-7.1 1.951-9.082 1.457l1.748-7.01c1.982.494 8.365 1.416 7.334 5.553z" style="fill:#fff" filter="url(#c)" transform="scale(.01563)"/></svg>
|
After Width: | Height: | Size: 1.7 KiB |
@ -4,6 +4,7 @@ slugs=(`curl -sSL https://raw.githubusercontent.com/mempool/mining-pools/master/
|
||||
|
||||
while true
|
||||
do for url in / \
|
||||
'/api/v1/blocks' \
|
||||
'/api/v1/statistics/2h' \
|
||||
'/api/v1/statistics/24h' \
|
||||
'/api/v1/statistics/1w' \
|
||||
@ -36,7 +37,6 @@ do for url in / \
|
||||
'/api/v1/mining/hashrate/pools/3y' \
|
||||
'/api/v1/mining/hashrate/pools/all' \
|
||||
'/api/v1/mining/reward-stats/144' \
|
||||
'/api/v1/mining/blocks-extras' \
|
||||
'/api/v1/mining/blocks/fees/24h' \
|
||||
'/api/v1/mining/blocks/fees/3d' \
|
||||
'/api/v1/mining/blocks/fees/1w' \
|
||||
|
Loading…
Reference in New Issue
Block a user