Merge branch 'origin/HEAD' into natsoni/more-fiat-currencies and fix db version conflicts
@ -33,7 +33,7 @@ interface GraphTx {
|
|||||||
vsize: number;
|
vsize: number;
|
||||||
weight: number;
|
weight: number;
|
||||||
fees: {
|
fees: {
|
||||||
base: number;
|
base: number; // in sats
|
||||||
};
|
};
|
||||||
depends: string[];
|
depends: string[];
|
||||||
spentby: string[];
|
spentby: string[];
|
||||||
@ -42,7 +42,7 @@ interface GraphTx {
|
|||||||
interface MempoolTx extends GraphTx {
|
interface MempoolTx extends GraphTx {
|
||||||
ancestorcount: number;
|
ancestorcount: number;
|
||||||
ancestorsize: number;
|
ancestorsize: number;
|
||||||
fees: {
|
fees: { // in sats
|
||||||
base: number;
|
base: number;
|
||||||
ancestor: number;
|
ancestor: number;
|
||||||
};
|
};
|
||||||
@ -317,7 +317,7 @@ class AccelerationCosts {
|
|||||||
throw new Error('invalid_tx_dependencies');
|
throw new Error('invalid_tx_dependencies');
|
||||||
}
|
}
|
||||||
|
|
||||||
let totalFee = Math.round(tx.fees.ancestor * 100_000_000);
|
let totalFee = tx.fees.ancestor;
|
||||||
|
|
||||||
// transaction is already CPFP-d above the target rate by some descendant
|
// transaction is already CPFP-d above the target rate by some descendant
|
||||||
if (includedInCluster) {
|
if (includedInCluster) {
|
||||||
@ -325,7 +325,7 @@ class AccelerationCosts {
|
|||||||
let clusterFee = 0;
|
let clusterFee = 0;
|
||||||
includedInCluster.forEach(entry => {
|
includedInCluster.forEach(entry => {
|
||||||
clusterSize += entry.vsize;
|
clusterSize += entry.vsize;
|
||||||
clusterFee += (entry.fees.base * 100_000_000);
|
clusterFee += entry.fees.base;
|
||||||
});
|
});
|
||||||
const clusterRate = clusterFee / clusterSize;
|
const clusterRate = clusterFee / clusterSize;
|
||||||
totalFee = Math.ceil(tx.ancestorsize * clusterRate);
|
totalFee = Math.ceil(tx.ancestorsize * clusterRate);
|
||||||
@ -448,8 +448,8 @@ class AccelerationCosts {
|
|||||||
* @param tx
|
* @param tx
|
||||||
*/
|
*/
|
||||||
private setAncestorScores(tx: MempoolTx): void {
|
private setAncestorScores(tx: MempoolTx): void {
|
||||||
tx.individualRate = (tx.fees.base * 100_000_000) / tx.vsize;
|
tx.individualRate = tx.fees.base / tx.vsize;
|
||||||
tx.ancestorRate = (tx.fees.ancestor * 100_000_000) / tx.ancestorsize;
|
tx.ancestorRate = tx.fees.ancestor / tx.ancestorsize;
|
||||||
tx.score = Math.min(tx.individualRate, tx.ancestorRate);
|
tx.score = Math.min(tx.individualRate, tx.ancestorRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,7 +293,6 @@ export class Common {
|
|||||||
}
|
}
|
||||||
if (vout.value < (dustSize * DUST_RELAY_TX_FEE)) {
|
if (vout.value < (dustSize * DUST_RELAY_TX_FEE)) {
|
||||||
// under minimum output size
|
// under minimum output size
|
||||||
console.log(`NON-STANDARD | dust | ${vout.value} | ${dustSize} ${dustSize * DUST_RELAY_TX_FEE} `, tx.txid);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -456,6 +455,8 @@ export class Common {
|
|||||||
flags |= TransactionFlags.no_rbf;
|
flags |= TransactionFlags.no_rbf;
|
||||||
}
|
}
|
||||||
let hasFakePubkey = false;
|
let hasFakePubkey = false;
|
||||||
|
let P2WSHCount = 0;
|
||||||
|
let olgaSize = 0;
|
||||||
for (const vout of tx.vout) {
|
for (const vout of tx.vout) {
|
||||||
switch (vout.scriptpubkey_type) {
|
switch (vout.scriptpubkey_type) {
|
||||||
case 'p2pk': {
|
case 'p2pk': {
|
||||||
@ -483,6 +484,20 @@ export class Common {
|
|||||||
if (vout.scriptpubkey_address) {
|
if (vout.scriptpubkey_address) {
|
||||||
reusedOutputAddresses[vout.scriptpubkey_address] = (reusedOutputAddresses[vout.scriptpubkey_address] || 0) + 1;
|
reusedOutputAddresses[vout.scriptpubkey_address] = (reusedOutputAddresses[vout.scriptpubkey_address] || 0) + 1;
|
||||||
}
|
}
|
||||||
|
if (vout.scriptpubkey_type === 'v0_p2wsh') {
|
||||||
|
if (!P2WSHCount) {
|
||||||
|
olgaSize = parseInt(vout.scriptpubkey.slice(4, 8), 16);
|
||||||
|
}
|
||||||
|
P2WSHCount++;
|
||||||
|
if (P2WSHCount === Math.ceil((olgaSize + 2) / 32)) {
|
||||||
|
const nullBytes = (P2WSHCount * 32) - olgaSize - 2;
|
||||||
|
if (vout.scriptpubkey.endsWith(''.padEnd(nullBytes * 2, '0'))) {
|
||||||
|
flags |= TransactionFlags.fake_scripthash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
P2WSHCount = 0;
|
||||||
|
}
|
||||||
outValues[vout.value || Math.random()] = (outValues[vout.value || Math.random()] || 0) + 1;
|
outValues[vout.value || Math.random()] = (outValues[vout.value || Math.random()] || 0) + 1;
|
||||||
}
|
}
|
||||||
if (hasFakePubkey) {
|
if (hasFakePubkey) {
|
||||||
|
@ -7,7 +7,7 @@ import cpfpRepository from '../repositories/CpfpRepository';
|
|||||||
import { RowDataPacket } from 'mysql2';
|
import { RowDataPacket } from 'mysql2';
|
||||||
|
|
||||||
class DatabaseMigration {
|
class DatabaseMigration {
|
||||||
private static currentVersion = 72;
|
private static currentVersion = 74;
|
||||||
private queryTimeout = 3600_000;
|
private queryTimeout = 3600_000;
|
||||||
private statisticsAddedIndexed = false;
|
private statisticsAddedIndexed = false;
|
||||||
private uniqueLogs: string[] = [];
|
private uniqueLogs: string[] = [];
|
||||||
@ -607,7 +607,20 @@ class DatabaseMigration {
|
|||||||
await this.updateToSchemaVersion(71);
|
await this.updateToSchemaVersion(71);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (databaseSchemaVersion < 72) {
|
if (databaseSchemaVersion < 72 && isBitcoin === true) {
|
||||||
|
// reindex Goggles flags for mined block templates above height 832000
|
||||||
|
await this.$executeQuery('UPDATE blocks_summaries SET version = 0 WHERE height >= 832000;');
|
||||||
|
await this.updateToSchemaVersion(72);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (databaseSchemaVersion < 73 && config.MEMPOOL.NETWORK === 'mainnet') {
|
||||||
|
// Clear bad data
|
||||||
|
await this.$executeQuery(`TRUNCATE accelerations`);
|
||||||
|
this.uniqueLog(logger.notice, `'accelerations' table has been truncated`);
|
||||||
|
await this.updateToSchemaVersion(73);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (databaseSchemaVersion < 74) {
|
||||||
await this.$executeQuery('ALTER TABLE `prices` ADD `BGN` float DEFAULT "-1"');
|
await this.$executeQuery('ALTER TABLE `prices` ADD `BGN` float DEFAULT "-1"');
|
||||||
await this.$executeQuery('ALTER TABLE `prices` ADD `BRL` float DEFAULT "-1"');
|
await this.$executeQuery('ALTER TABLE `prices` ADD `BRL` float DEFAULT "-1"');
|
||||||
await this.$executeQuery('ALTER TABLE `prices` ADD `CNY` float DEFAULT "-1"');
|
await this.$executeQuery('ALTER TABLE `prices` ADD `CNY` float DEFAULT "-1"');
|
||||||
@ -634,7 +647,7 @@ class DatabaseMigration {
|
|||||||
await this.$executeQuery('ALTER TABLE `prices` ADD `THB` float DEFAULT "-1"');
|
await this.$executeQuery('ALTER TABLE `prices` ADD `THB` float DEFAULT "-1"');
|
||||||
await this.$executeQuery('ALTER TABLE `prices` ADD `TRY` float DEFAULT "-1"');
|
await this.$executeQuery('ALTER TABLE `prices` ADD `TRY` float DEFAULT "-1"');
|
||||||
await this.$executeQuery('ALTER TABLE `prices` ADD `ZAR` float DEFAULT "-1"');
|
await this.$executeQuery('ALTER TABLE `prices` ADD `ZAR` float DEFAULT "-1"');
|
||||||
await this.updateToSchemaVersion(72);
|
await this.updateToSchemaVersion(74);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,6 +107,7 @@ class Mempool {
|
|||||||
if (config.MEMPOOL.CACHE_ENABLED && config.REDIS.ENABLED) {
|
if (config.MEMPOOL.CACHE_ENABLED && config.REDIS.ENABLED) {
|
||||||
await redisCache.$addTransaction(this.mempoolCache[txid]);
|
await redisCache.$addTransaction(this.mempoolCache[txid]);
|
||||||
}
|
}
|
||||||
|
this.mempoolCache[txid].flags = Common.getTransactionFlags(this.mempoolCache[txid]);
|
||||||
}
|
}
|
||||||
if (config.MEMPOOL.CACHE_ENABLED && config.REDIS.ENABLED) {
|
if (config.MEMPOOL.CACHE_ENABLED && config.REDIS.ENABLED) {
|
||||||
await redisCache.$flushTransactions();
|
await redisCache.$flushTransactions();
|
||||||
|
@ -226,6 +226,7 @@ export const TransactionFlags = {
|
|||||||
op_return: 0b00000001_00000000_00000000_00000000n,
|
op_return: 0b00000001_00000000_00000000_00000000n,
|
||||||
fake_pubkey: 0b00000010_00000000_00000000_00000000n,
|
fake_pubkey: 0b00000010_00000000_00000000_00000000n,
|
||||||
inscription: 0b00000100_00000000_00000000_00000000n,
|
inscription: 0b00000100_00000000_00000000_00000000n,
|
||||||
|
fake_scripthash: 0b00001000_00000000_00000000_00000000n,
|
||||||
// heuristics
|
// heuristics
|
||||||
coinjoin: 0b00000001_00000000_00000000_00000000_00000000n,
|
coinjoin: 0b00000001_00000000_00000000_00000000_00000000n,
|
||||||
consolidation: 0b00000010_00000000_00000000_00000000_00000000n,
|
consolidation: 0b00000010_00000000_00000000_00000000_00000000n,
|
||||||
|
@ -70,14 +70,26 @@ class AccelerationRepository {
|
|||||||
JOIN pools on pools.unique_id = accelerations.pool
|
JOIN pools on pools.unique_id = accelerations.pool
|
||||||
`;
|
`;
|
||||||
let params: any[] = [];
|
let params: any[] = [];
|
||||||
|
let hasFilter = false;
|
||||||
|
|
||||||
if (interval) {
|
if (interval) {
|
||||||
query += ` WHERE accelerations.added BETWEEN DATE_SUB(NOW(), INTERVAL ${interval}) AND NOW() `;
|
query += ` WHERE accelerations.added BETWEEN DATE_SUB(NOW(), INTERVAL ${interval}) AND NOW() `;
|
||||||
} else if (height != null) {
|
hasFilter = true;
|
||||||
query += ` WHERE accelerations.height = ? `;
|
}
|
||||||
|
|
||||||
|
if (height != null) {
|
||||||
|
if (hasFilter) {
|
||||||
|
query += ` AND accelerations.height = ? `;
|
||||||
|
} else {
|
||||||
|
query += ` WHERE accelerations.height = ? `;
|
||||||
|
}
|
||||||
params.push(height);
|
params.push(height);
|
||||||
} else if (poolSlug != null) {
|
} else if (poolSlug != null) {
|
||||||
query += ` WHERE pools.slug = ? `;
|
if (hasFilter) {
|
||||||
|
query += ` AND pools.slug = ? `;
|
||||||
|
} else {
|
||||||
|
query += ` WHERE pools.slug = ? `;
|
||||||
|
}
|
||||||
params.push(poolSlug);
|
params.push(poolSlug);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { ChangeDetectionStrategy, Component, ElementRef, Inject, LOCALE_ID, OnInit, ViewChild } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, ElementRef, Inject, LOCALE_ID, OnInit, ViewChild } from '@angular/core';
|
||||||
import { WebsocketService } from '../../services/websocket.service';
|
import { WebsocketService } from '../../services/websocket.service';
|
||||||
import { SeoService } from '../../services/seo.service';
|
import { SeoService } from '../../services/seo.service';
|
||||||
|
import { OpenGraphService } from '../../services/opengraph.service';
|
||||||
import { StateService } from '../../services/state.service';
|
import { StateService } from '../../services/state.service';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { ApiService } from '../../services/api.service';
|
import { ApiService } from '../../services/api.service';
|
||||||
@ -33,6 +34,7 @@ export class AboutComponent implements OnInit {
|
|||||||
constructor(
|
constructor(
|
||||||
private websocketService: WebsocketService,
|
private websocketService: WebsocketService,
|
||||||
private seoService: SeoService,
|
private seoService: SeoService,
|
||||||
|
private ogService: OpenGraphService,
|
||||||
public stateService: StateService,
|
public stateService: StateService,
|
||||||
private enterpriseService: EnterpriseService,
|
private enterpriseService: EnterpriseService,
|
||||||
private apiService: ApiService,
|
private apiService: ApiService,
|
||||||
@ -46,6 +48,7 @@ export class AboutComponent implements OnInit {
|
|||||||
this.backendInfo$ = this.stateService.backendInfo$;
|
this.backendInfo$ = this.stateService.backendInfo$;
|
||||||
this.seoService.setTitle($localize`:@@004b222ff9ef9dd4771b777950ca1d0e4cd4348a:About`);
|
this.seoService.setTitle($localize`:@@004b222ff9ef9dd4771b777950ca1d0e4cd4348a:About`);
|
||||||
this.seoService.setDescription($localize`:@@meta.description.about:Learn more about The Mempool Open Source Project®\: enterprise sponsors, individual sponsors, integrations, who contributes, FOSS licensing, and more.`);
|
this.seoService.setDescription($localize`:@@meta.description.about:Learn more about The Mempool Open Source Project®\: enterprise sponsors, individual sponsors, integrations, who contributes, FOSS licensing, and more.`);
|
||||||
|
this.ogService.setManualOgImage('about.jpg');
|
||||||
this.websocketService.want(['blocks']);
|
this.websocketService.want(['blocks']);
|
||||||
|
|
||||||
this.profiles$ = this.apiService.getAboutPageProfiles$().pipe(
|
this.profiles$ = this.apiService.getAboutPageProfiles$().pipe(
|
||||||
|
@ -219,7 +219,7 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<!-- LOGIN CTA -->
|
<!-- LOGIN CTA -->
|
||||||
<ng-container *ngIf="!isLoggedIn()">
|
<ng-container *ngIf="stateService.isMempoolSpaceBuild && !isLoggedIn()">
|
||||||
<tr class="group-first group-last" style="border-top: 1px dashed grey">
|
<tr class="group-first group-last" style="border-top: 1px dashed grey">
|
||||||
<td class="item"></td>
|
<td class="item"></td>
|
||||||
<td class="amt"></td>
|
<td class="amt"></td>
|
||||||
@ -228,6 +228,15 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
<ng-container *ngIf="!stateService.isMempoolSpaceBuild">
|
||||||
|
<tr class="group-first group-last" style="border-top: 1px dashed grey">
|
||||||
|
<td class="item"></td>
|
||||||
|
<td class="amt"></td>
|
||||||
|
<td class="units d-flex">
|
||||||
|
<a [href]="'https://mempool.space/tx/' + tx.txid + '#accelerate'" class="btn btn-purple flex-grow-1">Accelerate on mempool.space</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</ng-container>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
@ -6,6 +6,7 @@ import { Transaction } from '../../interfaces/electrs.interface';
|
|||||||
import { nextRoundNumber } from '../../shared/common.utils';
|
import { nextRoundNumber } from '../../shared/common.utils';
|
||||||
import { ServicesApiServices } from '../../services/services-api.service';
|
import { ServicesApiServices } from '../../services/services-api.service';
|
||||||
import { AudioService } from '../../services/audio.service';
|
import { AudioService } from '../../services/audio.service';
|
||||||
|
import { StateService } from '../../services/state.service';
|
||||||
|
|
||||||
export type AccelerationEstimate = {
|
export type AccelerationEstimate = {
|
||||||
txSummary: TxSummary;
|
txSummary: TxSummary;
|
||||||
@ -63,6 +64,7 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges
|
|||||||
maxRateOptions: RateOption[] = [];
|
maxRateOptions: RateOption[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
public stateService: StateService,
|
||||||
private servicesApiService: ServicesApiServices,
|
private servicesApiService: ServicesApiServices,
|
||||||
private storageService: StorageService,
|
private storageService: StorageService,
|
||||||
private audioService: AudioService,
|
private audioService: AudioService,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { ChangeDetectionStrategy, Component, HostListener, OnInit } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, HostListener, OnInit } from '@angular/core';
|
||||||
import { SeoService } from '../../../services/seo.service';
|
import { SeoService } from '../../../services/seo.service';
|
||||||
|
import { OpenGraphService } from '../../../services/opengraph.service';
|
||||||
import { WebsocketService } from '../../../services/websocket.service';
|
import { WebsocketService } from '../../../services/websocket.service';
|
||||||
import { Acceleration, BlockExtended } from '../../../interfaces/node-api.interface';
|
import { Acceleration, BlockExtended } from '../../../interfaces/node-api.interface';
|
||||||
import { StateService } from '../../../services/state.service';
|
import { StateService } from '../../../services/state.service';
|
||||||
@ -34,11 +35,13 @@ export class AcceleratorDashboardComponent implements OnInit {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private seoService: SeoService,
|
private seoService: SeoService,
|
||||||
|
private ogService: OpenGraphService,
|
||||||
private websocketService: WebsocketService,
|
private websocketService: WebsocketService,
|
||||||
private serviceApiServices: ServicesApiServices,
|
private serviceApiServices: ServicesApiServices,
|
||||||
private stateService: StateService,
|
private stateService: StateService,
|
||||||
) {
|
) {
|
||||||
this.seoService.setTitle($localize`:@@a681a4e2011bb28157689dbaa387de0dd0aa0c11:Accelerator Dashboard`);
|
this.seoService.setTitle($localize`:@@a681a4e2011bb28157689dbaa387de0dd0aa0c11:Accelerator Dashboard`);
|
||||||
|
this.ogService.setManualOgImage('accelerator.jpg');
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
@ -6,6 +6,7 @@ import { ApiService } from '../../services/api.service';
|
|||||||
import { StateService } from '../../services/state.service';
|
import { StateService } from '../../services/state.service';
|
||||||
import { WebsocketService } from '../../services/websocket.service';
|
import { WebsocketService } from '../../services/websocket.service';
|
||||||
import { SeoService } from '../../services/seo.service';
|
import { SeoService } from '../../services/seo.service';
|
||||||
|
import { OpenGraphService } from '../../services/opengraph.service';
|
||||||
import { seoDescriptionNetwork } from '../../shared/common.utils';
|
import { seoDescriptionNetwork } from '../../shared/common.utils';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -39,6 +40,7 @@ export class BlocksList implements OnInit {
|
|||||||
public stateService: StateService,
|
public stateService: StateService,
|
||||||
private cd: ChangeDetectorRef,
|
private cd: ChangeDetectorRef,
|
||||||
private seoService: SeoService,
|
private seoService: SeoService,
|
||||||
|
private ogService: OpenGraphService,
|
||||||
) {
|
) {
|
||||||
this.isMempoolModule = this.stateService.env.BASE_MODULE === 'mempool';
|
this.isMempoolModule = this.stateService.env.BASE_MODULE === 'mempool';
|
||||||
}
|
}
|
||||||
@ -57,6 +59,7 @@ export class BlocksList implements OnInit {
|
|||||||
|
|
||||||
if (!this.widget) {
|
if (!this.widget) {
|
||||||
this.seoService.setTitle($localize`:@@m8a7b4bd44c0ac71b2e72de0398b303257f7d2f54:Blocks`);
|
this.seoService.setTitle($localize`:@@m8a7b4bd44c0ac71b2e72de0398b303257f7d2f54:Blocks`);
|
||||||
|
this.ogService.setManualOgImage('recent-blocks.jpg');
|
||||||
}
|
}
|
||||||
if( this.stateService.network==='liquid'||this.stateService.network==='liquidtestnet' ) {
|
if( this.stateService.network==='liquid'||this.stateService.network==='liquidtestnet' ) {
|
||||||
this.seoService.setDescription($localize`:@@meta.description.liquid.blocks:See the most recent Liquid${seoDescriptionNetwork(this.stateService.network)} blocks along with basic stats such as block height, block size, and more.`);
|
this.seoService.setDescription($localize`:@@meta.description.liquid.blocks:See the most recent Liquid${seoDescriptionNetwork(this.stateService.network)} blocks along with basic stats such as block height, block size, and more.`);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { AfterViewInit, ChangeDetectionStrategy, Component, HostListener, OnInit } from '@angular/core';
|
import { AfterViewInit, ChangeDetectionStrategy, Component, HostListener, OnInit } from '@angular/core';
|
||||||
import { SeoService } from '../../services/seo.service';
|
import { SeoService } from '../../services/seo.service';
|
||||||
|
import { OpenGraphService } from '../../services/opengraph.service';
|
||||||
import { WebsocketService } from '../../services/websocket.service';
|
import { WebsocketService } from '../../services/websocket.service';
|
||||||
import { StateService } from '../../services/state.service';
|
import { StateService } from '../../services/state.service';
|
||||||
import { EventType, NavigationStart, Router } from '@angular/router';
|
import { EventType, NavigationStart, Router } from '@angular/router';
|
||||||
@ -15,12 +16,14 @@ export class MiningDashboardComponent implements OnInit, AfterViewInit {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private seoService: SeoService,
|
private seoService: SeoService,
|
||||||
|
private ogService: OpenGraphService,
|
||||||
private websocketService: WebsocketService,
|
private websocketService: WebsocketService,
|
||||||
private stateService: StateService,
|
private stateService: StateService,
|
||||||
private router: Router
|
private router: Router
|
||||||
) {
|
) {
|
||||||
this.seoService.setTitle($localize`:@@a681a4e2011bb28157689dbaa387de0dd0aa0c11:Mining Dashboard`);
|
this.seoService.setTitle($localize`:@@a681a4e2011bb28157689dbaa387de0dd0aa0c11:Mining Dashboard`);
|
||||||
this.seoService.setDescription($localize`:@@meta.description.mining.dashboard:Get real-time Bitcoin mining stats like hashrate, difficulty adjustment, block rewards, pool dominance, and more.`);
|
this.seoService.setDescription($localize`:@@meta.description.mining.dashboard:Get real-time Bitcoin mining stats like hashrate, difficulty adjustment, block rewards, pool dominance, and more.`);
|
||||||
|
this.ogService.setManualOgImage('mining.jpg');
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { Env, StateService } from '../../services/state.service';
|
import { Env, StateService } from '../../services/state.service';
|
||||||
import { SeoService } from '../../services/seo.service';
|
import { SeoService } from '../../services/seo.service';
|
||||||
|
import { OpenGraphService } from '../../services/opengraph.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-privacy-policy',
|
selector: 'app-privacy-policy',
|
||||||
@ -13,10 +14,12 @@ export class PrivacyPolicyComponent {
|
|||||||
constructor(
|
constructor(
|
||||||
private stateService: StateService,
|
private stateService: StateService,
|
||||||
private seoService: SeoService,
|
private seoService: SeoService,
|
||||||
|
private ogService: OpenGraphService,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.seoService.setTitle('Privacy Policy');
|
this.seoService.setTitle('Privacy Policy');
|
||||||
this.seoService.setDescription('Trusted third parties are security holes, as are trusted first parties...you should only trust your own self-hosted instance of The Mempool Open Source Project®.');
|
this.seoService.setDescription('Trusted third parties are security holes, as are trusted first parties...you should only trust your own self-hosted instance of The Mempool Open Source Project®.');
|
||||||
|
this.ogService.setManualOgImage('privacy.jpg');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms
|
|||||||
import { ApiService } from '../../services/api.service';
|
import { ApiService } from '../../services/api.service';
|
||||||
import { StateService } from '../../services/state.service';
|
import { StateService } from '../../services/state.service';
|
||||||
import { SeoService } from '../../services/seo.service';
|
import { SeoService } from '../../services/seo.service';
|
||||||
|
import { OpenGraphService } from '../../services/opengraph.service';
|
||||||
import { seoDescriptionNetwork } from '../../shared/common.utils';
|
import { seoDescriptionNetwork } from '../../shared/common.utils';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -21,6 +22,7 @@ export class PushTransactionComponent implements OnInit {
|
|||||||
private apiService: ApiService,
|
private apiService: ApiService,
|
||||||
public stateService: StateService,
|
public stateService: StateService,
|
||||||
private seoService: SeoService,
|
private seoService: SeoService,
|
||||||
|
private ogService: OpenGraphService,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
@ -30,6 +32,7 @@ export class PushTransactionComponent implements OnInit {
|
|||||||
|
|
||||||
this.seoService.setTitle($localize`:@@meta.title.push-tx:Broadcast Transaction`);
|
this.seoService.setTitle($localize`:@@meta.title.push-tx:Broadcast Transaction`);
|
||||||
this.seoService.setDescription($localize`:@@meta.description.push-tx:Broadcast a transaction to the ${this.stateService.network==='liquid'||this.stateService.network==='liquidtestnet'?'Liquid':'Bitcoin'}${seoDescriptionNetwork(this.stateService.network)} network using the transaction's hash.`);
|
this.seoService.setDescription($localize`:@@meta.description.push-tx:Broadcast a transaction to the ${this.stateService.network==='liquid'||this.stateService.network==='liquidtestnet'?'Liquid':'Bitcoin'}${seoDescriptionNetwork(this.stateService.network)} network using the transaction's hash.`);
|
||||||
|
this.ogService.setManualOgImage('broadcast-tx.jpg');
|
||||||
}
|
}
|
||||||
|
|
||||||
postTx() {
|
postTx() {
|
||||||
|
@ -7,6 +7,7 @@ import { RbfTree } from '../../interfaces/node-api.interface';
|
|||||||
import { ApiService } from '../../services/api.service';
|
import { ApiService } from '../../services/api.service';
|
||||||
import { StateService } from '../../services/state.service';
|
import { StateService } from '../../services/state.service';
|
||||||
import { SeoService } from '../../services/seo.service';
|
import { SeoService } from '../../services/seo.service';
|
||||||
|
import { OpenGraphService } from '../../services/opengraph.service';
|
||||||
import { seoDescriptionNetwork } from '../../shared/common.utils';
|
import { seoDescriptionNetwork } from '../../shared/common.utils';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -29,6 +30,7 @@ export class RbfList implements OnInit, OnDestroy {
|
|||||||
public stateService: StateService,
|
public stateService: StateService,
|
||||||
private websocketService: WebsocketService,
|
private websocketService: WebsocketService,
|
||||||
private seoService: SeoService,
|
private seoService: SeoService,
|
||||||
|
private ogService: OpenGraphService,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
@ -57,6 +59,7 @@ export class RbfList implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
this.seoService.setTitle($localize`:@@5e3d5a82750902f159122fcca487b07f1af3141f:RBF Replacements`);
|
this.seoService.setTitle($localize`:@@5e3d5a82750902f159122fcca487b07f1af3141f:RBF Replacements`);
|
||||||
this.seoService.setDescription($localize`:@@meta.description.rbf-list:See the most recent RBF replacements on the Bitcoin${seoDescriptionNetwork(this.stateService.network)} network, updated in real-time.`);
|
this.seoService.setDescription($localize`:@@meta.description.rbf-list:See the most recent RBF replacements on the Bitcoin${seoDescriptionNetwork(this.stateService.network)} network, updated in real-time.`);
|
||||||
|
this.ogService.setManualOgImage('rbf.jpg');
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { Env, StateService } from '../../services/state.service';
|
import { Env, StateService } from '../../services/state.service';
|
||||||
import { SeoService } from '../../services/seo.service';
|
import { SeoService } from '../../services/seo.service';
|
||||||
|
import { OpenGraphService } from '../../services/opengraph.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-terms-of-service',
|
selector: 'app-terms-of-service',
|
||||||
@ -12,10 +13,12 @@ export class TermsOfServiceComponent {
|
|||||||
constructor(
|
constructor(
|
||||||
private stateService: StateService,
|
private stateService: StateService,
|
||||||
private seoService: SeoService,
|
private seoService: SeoService,
|
||||||
|
private ogService: OpenGraphService,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.seoService.setTitle('Terms of Service');
|
this.seoService.setTitle('Terms of Service');
|
||||||
this.seoService.setDescription('Out of respect for the Bitcoin community, the mempool.space website is Bitcoin Only and does not display any advertising.');
|
this.seoService.setDescription('Out of respect for the Bitcoin community, the mempool.space website is Bitcoin Only and does not display any advertising.');
|
||||||
|
this.ogService.setManualOgImage('tos.jpg');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { Env, StateService } from '../../services/state.service';
|
import { Env, StateService } from '../../services/state.service';
|
||||||
import { SeoService } from '../../services/seo.service';
|
import { SeoService } from '../../services/seo.service';
|
||||||
|
import { OpenGraphService } from '../../services/opengraph.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-trademark-policy',
|
selector: 'app-trademark-policy',
|
||||||
@ -13,10 +14,12 @@ export class TrademarkPolicyComponent {
|
|||||||
constructor(
|
constructor(
|
||||||
private stateService: StateService,
|
private stateService: StateService,
|
||||||
private seoService: SeoService,
|
private seoService: SeoService,
|
||||||
|
private ogService: OpenGraphService,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.seoService.setTitle('Trademark Policy');
|
this.seoService.setTitle('Trademark Policy');
|
||||||
this.seoService.setDescription('An overview of the trademarks registered by Mempool Space K.K. and The Mempool Open Source Project® and what we consider to be lawful usage of those trademarks.');
|
this.seoService.setDescription('An overview of the trademarks registered by Mempool Space K.K. and The Mempool Open Source Project® and what we consider to be lawful usage of those trademarks.');
|
||||||
|
this.ogService.setManualOgImage('trademark-policy.jpg');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,7 +78,8 @@
|
|||||||
{{ pool.name }}
|
{{ pool.name }}
|
||||||
</a>
|
</a>
|
||||||
<ng-container *ngIf="auditStatus">
|
<ng-container *ngIf="auditStatus">
|
||||||
<span *ngIf="auditStatus.coinbase; else expected" class="badge badge-primary mr-1" i18n="tx-features.tag.coinbase|Coinbase">Coinbase</span>
|
<span *ngIf="auditStatus.coinbase; else accelerated" class="badge badge-primary mr-1" i18n="tx-features.tag.coinbase|Coinbase">Coinbase</span>
|
||||||
|
<ng-template #accelerated><span *ngIf="auditStatus.accelerated || accelerationInfo || (tx && tx.acceleration) ; else expected" class="badge badge-accelerated mr-1" i18n="transaction.audit.accelerated">Accelerated</span></ng-template>
|
||||||
<ng-template #expected><span *ngIf="auditStatus.expected; else seen" class="badge badge-success mr-1" i18n-ngbTooltip="Expected in block tooltip" ngbTooltip="This transaction was projected to be included in the block" placement="bottom" i18n="tx-features.tag.expected|Expected in Block">Expected in Block</span></ng-template>
|
<ng-template #expected><span *ngIf="auditStatus.expected; else seen" class="badge badge-success mr-1" i18n-ngbTooltip="Expected in block tooltip" ngbTooltip="This transaction was projected to be included in the block" placement="bottom" i18n="tx-features.tag.expected|Expected in Block">Expected in Block</span></ng-template>
|
||||||
<ng-template #seen><span *ngIf="auditStatus.seen; else notSeen" class="badge badge-success mr-1" i18n-ngbTooltip="Seen in mempool tooltip" ngbTooltip="This transaction was seen in the mempool prior to mining" placement="bottom" i18n="tx-features.tag.seen|Seen in Mempool">Seen in Mempool</span></ng-template>
|
<ng-template #seen><span *ngIf="auditStatus.seen; else notSeen" class="badge badge-success mr-1" i18n-ngbTooltip="Seen in mempool tooltip" ngbTooltip="This transaction was seen in the mempool prior to mining" placement="bottom" i18n="tx-features.tag.seen|Seen in Mempool">Seen in Mempool</span></ng-template>
|
||||||
<ng-template #notSeen><span class="badge badge-warning mr-1" i18n-ngbTooltip="Not seen in mempool tooltip" ngbTooltip="This transaction was missing from our mempool prior to mining" placement="bottom" i18n="tx-features.tag.not-seen|Not seen in Mempool">Not seen in Mempool</span></ng-template>
|
<ng-template #notSeen><span class="badge badge-warning mr-1" i18n-ngbTooltip="Not seen in mempool tooltip" ngbTooltip="This transaction was missing from our mempool prior to mining" placement="bottom" i18n="tx-features.tag.not-seen|Not seen in Mempool">Not seen in Mempool</span></ng-template>
|
||||||
@ -554,10 +555,6 @@
|
|||||||
|
|
||||||
<ng-template [ngIf]="tx?.status?.confirmed || tx.acceleration || accelerationInfo">
|
<ng-template [ngIf]="tx?.status?.confirmed || tx.acceleration || accelerationInfo">
|
||||||
<app-tx-fee-rating *ngIf="!(tx.acceleration || accelerationInfo) && (tx.fee || tx.effectiveFeePerVsize)" class="ml-2 mr-2 effective-fee-rating" [tx]="tx"></app-tx-fee-rating>
|
<app-tx-fee-rating *ngIf="!(tx.acceleration || accelerationInfo) && (tx.fee || tx.effectiveFeePerVsize)" class="ml-2 mr-2 effective-fee-rating" [tx]="tx"></app-tx-fee-rating>
|
||||||
<ng-container *ngIf="accelerationInfo || tx.acceleration">
|
|
||||||
|
|
||||||
<span class="badge badge-accelerated" i18n="transaction.audit.accelerated">Accelerated</span>
|
|
||||||
</ng-container>
|
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</div>
|
</div>
|
||||||
<button *ngIf="cpfpInfo?.bestDescendant || cpfpInfo?.descendants?.length || cpfpInfo?.ancestors?.length" type="button" class="btn btn-outline-info btn-sm btn-small-height float-right" (click)="showCpfpDetails = !showCpfpDetails">CPFP <fa-icon [icon]="['fas', 'info-circle']" [fixedWidth]="true"></fa-icon></button>
|
<button *ngIf="cpfpInfo?.bestDescendant || cpfpInfo?.descendants?.length || cpfpInfo?.ancestors?.length" type="button" class="btn btn-outline-info btn-sm btn-small-height float-right" (click)="showCpfpDetails = !showCpfpDetails">CPFP <fa-icon [icon]="['fas', 'info-circle']" [fixedWidth]="true"></fa-icon></button>
|
||||||
|
@ -3,6 +3,7 @@ import { ActivatedRoute } from '@angular/router';
|
|||||||
import { Env, StateService } from '../../services/state.service';
|
import { Env, StateService } from '../../services/state.service';
|
||||||
import { WebsocketService } from '../../services/websocket.service';
|
import { WebsocketService } from '../../services/websocket.service';
|
||||||
import { SeoService } from '../../services/seo.service';
|
import { SeoService } from '../../services/seo.service';
|
||||||
|
import { OpenGraphService } from '../../services/opengraph.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-docs',
|
selector: 'app-docs',
|
||||||
@ -24,6 +25,7 @@ export class DocsComponent implements OnInit {
|
|||||||
private stateService: StateService,
|
private stateService: StateService,
|
||||||
private websocket: WebsocketService,
|
private websocket: WebsocketService,
|
||||||
private seoService: SeoService,
|
private seoService: SeoService,
|
||||||
|
private ogService: OpenGraphService,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
@ -44,6 +46,7 @@ export class DocsComponent implements OnInit {
|
|||||||
this.activeTab = 0;
|
this.activeTab = 0;
|
||||||
this.seoService.setTitle($localize`:@@meta.title.docs.faq:FAQ`);
|
this.seoService.setTitle($localize`:@@meta.title.docs.faq:FAQ`);
|
||||||
this.seoService.setDescription($localize`:@@meta.description.docs.faq:Get answers to common questions like: What is a mempool? Why isn't my transaction confirming? How can I run my own instance of The Mempool Open Source Project? And more.`);
|
this.seoService.setDescription($localize`:@@meta.description.docs.faq:Get answers to common questions like: What is a mempool? Why isn't my transaction confirming? How can I run my own instance of The Mempool Open Source Project? And more.`);
|
||||||
|
this.ogService.setManualOgImage('faq.jpg');
|
||||||
} else if( url[1].path === "rest" ) {
|
} else if( url[1].path === "rest" ) {
|
||||||
this.activeTab = 1;
|
this.activeTab = 1;
|
||||||
this.seoService.setTitle($localize`:@@meta.title.docs.rest:REST API`);
|
this.seoService.setTitle($localize`:@@meta.title.docs.rest:REST API`);
|
||||||
|
@ -3,6 +3,7 @@ import { Observable } from 'rxjs';
|
|||||||
import { share } from 'rxjs/operators';
|
import { share } from 'rxjs/operators';
|
||||||
import { INodesRanking, INodesStatistics } from '../../interfaces/node-api.interface';
|
import { INodesRanking, INodesStatistics } from '../../interfaces/node-api.interface';
|
||||||
import { SeoService } from '../../services/seo.service';
|
import { SeoService } from '../../services/seo.service';
|
||||||
|
import { OpenGraphService } from '../../services/opengraph.service';
|
||||||
import { StateService } from '../../services/state.service';
|
import { StateService } from '../../services/state.service';
|
||||||
import { LightningApiService } from '../lightning-api.service';
|
import { LightningApiService } from '../lightning-api.service';
|
||||||
|
|
||||||
@ -21,6 +22,7 @@ export class LightningDashboardComponent implements OnInit, AfterViewInit {
|
|||||||
constructor(
|
constructor(
|
||||||
private lightningApiService: LightningApiService,
|
private lightningApiService: LightningApiService,
|
||||||
private seoService: SeoService,
|
private seoService: SeoService,
|
||||||
|
private ogService: OpenGraphService,
|
||||||
private stateService: StateService,
|
private stateService: StateService,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
@ -29,6 +31,7 @@ export class LightningDashboardComponent implements OnInit, AfterViewInit {
|
|||||||
|
|
||||||
this.seoService.setTitle($localize`:@@142e923d3b04186ac6ba23387265d22a2fa404e0:Lightning Explorer`);
|
this.seoService.setTitle($localize`:@@142e923d3b04186ac6ba23387265d22a2fa404e0:Lightning Explorer`);
|
||||||
this.seoService.setDescription($localize`:@@meta.description.lightning.dashboard:Get stats on the Lightning network (aggregate capacity, connectivity, etc), Lightning nodes (channels, liquidity, etc) and Lightning channels (status, fees, etc).`);
|
this.seoService.setDescription($localize`:@@meta.description.lightning.dashboard:Get stats on the Lightning network (aggregate capacity, connectivity, etc), Lightning nodes (channels, liquidity, etc) and Lightning channels (status, fees, etc).`);
|
||||||
|
this.ogService.setManualOgImage('lightning.jpg');
|
||||||
|
|
||||||
this.nodesRanking$ = this.lightningApiService.getNodesRanking$().pipe(share());
|
this.nodesRanking$ = this.lightningApiService.getNodesRanking$().pipe(share());
|
||||||
this.statistics$ = this.lightningApiService.getLatestStatistics$().pipe(share());
|
this.statistics$ = this.lightningApiService.getLatestStatistics$().pipe(share());
|
||||||
|
@ -25,7 +25,7 @@ export class OpenGraphService {
|
|||||||
) {
|
) {
|
||||||
// save og:image tag from original template
|
// save og:image tag from original template
|
||||||
const initialOgImageTag = metaService.getTag("property='og:image'");
|
const initialOgImageTag = metaService.getTag("property='og:image'");
|
||||||
this.defaultImageUrl = initialOgImageTag?.content || 'https://mempool.space/resources/mempool-space-preview.png';
|
this.defaultImageUrl = initialOgImageTag?.content || 'https://mempool.space/resources/previews/mempool-space-preview.jpg';
|
||||||
this.router.events.pipe(
|
this.router.events.pipe(
|
||||||
filter(event => event instanceof NavigationEnd),
|
filter(event => event instanceof NavigationEnd),
|
||||||
map(() => this.activatedRoute),
|
map(() => this.activatedRoute),
|
||||||
@ -53,7 +53,7 @@ export class OpenGraphService {
|
|||||||
const lang = this.LanguageService.getLanguage();
|
const lang = this.LanguageService.getLanguage();
|
||||||
const ogImageUrl = `${window.location.protocol}//${window.location.host}/render/${lang}/preview${this.router.url}`;
|
const ogImageUrl = `${window.location.protocol}//${window.location.host}/render/${lang}/preview${this.router.url}`;
|
||||||
this.metaService.updateTag({ property: 'og:image', content: ogImageUrl });
|
this.metaService.updateTag({ property: 'og:image', content: ogImageUrl });
|
||||||
this.metaService.updateTag({ property: 'twitter:image:src', content: ogImageUrl });
|
this.metaService.updateTag({ name: 'twitter:image', content: ogImageUrl });
|
||||||
this.metaService.updateTag({ property: 'og:image:type', content: 'image/png' });
|
this.metaService.updateTag({ property: 'og:image:type', content: 'image/png' });
|
||||||
this.metaService.updateTag({ property: 'og:image:width', content: '1200' });
|
this.metaService.updateTag({ property: 'og:image:width', content: '1200' });
|
||||||
this.metaService.updateTag({ property: 'og:image:height', content: '600' });
|
this.metaService.updateTag({ property: 'og:image:height', content: '600' });
|
||||||
@ -61,12 +61,21 @@ export class OpenGraphService {
|
|||||||
|
|
||||||
clearOgImage() {
|
clearOgImage() {
|
||||||
this.metaService.updateTag({ property: 'og:image', content: this.defaultImageUrl });
|
this.metaService.updateTag({ property: 'og:image', content: this.defaultImageUrl });
|
||||||
this.metaService.updateTag({ property: 'twitter:image:src', content: this.defaultImageUrl });
|
this.metaService.updateTag({ name: 'twitter:image', content: this.defaultImageUrl });
|
||||||
this.metaService.updateTag({ property: 'og:image:type', content: 'image/png' });
|
this.metaService.updateTag({ property: 'og:image:type', content: 'image/png' });
|
||||||
this.metaService.updateTag({ property: 'og:image:width', content: '1000' });
|
this.metaService.updateTag({ property: 'og:image:width', content: '1000' });
|
||||||
this.metaService.updateTag({ property: 'og:image:height', content: '500' });
|
this.metaService.updateTag({ property: 'og:image:height', content: '500' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setManualOgImage(imageFilename) {
|
||||||
|
const ogImage = `${window.location.protocol}//${window.location.host}/resources/previews/${imageFilename}`;
|
||||||
|
this.metaService.updateTag({ property: 'og:image', content: ogImage });
|
||||||
|
this.metaService.updateTag({ property: 'og:image:type', content: 'image/jpeg' });
|
||||||
|
this.metaService.updateTag({ property: 'og:image:width', content: '2000' });
|
||||||
|
this.metaService.updateTag({ property: 'og:image:height', content: '1000' });
|
||||||
|
this.metaService.updateTag({ name: 'twitter:image', content: ogImage });
|
||||||
|
}
|
||||||
|
|
||||||
/// register an event that needs to resolve before we can take a screenshot
|
/// register an event that needs to resolve before we can take a screenshot
|
||||||
waitFor(event) {
|
waitFor(event) {
|
||||||
if (!this.previewLoadingEvents[event]) {
|
if (!this.previewLoadingEvents[event]) {
|
||||||
|
@ -39,14 +39,14 @@ export class SeoService {
|
|||||||
setTitle(newTitle: string): void {
|
setTitle(newTitle: string): void {
|
||||||
this.titleService.setTitle(newTitle + ' - ' + this.getTitle());
|
this.titleService.setTitle(newTitle + ' - ' + this.getTitle());
|
||||||
this.metaService.updateTag({ property: 'og:title', content: newTitle});
|
this.metaService.updateTag({ property: 'og:title', content: newTitle});
|
||||||
this.metaService.updateTag({ property: 'twitter:title', content: newTitle});
|
this.metaService.updateTag({ name: 'twitter:title', content: newTitle});
|
||||||
this.metaService.updateTag({ property: 'og:meta:ready', content: 'ready'});
|
this.metaService.updateTag({ property: 'og:meta:ready', content: 'ready'});
|
||||||
}
|
}
|
||||||
|
|
||||||
resetTitle(): void {
|
resetTitle(): void {
|
||||||
this.titleService.setTitle(this.getTitle());
|
this.titleService.setTitle(this.getTitle());
|
||||||
this.metaService.updateTag({ property: 'og:title', content: this.getTitle()});
|
this.metaService.updateTag({ property: 'og:title', content: this.getTitle()});
|
||||||
this.metaService.updateTag({ property: 'twitter:title', content: this.getTitle()});
|
this.metaService.updateTag({ name: 'twitter:title', content: this.getTitle()});
|
||||||
this.metaService.updateTag({ property: 'og:meta:ready', content: 'ready'});
|
this.metaService.updateTag({ property: 'og:meta:ready', content: 'ready'});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,6 +91,7 @@ const defaultEnv: Env = {
|
|||||||
})
|
})
|
||||||
export class StateService {
|
export class StateService {
|
||||||
isBrowser: boolean = isPlatformBrowser(this.platformId);
|
isBrowser: boolean = isPlatformBrowser(this.platformId);
|
||||||
|
isMempoolSpaceBuild = window['isMempoolSpaceBuild'] ?? false;
|
||||||
network = '';
|
network = '';
|
||||||
lightning = false;
|
lightning = false;
|
||||||
blockVSize: number;
|
blockVSize: number;
|
||||||
|
@ -21,12 +21,12 @@
|
|||||||
<div class="selector">
|
<div class="selector">
|
||||||
<app-rate-unit-selector></app-rate-unit-selector>
|
<app-rate-unit-selector></app-rate-unit-selector>
|
||||||
</div>
|
</div>
|
||||||
<a *ngIf="servicesEnabled" class="btn btn-purple sponsor d-none d-sm-flex justify-content-center" [routerLink]="['/login' | relativeUrl]">
|
<a *ngIf="stateService.isMempoolSpaceBuild" class="btn btn-purple sponsor d-none d-sm-flex justify-content-center" [routerLink]="['/login' | relativeUrl]">
|
||||||
<span *ngIf="loggedIn" i18n="shared.my-account">My Account</span>
|
<span *ngIf="loggedIn" i18n="shared.my-account">My Account</span>
|
||||||
<span *ngIf="!loggedIn" i18n="shared.sign-in">Sign In</span>
|
<span *ngIf="!loggedIn" i18n="shared.sign-in">Sign In</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<a *ngIf="servicesEnabled" class="btn btn-purple sponsor d-flex d-sm-none justify-content-center ml-auto mr-auto mt-0 mb-2" [routerLink]="['/login' | relativeUrl]">
|
<a *ngIf="stateService.isMempoolSpaceBuild" class="btn btn-purple sponsor d-flex d-sm-none justify-content-center ml-auto mr-auto mt-0 mb-2" [routerLink]="['/login' | relativeUrl]">
|
||||||
<span *ngIf="loggedIn" i18n="shared.my-account">My Account</span>
|
<span *ngIf="loggedIn" i18n="shared.my-account">My Account</span>
|
||||||
<span *ngIf="!loggedIn" i18n="shared.sign-in">Sign In</span>
|
<span *ngIf="!loggedIn" i18n="shared.sign-in">Sign In</span>
|
||||||
</a>
|
</a>
|
||||||
|
@ -30,7 +30,6 @@ export class GlobalFooterComponent implements OnInit {
|
|||||||
loggedIn = false;
|
loggedIn = false;
|
||||||
urlSubscription: Subscription;
|
urlSubscription: Subscription;
|
||||||
isServicesPage = false;
|
isServicesPage = false;
|
||||||
servicesEnabled = false;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public stateService: StateService,
|
public stateService: StateService,
|
||||||
@ -45,7 +44,6 @@ export class GlobalFooterComponent implements OnInit {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.servicesEnabled = this.officialMempoolSpace && this.stateService.env.ACCELERATOR === true && this.stateService.network === '';
|
|
||||||
this.isServicesPage = this.router.url.includes('/services/');
|
this.isServicesPage = this.router.url.includes('/services/');
|
||||||
|
|
||||||
this.env = this.stateService.env;
|
this.env = this.stateService.env;
|
||||||
|
@ -40,6 +40,7 @@ export const TransactionFlags = {
|
|||||||
op_return: 0b00000001_00000000_00000000_00000000n,
|
op_return: 0b00000001_00000000_00000000_00000000n,
|
||||||
fake_pubkey: 0b00000010_00000000_00000000_00000000n,
|
fake_pubkey: 0b00000010_00000000_00000000_00000000n,
|
||||||
inscription: 0b00000100_00000000_00000000_00000000n,
|
inscription: 0b00000100_00000000_00000000_00000000n,
|
||||||
|
fake_scripthash: 0b00001000_00000000_00000000_00000000n,
|
||||||
// heuristics
|
// heuristics
|
||||||
coinjoin: 0b00000001_00000000_00000000_00000000_00000000n,
|
coinjoin: 0b00000001_00000000_00000000_00000000_00000000n,
|
||||||
consolidation: 0b00000010_00000000_00000000_00000000_00000000n,
|
consolidation: 0b00000010_00000000_00000000_00000000_00000000n,
|
||||||
@ -85,6 +86,7 @@ export const TransactionFilters: { [key: string]: Filter } = {
|
|||||||
op_return: { key: 'op_return', label: 'OP_RETURN', flag: TransactionFlags.op_return, important: true },
|
op_return: { key: 'op_return', label: 'OP_RETURN', flag: TransactionFlags.op_return, important: true },
|
||||||
fake_pubkey: { key: 'fake_pubkey', label: 'Fake pubkey', flag: TransactionFlags.fake_pubkey },
|
fake_pubkey: { key: 'fake_pubkey', label: 'Fake pubkey', flag: TransactionFlags.fake_pubkey },
|
||||||
inscription: { key: 'inscription', label: 'Inscription', flag: TransactionFlags.inscription, important: true },
|
inscription: { key: 'inscription', label: 'Inscription', flag: TransactionFlags.inscription, important: true },
|
||||||
|
fake_scripthash: { key: 'fake_scripthash', label: 'Fake scripthash', flag: TransactionFlags.fake_scripthash },
|
||||||
/* heuristics */
|
/* heuristics */
|
||||||
coinjoin: { key: 'coinjoin', label: 'Coinjoin', flag: TransactionFlags.coinjoin, important: true },
|
coinjoin: { key: 'coinjoin', label: 'Coinjoin', flag: TransactionFlags.coinjoin, important: true },
|
||||||
consolidation: { key: 'consolidation', label: 'Consolidation', flag: TransactionFlags.consolidation },
|
consolidation: { key: 'consolidation', label: 'Consolidation', flag: TransactionFlags.consolidation },
|
||||||
@ -101,7 +103,7 @@ export const FilterGroups: { label: string, filters: Filter[]}[] = [
|
|||||||
{ label: 'Features', filters: ['rbf', 'no_rbf', 'v1', 'v2', 'v3', 'nonstandard'] },
|
{ label: 'Features', filters: ['rbf', 'no_rbf', 'v1', 'v2', 'v3', 'nonstandard'] },
|
||||||
{ label: 'Address Types', filters: ['p2pk', 'p2ms', 'p2pkh', 'p2sh', 'p2wpkh', 'p2wsh', 'p2tr'] },
|
{ label: 'Address Types', filters: ['p2pk', 'p2ms', 'p2pkh', 'p2sh', 'p2wpkh', 'p2wsh', 'p2tr'] },
|
||||||
{ label: 'Behavior', filters: ['cpfp_parent', 'cpfp_child', 'replacement', 'acceleration'] },
|
{ label: 'Behavior', filters: ['cpfp_parent', 'cpfp_child', 'replacement', 'acceleration'] },
|
||||||
{ label: 'Data', filters: ['op_return', 'fake_pubkey', 'inscription'] },
|
{ label: 'Data', filters: ['op_return', 'fake_pubkey', 'fake_scripthash', 'inscription'] },
|
||||||
{ label: 'Heuristics', filters: ['coinjoin', 'consolidation', 'batch_payout'] },
|
{ label: 'Heuristics', filters: ['coinjoin', 'consolidation', 'batch_payout'] },
|
||||||
{ label: 'Sighash Flags', filters: ['sighash_all', 'sighash_none', 'sighash_single', 'sighash_default', 'sighash_acp'] },
|
{ label: 'Sighash Flags', filters: ['sighash_all', 'sighash_none', 'sighash_single', 'sighash_default', 'sighash_acp'] },
|
||||||
].map(group => ({ label: group.label, filters: group.filters.map(filter => TransactionFilters[filter] || null).filter(f => f != null) }));
|
].map(group => ({ label: group.label, filters: group.filters.map(filter => TransactionFilters[filter] || null).filter(f => f != null) }));
|
@ -8,17 +8,17 @@
|
|||||||
<base href="/">
|
<base href="/">
|
||||||
|
|
||||||
<meta name="description" content="Explore the full Bitcoin ecosystem with The Mempool Open Source Project®. See the real-time status of your transactions, get network info, and more." />
|
<meta name="description" content="Explore the full Bitcoin ecosystem with The Mempool Open Source Project®. See the real-time status of your transactions, get network info, and more." />
|
||||||
<meta property="og:image" content="https://mempool.space/resources/mempool-space-preview.png" />
|
<meta property="og:image" content="https://mempool.space/resources/previews/mempool-space-preview.jpg" />
|
||||||
<meta property="og:image:type" content="image/png" />
|
<meta property="og:image:type" content="image/jpeg" />
|
||||||
<meta property="og:image:width" content="1000" />
|
<meta property="og:image:width" content="2000" />
|
||||||
<meta property="og:image:height" content="500" />
|
<meta property="og:image:height" content="1000" />
|
||||||
<meta property="og:description" content="Explore the full Bitcoin ecosystem with The Mempool Open Source Project®. See the real-time status of your transactions, get network info, and more." />
|
<meta property="og:description" content="Explore the full Bitcoin ecosystem with The Mempool Open Source Project®. See the real-time status of your transactions, get network info, and more." />
|
||||||
<meta name="twitter:card" content="summary_large_image">
|
<meta name="twitter:card" content="summary_large_image">
|
||||||
<meta name="twitter:site" content="@mempool">
|
<meta name="twitter:site" content="@mempool">
|
||||||
<meta name="twitter:creator" content="@mempool">
|
<meta name="twitter:creator" content="@mempool">
|
||||||
<meta name="twitter:title" content="The Mempool Open Source Project®">
|
<meta name="twitter:title" content="The Mempool Open Source Project®">
|
||||||
<meta name="twitter:description" content="Explore the full Bitcoin ecosystem with The Mempool Open Source Project®. See the real-time status of your transactions, get network info, and more." />
|
<meta name="twitter:description" content="Explore the full Bitcoin ecosystem with The Mempool Open Source Project®. See the real-time status of your transactions, get network info, and more." />
|
||||||
<meta name="twitter:image:src" content="https://mempool.space/resources/mempool-space-preview.png" />
|
<meta name="twitter:image" content="https://mempool.space/resources/previews/mempool-space-preview.jpg" />
|
||||||
<meta name="twitter:domain" content="mempool.space">
|
<meta name="twitter:domain" content="mempool.space">
|
||||||
|
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/resources/favicons/apple-touch-icon.png">
|
<link rel="apple-touch-icon" sizes="180x180" href="/resources/favicons/apple-touch-icon.png">
|
||||||
|
BIN
frontend/src/resources/previews/about.jpg
Normal file
After Width: | Height: | Size: 73 KiB |
BIN
frontend/src/resources/previews/accelerator.jpg
Normal file
After Width: | Height: | Size: 60 KiB |
BIN
frontend/src/resources/previews/blocks.jpg
Normal file
After Width: | Height: | Size: 72 KiB |
BIN
frontend/src/resources/previews/faq.jpg
Normal file
After Width: | Height: | Size: 44 KiB |
BIN
frontend/src/resources/previews/lightning.jpg
Normal file
After Width: | Height: | Size: 114 KiB |
BIN
frontend/src/resources/previews/mempool-space-preview.jpg
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
frontend/src/resources/previews/mining.jpg
Normal file
After Width: | Height: | Size: 113 KiB |
BIN
frontend/src/resources/previews/privacy-policy.jpg
Normal file
After Width: | Height: | Size: 95 KiB |
BIN
frontend/src/resources/previews/rbf.jpg
Normal file
After Width: | Height: | Size: 60 KiB |
BIN
frontend/src/resources/previews/terms-of-service.jpg
Normal file
After Width: | Height: | Size: 108 KiB |
BIN
frontend/src/resources/previews/trademark-policy.jpg
Normal file
After Width: | Height: | Size: 78 KiB |
BIN
frontend/src/resources/previews/tx-push.jpg
Normal file
After Width: | Height: | Size: 43 KiB |
@ -11,6 +11,30 @@ txindex=1
|
|||||||
[liquidv1]
|
[liquidv1]
|
||||||
validatepegin=1
|
validatepegin=1
|
||||||
mainchainrpcport=8332
|
mainchainrpcport=8332
|
||||||
|
#addnode=[2401:b140:1::92:201]:7042
|
||||||
|
#addnode=[2401:b140:1::92:202]:7042
|
||||||
|
#addnode=[2401:b140:1::92:203]:7042
|
||||||
|
#addnode=[2401:b140:1::92:204]:7042
|
||||||
|
#addnode=[2401:b140:1::92:205]:7042
|
||||||
|
#addnode=[2401:b140:1::92:206]:7042
|
||||||
|
#addnode=[2401:b140:2::92:201]:7042
|
||||||
|
#addnode=[2401:b140:2::92:202]:7042
|
||||||
|
#addnode=[2401:b140:2::92:203]:7042
|
||||||
|
#addnode=[2401:b140:2::92:204]:7042
|
||||||
|
#addnode=[2401:b140:2::92:205]:7042
|
||||||
|
#addnode=[2401:b140:2::92:206]:7042
|
||||||
|
#addnode=[2401:b140:3::92:201]:7042
|
||||||
|
#addnode=[2401:b140:3::92:202]:7042
|
||||||
|
#addnode=[2401:b140:3::92:203]:7042
|
||||||
|
#addnode=[2401:b140:3::92:204]:7042
|
||||||
|
#addnode=[2401:b140:3::92:205]:7042
|
||||||
|
#addnode=[2401:b140:3::92:206]:7042
|
||||||
|
#addnode=[2401:b140:4::92:201]:7042
|
||||||
|
#addnode=[2401:b140:4::92:202]:7042
|
||||||
|
#addnode=[2401:b140:4::92:203]:7042
|
||||||
|
#addnode=[2401:b140:4::92:204]:7042
|
||||||
|
#addnode=[2401:b140:4::92:205]:7042
|
||||||
|
#addnode=[2401:b140:4::92:206]:7042
|
||||||
|
|
||||||
[liquidtestnet]
|
[liquidtestnet]
|
||||||
rpcport=7040
|
rpcport=7040
|
||||||
@ -34,3 +58,27 @@ signblockscript=51210217e403ddb181872c32a0cd468c710040b2f53d8cac69f18dad07985ee3
|
|||||||
evbparams=dynafed:0:::
|
evbparams=dynafed:0:::
|
||||||
addnode=liquid-testnet.blockstream.com:18892
|
addnode=liquid-testnet.blockstream.com:18892
|
||||||
addnode=liquidtestnet.com:18891
|
addnode=liquidtestnet.com:18891
|
||||||
|
#addnode=[2401:b140:1::92:201]:18891
|
||||||
|
#addnode=[2401:b140:1::92:202]:18891
|
||||||
|
#addnode=[2401:b140:1::92:203]:18891
|
||||||
|
#addnode=[2401:b140:1::92:204]:18891
|
||||||
|
#addnode=[2401:b140:1::92:205]:18891
|
||||||
|
#addnode=[2401:b140:1::92:206]:18891
|
||||||
|
#addnode=[2401:b140:2::92:201]:18891
|
||||||
|
#addnode=[2401:b140:2::92:202]:18891
|
||||||
|
#addnode=[2401:b140:2::92:203]:18891
|
||||||
|
#addnode=[2401:b140:2::92:204]:18891
|
||||||
|
#addnode=[2401:b140:2::92:205]:18891
|
||||||
|
#addnode=[2401:b140:2::92:206]:18891
|
||||||
|
#addnode=[2401:b140:3::92:201]:18891
|
||||||
|
#addnode=[2401:b140:3::92:202]:18891
|
||||||
|
#addnode=[2401:b140:3::92:203]:18891
|
||||||
|
#addnode=[2401:b140:3::92:204]:18891
|
||||||
|
#addnode=[2401:b140:3::92:205]:18891
|
||||||
|
#addnode=[2401:b140:3::92:206]:18891
|
||||||
|
#addnode=[2401:b140:4::92:201]:18891
|
||||||
|
#addnode=[2401:b140:4::92:202]:18891
|
||||||
|
#addnode=[2401:b140:4::92:203]:18891
|
||||||
|
#addnode=[2401:b140:4::92:204]:18891
|
||||||
|
#addnode=[2401:b140:4::92:205]:18891
|
||||||
|
#addnode=[2401:b140:4::92:206]:18891
|
||||||
|
@ -7,6 +7,18 @@ interface Match {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const routes = {
|
const routes = {
|
||||||
|
about: {
|
||||||
|
title: "About",
|
||||||
|
fallbackImg: '/resources/previews/about.jpg',
|
||||||
|
},
|
||||||
|
acceleration: {
|
||||||
|
title: "Acceleration",
|
||||||
|
fallbackImg: '/resources/previews/accelerator.jpg',
|
||||||
|
},
|
||||||
|
accelerator: {
|
||||||
|
title: "Mempool Accelerator",
|
||||||
|
fallbackImg: '/resources/previews/accelerator.jpg',
|
||||||
|
},
|
||||||
block: {
|
block: {
|
||||||
render: true,
|
render: true,
|
||||||
params: 1,
|
params: 1,
|
||||||
@ -14,6 +26,20 @@ const routes = {
|
|||||||
return `Block: ${path[0]}`;
|
return `Block: ${path[0]}`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
blocks: {
|
||||||
|
title: "Blocks",
|
||||||
|
fallbackImg: '/resources/previews/blocks.jpg',
|
||||||
|
},
|
||||||
|
docs: {
|
||||||
|
title: "Docs",
|
||||||
|
fallbackImg: '/resources/previews/faq.jpg',
|
||||||
|
routes: {
|
||||||
|
faq: {
|
||||||
|
title: "FAQ",
|
||||||
|
fallbackImg: '/resources/previews/faq.jpg',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
address: {
|
address: {
|
||||||
render: true,
|
render: true,
|
||||||
params: 1,
|
params: 1,
|
||||||
@ -26,11 +52,17 @@ const routes = {
|
|||||||
params: 1,
|
params: 1,
|
||||||
getTitle(path) {
|
getTitle(path) {
|
||||||
return `Transaction: ${path[0]}`;
|
return `Transaction: ${path[0]}`;
|
||||||
|
},
|
||||||
|
routes: {
|
||||||
|
push: {
|
||||||
|
title: "Push Transaction",
|
||||||
|
fallbackImg: '/resources/previews/tx-push.jpg',
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
lightning: {
|
lightning: {
|
||||||
title: "Lightning",
|
title: "Lightning",
|
||||||
fallbackImg: '/resources/previews/lightning.png',
|
fallbackImg: '/resources/previews/lightning.jpg',
|
||||||
routes: {
|
routes: {
|
||||||
node: {
|
node: {
|
||||||
render: true,
|
render: true,
|
||||||
@ -68,7 +100,7 @@ const routes = {
|
|||||||
},
|
},
|
||||||
mining: {
|
mining: {
|
||||||
title: "Mining",
|
title: "Mining",
|
||||||
fallbackImg: '/resources/previews/mining.png',
|
fallbackImg: '/resources/previews/mining.jpg',
|
||||||
routes: {
|
routes: {
|
||||||
pool: {
|
pool: {
|
||||||
render: true,
|
render: true,
|
||||||
@ -78,12 +110,28 @@ const routes = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"privacy-policy": {
|
||||||
|
title: "Privacy Policy",
|
||||||
|
fallbackImg: '/resources/previews/privacy-policy.jpg',
|
||||||
|
},
|
||||||
|
rbf: {
|
||||||
|
title: "RBF",
|
||||||
|
fallbackImg: '/resources/previews/rbf.jpg',
|
||||||
|
},
|
||||||
|
"terms-of-service": {
|
||||||
|
title: "Terms of Service",
|
||||||
|
fallbackImg: '/resources/previews/terms-of-service.jpg',
|
||||||
|
},
|
||||||
|
"trademark-policy": {
|
||||||
|
title: "Trademark Policy",
|
||||||
|
fallbackImg: '/resources/previews/trademark-policy.jpg',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const networks = {
|
const networks = {
|
||||||
bitcoin: {
|
bitcoin: {
|
||||||
fallbackImg: '/resources/previews/dashboard.png',
|
fallbackImg: '/resources/previews/mempool-space-preview.jpg',
|
||||||
routes: {
|
routes: {
|
||||||
...routes // all routes supported
|
...routes // all routes supported
|
||||||
}
|
}
|
||||||
|