Compare commits
54 Commits
v2.3.0-rc4
...
v2.3.0-rc5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
898ff5da23 | ||
|
|
d78d2c0eca | ||
|
|
a08e77ff3e | ||
|
|
1e39eb0fa5 | ||
|
|
5de133ae6a | ||
|
|
d27b125848 | ||
|
|
ad36d53bb5 | ||
|
|
24f76f2f37 | ||
|
|
691bdda523 | ||
|
|
81bb31090e | ||
|
|
cc0a0719b6 | ||
|
|
7dca8ae1a0 | ||
|
|
84027d5568 | ||
|
|
4116186c1a | ||
|
|
358301020f | ||
|
|
642022bfd8 | ||
|
|
70f25b6c9c | ||
|
|
c778e84247 | ||
|
|
4de1d017ad | ||
|
|
61851be23a | ||
|
|
5de949eaed | ||
|
|
de6434a5ba | ||
|
|
c8639ec71d | ||
|
|
e1275c62cc | ||
|
|
be45e88056 | ||
|
|
990ab3da5f | ||
|
|
d1d74ebf37 | ||
|
|
6ab79b3c35 | ||
|
|
4f21fc0d87 | ||
|
|
10c4e47091 | ||
|
|
dd49ff0084 | ||
|
|
853314ba58 | ||
|
|
784e2470df | ||
|
|
350b4922da | ||
|
|
40fb1792f4 | ||
|
|
7ce1cc5103 | ||
|
|
71a4e24900 | ||
|
|
a48c2c07b0 | ||
|
|
d89d7efbe6 | ||
|
|
5ea4b043d9 | ||
|
|
dd4710b602 | ||
|
|
832c0cb3cc | ||
|
|
04216e952a | ||
|
|
951d0f0039 | ||
|
|
706f4bbc55 | ||
|
|
3fd96e412b | ||
|
|
766803ded1 | ||
|
|
504f46cad9 | ||
|
|
fd34761a93 | ||
|
|
96e8f45e5b | ||
|
|
195fae670b | ||
|
|
dd767f9468 | ||
|
|
bc8104eeb4 | ||
|
|
2c61eb6227 |
@@ -25,8 +25,7 @@ help:
|
||||
.PHONY: init
|
||||
init:
|
||||
@echo ''
|
||||
mkdir -p $(DATA) $(DATA)/mysql $(DATA)/mysql/db-scripts $(DATA)/mysql/data
|
||||
install -v mariadb-structure.sql $(DATA)/mysql/db-scripts
|
||||
mkdir -p $(DATA) $(DATA)/mysql $(DATA)/mysql/data
|
||||
#REF: https://github.com/mempool/mempool/blob/master/docker/README.md
|
||||
cat docker/docker-compose.yml > docker-compose.yml
|
||||
cat backend/mempool-config.sample.json > backend/mempool-config.json
|
||||
|
||||
@@ -69,11 +69,6 @@ Create database and grant privileges:
|
||||
Query OK, 0 rows affected (0.00 sec)
|
||||
```
|
||||
|
||||
From the mempool repo's top-level folder, import the database structure:
|
||||
```bash
|
||||
mysql -u mempool -p mempool < mariadb-structure.sql
|
||||
```
|
||||
|
||||
## Mempool Backend
|
||||
Install mempool dependencies from npm and build the backend:
|
||||
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
import { CpfpInfo, TransactionExtended, TransactionStripped } from '../mempool.interfaces';
|
||||
import config from '../config';
|
||||
export class Common {
|
||||
static nativeAssetId = '6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d';
|
||||
static nativeAssetId = config.MEMPOOL.NETWORK === 'liquidtestnet' ?
|
||||
'144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49'
|
||||
: '6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d';
|
||||
static _isLiquid = config.MEMPOOL.NETWORK === 'liquid' || config.MEMPOOL.NETWORK === 'liquidtestnet';
|
||||
|
||||
static isLiquid(): boolean {
|
||||
return this._isLiquid;
|
||||
}
|
||||
|
||||
static median(numbers: number[]) {
|
||||
let medianNr = 0;
|
||||
@@ -107,7 +114,7 @@ export class Common {
|
||||
totalFees += tx.bestDescendant.fee;
|
||||
}
|
||||
|
||||
tx.effectiveFeePerVsize = Math.max(config.MEMPOOL.NETWORK === 'liquid' ? 0.1 : 1, totalFees / (totalWeight / 4));
|
||||
tx.effectiveFeePerVsize = Math.max(Common.isLiquid() ? 0.1 : 1, totalFees / (totalWeight / 4));
|
||||
tx.cpfpChecked = true;
|
||||
|
||||
return {
|
||||
|
||||
@@ -127,7 +127,7 @@ class DatabaseMigration {
|
||||
const queries: string[] = [];
|
||||
|
||||
if (version < 1) {
|
||||
if (config.MEMPOOL.NETWORK !== 'liquid') {
|
||||
if (config.MEMPOOL.NETWORK !== 'liquid' && config.MEMPOOL.NETWORK !== 'liquidtestnet') {
|
||||
queries.push(`UPDATE statistics SET
|
||||
vsize_1 = vsize_1 + vsize_2, vsize_2 = vsize_3,
|
||||
vsize_3 = vsize_4, vsize_4 = vsize_5,
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import config from '../config';
|
||||
import { MempoolBlock } from '../mempool.interfaces';
|
||||
import { Common } from './common';
|
||||
import mempool from './mempool';
|
||||
import projectedBlocks from './mempool-blocks';
|
||||
|
||||
class FeeApi {
|
||||
constructor() { }
|
||||
|
||||
defaultFee = config.MEMPOOL.NETWORK === 'liquid' ? 0.1 : 1;
|
||||
defaultFee = Common.isLiquid() ? 0.1 : 1;
|
||||
|
||||
public getRecommendedFee() {
|
||||
const pBlocks = projectedBlocks.getMempoolBlocks();
|
||||
|
||||
@@ -4,6 +4,7 @@ import logger from '../logger';
|
||||
|
||||
import { Statistic, TransactionExtended, OptimizedStatistic } from '../mempool.interfaces';
|
||||
import config from '../config';
|
||||
import { Common } from './common';
|
||||
|
||||
class Statistics {
|
||||
protected intervalTimer: NodeJS.Timer | undefined;
|
||||
@@ -90,9 +91,9 @@ class Statistics {
|
||||
memPoolArray.forEach((transaction) => {
|
||||
for (let i = 0; i < logFees.length; i++) {
|
||||
if (
|
||||
(config.MEMPOOL.NETWORK === 'liquid' && (i === lastItem || transaction.effectiveFeePerVsize * 10 < logFees[i + 1]))
|
||||
(Common.isLiquid() && (i === lastItem || transaction.effectiveFeePerVsize * 10 < logFees[i + 1]))
|
||||
||
|
||||
(config.MEMPOOL.NETWORK !== 'liquid' && (i === lastItem || transaction.effectiveFeePerVsize < logFees[i + 1]))
|
||||
(!Common.isLiquid() && (i === lastItem || transaction.effectiveFeePerVsize < logFees[i + 1]))
|
||||
) {
|
||||
if (weightVsizeFees[logFees[i]]) {
|
||||
weightVsizeFees[logFees[i]] += transaction.vsize;
|
||||
@@ -407,7 +408,7 @@ class Statistics {
|
||||
public async $list1W(): Promise<OptimizedStatistic[]> {
|
||||
try {
|
||||
const connection = await DB.pool.getConnection();
|
||||
const query = this.getQueryForDaysAvg(600, '1 WEEK'); // 10m interval
|
||||
const query = this.getQueryForDaysAvg(300, '1 WEEK'); // 5m interval
|
||||
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout });
|
||||
connection.release();
|
||||
return this.mapStatisticToOptimizedStatistic(rows);
|
||||
@@ -420,7 +421,7 @@ class Statistics {
|
||||
public async $list1M(): Promise<OptimizedStatistic[]> {
|
||||
try {
|
||||
const connection = await DB.pool.getConnection();
|
||||
const query = this.getQueryForDaysAvg(3600, '1 MONTH'); // 1h interval
|
||||
const query = this.getQueryForDaysAvg(1800, '1 MONTH'); // 30m interval
|
||||
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout });
|
||||
connection.release();
|
||||
return this.mapStatisticToOptimizedStatistic(rows);
|
||||
@@ -433,7 +434,7 @@ class Statistics {
|
||||
public async $list3M(): Promise<OptimizedStatistic[]> {
|
||||
try {
|
||||
const connection = await DB.pool.getConnection();
|
||||
const query = this.getQueryForDaysAvg(14400, '3 MONTH'); // 4h interval
|
||||
const query = this.getQueryForDaysAvg(7200, '3 MONTH'); // 2h interval
|
||||
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout });
|
||||
connection.release();
|
||||
return this.mapStatisticToOptimizedStatistic(rows);
|
||||
@@ -446,7 +447,7 @@ class Statistics {
|
||||
public async $list6M(): Promise<OptimizedStatistic[]> {
|
||||
try {
|
||||
const connection = await DB.pool.getConnection();
|
||||
const query = this.getQueryForDaysAvg(21600, '6 MONTH'); // 6h interval
|
||||
const query = this.getQueryForDaysAvg(10800, '6 MONTH'); // 3h interval
|
||||
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout });
|
||||
connection.release();
|
||||
return this.mapStatisticToOptimizedStatistic(rows);
|
||||
@@ -459,7 +460,7 @@ class Statistics {
|
||||
public async $list1Y(): Promise<OptimizedStatistic[]> {
|
||||
try {
|
||||
const connection = await DB.pool.getConnection();
|
||||
const query = this.getQueryForDays(43200, '1 YEAR'); // 12h interval
|
||||
const query = this.getQueryForDays(28800, '1 YEAR'); // 8h interval
|
||||
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout });
|
||||
connection.release();
|
||||
return this.mapStatisticToOptimizedStatistic(rows);
|
||||
@@ -472,7 +473,7 @@ class Statistics {
|
||||
public async $list2Y(): Promise<OptimizedStatistic[]> {
|
||||
try {
|
||||
const connection = await DB.pool.getConnection();
|
||||
const query = this.getQueryForDays(86400, "2 YEAR"); // 1d interval
|
||||
const query = this.getQueryForDays(28800, "2 YEAR"); // 8h interval
|
||||
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout });
|
||||
connection.release();
|
||||
return this.mapStatisticToOptimizedStatistic(rows);
|
||||
@@ -485,7 +486,7 @@ class Statistics {
|
||||
public async $list3Y(): Promise<OptimizedStatistic[]> {
|
||||
try {
|
||||
const connection = await DB.pool.getConnection();
|
||||
const query = this.getQueryForDays(86400, "3 YEAR"); // 1d interval
|
||||
const query = this.getQueryForDays(43200, "3 YEAR"); // 12h interval
|
||||
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout });
|
||||
connection.release();
|
||||
return this.mapStatisticToOptimizedStatistic(rows);
|
||||
|
||||
@@ -2,6 +2,7 @@ import bitcoinApi from './bitcoin/bitcoin-api-factory';
|
||||
import { TransactionExtended, TransactionMinerInfo } from '../mempool.interfaces';
|
||||
import { IEsploraApi } from './bitcoin/esplora-api.interface';
|
||||
import config from '../config';
|
||||
import { Common } from './common';
|
||||
|
||||
class TransactionUtils {
|
||||
constructor() { }
|
||||
@@ -31,7 +32,8 @@ class TransactionUtils {
|
||||
// @ts-ignore
|
||||
return transaction;
|
||||
}
|
||||
const feePerVbytes = Math.max(config.MEMPOOL.NETWORK === 'liquid' ? 0.1 : 1, (transaction.fee || 0) / (transaction.weight / 4));
|
||||
const feePerVbytes = Math.max(Common.isLiquid() ? 0.1 : 1,
|
||||
(transaction.fee || 0) / (transaction.weight / 4));
|
||||
const transactionExtended: TransactionExtended = Object.assign({
|
||||
vsize: Math.round(transaction.weight / 4),
|
||||
feePerVsize: feePerVbytes,
|
||||
|
||||
@@ -2,7 +2,7 @@ const configFile = require('../mempool-config.json');
|
||||
|
||||
interface IConfig {
|
||||
MEMPOOL: {
|
||||
NETWORK: 'mainnet' | 'testnet' | 'signet' | 'liquid';
|
||||
NETWORK: 'mainnet' | 'testnet' | 'signet' | 'liquid' | 'liquidtestnet';
|
||||
BACKEND: 'esplora' | 'electrum' | 'none';
|
||||
HTTP_PORT: number;
|
||||
SPAWN_CLUSTER_PROCS: number;
|
||||
|
||||
@@ -24,6 +24,7 @@ import elementsParser from './api/liquid/elements-parser';
|
||||
import databaseMigration from './api/database-migration';
|
||||
import syncAssets from './sync-assets';
|
||||
import icons from './api/liquid/icons';
|
||||
import { Common } from './api/common';
|
||||
|
||||
class Server {
|
||||
private wss: WebSocket.Server | undefined;
|
||||
@@ -96,7 +97,7 @@ class Server {
|
||||
statistics.startStatistics();
|
||||
}
|
||||
|
||||
if (config.MEMPOOL.NETWORK === 'liquid') {
|
||||
if (Common.isLiquid()) {
|
||||
icons.loadIcons();
|
||||
}
|
||||
|
||||
@@ -156,7 +157,7 @@ class Server {
|
||||
if (this.wss) {
|
||||
websocketHandler.setWebsocketServer(this.wss);
|
||||
}
|
||||
if (config.MEMPOOL.NETWORK === 'liquid' && config.DATABASE.ENABLED) {
|
||||
if (Common.isLiquid() && config.DATABASE.ENABLED) {
|
||||
blocks.setNewBlockCallback(async () => {
|
||||
try {
|
||||
await elementsParser.$parse();
|
||||
@@ -283,14 +284,14 @@ class Server {
|
||||
;
|
||||
}
|
||||
|
||||
if (config.MEMPOOL.NETWORK === 'liquid') {
|
||||
if (Common.isLiquid()) {
|
||||
this.app
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'assets/icons', routes.getAllLiquidIcon)
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'asset/:assetId/icon', routes.getLiquidIcon)
|
||||
;
|
||||
}
|
||||
|
||||
if (config.MEMPOOL.NETWORK === 'liquid' && config.DATABASE.ENABLED) {
|
||||
if (Common.isLiquid() && config.DATABASE.ENABLED) {
|
||||
this.app
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'liquid/pegs/month', routes.$getElementsPegsByMonth)
|
||||
;
|
||||
|
||||
@@ -5,11 +5,9 @@
|
||||
In an empty dir create 2 sub-dirs
|
||||
|
||||
```bash
|
||||
mkdir -p data mysql/data mysql/db-scripts
|
||||
mkdir -p data mysql/data
|
||||
```
|
||||
|
||||
In the `mysql/db-scripts` sub-dir add the `mariadb-structure.sql` file from the mempool repo
|
||||
|
||||
Your dir should now look like that:
|
||||
|
||||
```bash
|
||||
@@ -23,9 +21,6 @@ data mysql
|
||||
data db-scripts
|
||||
|
||||
./mysql/data:
|
||||
|
||||
./mysql/db-scripts:
|
||||
mariadb-structure.sql
|
||||
```
|
||||
|
||||
In the main dir add the following `docker-compose.yml`
|
||||
|
||||
2
frontend/.gitignore
vendored
2
frontend/.gitignore
vendored
@@ -50,6 +50,8 @@ Thumbs.db
|
||||
|
||||
src/resources/assets.json
|
||||
src/resources/assets.minimal.json
|
||||
src/resources/assets-testnet.json
|
||||
src/resources/assets-testnet.minimal.json
|
||||
src/resources/pools.json
|
||||
|
||||
# environment config
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"TESTNET_ENABLED": false,
|
||||
"SIGNET_ENABLED": false,
|
||||
"LIQUID_ENABLED": false,
|
||||
"LIQUID_TESTNET_ENABLED": false,
|
||||
"BISQ_ENABLED": false,
|
||||
"BISQ_SEPARATE_BACKEND": false,
|
||||
"ITEMS_PER_PAGE": 10,
|
||||
|
||||
@@ -24,6 +24,7 @@ PROXY_CONFIG = [
|
||||
'/api/**', '!/api/v1/ws',
|
||||
'!/bisq', '!/bisq/**', '!/bisq/',
|
||||
'!/liquid', '!/liquid/**', '!/liquid/',
|
||||
'!/liquidtestnet', '!/liquidtestnet/**', '!/liquidtestnet/',
|
||||
'/testnet/api/**', '/signet/api/**'
|
||||
],
|
||||
target: "https://mempool.space",
|
||||
@@ -57,6 +58,16 @@ PROXY_CONFIG = [
|
||||
ws: true,
|
||||
secure: false,
|
||||
changeOrigin: true
|
||||
},
|
||||
{
|
||||
context: ['/api/liquidtestnet**', '/liquidtestnet/api/**'],
|
||||
target: "https://liquid.network/testnet",
|
||||
pathRewrite: {
|
||||
"^/api/liquidtestnet/": "/liquidtestnet/api"
|
||||
},
|
||||
ws: true,
|
||||
secure: false,
|
||||
changeOrigin: true
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -105,6 +105,91 @@ let routes: Routes = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'liquidtestnet',
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: MasterPageComponent,
|
||||
children: [
|
||||
{
|
||||
path: 'tx/push',
|
||||
component: PushTransactionComponent,
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
component: StartComponent,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: DashboardComponent
|
||||
},
|
||||
{
|
||||
path: 'tx/:id',
|
||||
component: TransactionComponent
|
||||
},
|
||||
{
|
||||
path: 'block/:id',
|
||||
component: BlockComponent
|
||||
},
|
||||
{
|
||||
path: 'mempool-block/:id',
|
||||
component: MempoolBlockComponent
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'blocks',
|
||||
component: LatestBlocksComponent,
|
||||
},
|
||||
{
|
||||
path: 'graphs',
|
||||
component: StatisticsComponent,
|
||||
},
|
||||
{
|
||||
path: 'address/:id',
|
||||
component: AddressComponent
|
||||
},
|
||||
{
|
||||
path: 'asset/:id',
|
||||
component: AssetComponent
|
||||
},
|
||||
{
|
||||
path: 'assets',
|
||||
component: AssetsComponent,
|
||||
},
|
||||
{
|
||||
path: 'docs/api/:type',
|
||||
component: DocsComponent
|
||||
},
|
||||
{
|
||||
path: 'docs/api',
|
||||
redirectTo: 'docs/api/rest'
|
||||
},
|
||||
{
|
||||
path: 'docs',
|
||||
redirectTo: 'docs/api/rest'
|
||||
},
|
||||
{
|
||||
path: 'api',
|
||||
redirectTo: 'docs/api/rest'
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'tv',
|
||||
component: TelevisionComponent
|
||||
},
|
||||
{
|
||||
path: 'status',
|
||||
component: StatusViewComponent
|
||||
},
|
||||
{
|
||||
path: '**',
|
||||
redirectTo: ''
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'liquid',
|
||||
children: [
|
||||
@@ -466,6 +551,94 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'testnet',
|
||||
component: LiquidMasterPageComponent,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: StartComponent,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: DashboardComponent
|
||||
},
|
||||
{
|
||||
path: 'tx/push',
|
||||
component: PushTransactionComponent,
|
||||
},
|
||||
{
|
||||
path: 'tx/:id',
|
||||
component: TransactionComponent
|
||||
},
|
||||
{
|
||||
path: 'block/:id',
|
||||
component: BlockComponent
|
||||
},
|
||||
{
|
||||
path: 'mempool-block/:id',
|
||||
component: MempoolBlockComponent
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'blocks',
|
||||
component: LatestBlocksComponent,
|
||||
},
|
||||
{
|
||||
path: 'graphs',
|
||||
component: StatisticsComponent,
|
||||
},
|
||||
{
|
||||
path: 'address/:id',
|
||||
component: AddressComponent
|
||||
},
|
||||
{
|
||||
path: 'asset/:id',
|
||||
component: AssetComponent
|
||||
},
|
||||
{
|
||||
path: 'assets',
|
||||
component: AssetsComponent,
|
||||
},
|
||||
{
|
||||
path: 'docs/api/:type',
|
||||
component: DocsComponent
|
||||
},
|
||||
{
|
||||
path: 'docs/api',
|
||||
redirectTo: 'docs/api/rest'
|
||||
},
|
||||
{
|
||||
path: 'docs',
|
||||
redirectTo: 'docs/api/rest'
|
||||
},
|
||||
{
|
||||
path: 'api',
|
||||
redirectTo: 'docs/api/rest'
|
||||
},
|
||||
{
|
||||
path: 'about',
|
||||
component: AboutComponent,
|
||||
},
|
||||
{
|
||||
path: 'terms-of-service',
|
||||
component: TermsOfServiceComponent
|
||||
},
|
||||
{
|
||||
path: 'privacy-policy',
|
||||
component: PrivacyPolicyComponent
|
||||
},
|
||||
{
|
||||
path: 'trademark-policy',
|
||||
component: TrademarkPolicyComponent
|
||||
},
|
||||
{
|
||||
path: 'sponsor',
|
||||
component: SponsorComponent,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'tv',
|
||||
component: TelevisionComponent
|
||||
|
||||
@@ -7,6 +7,7 @@ import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { merge, combineLatest, Observable } from 'rxjs';
|
||||
import { AssetExtended } from '../interfaces/electrs.interface';
|
||||
import { SeoService } from '../services/seo.service';
|
||||
import { StateService } from '../services/state.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-assets',
|
||||
@@ -15,7 +16,8 @@ import { SeoService } from '../services/seo.service';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class AssetsComponent implements OnInit {
|
||||
nativeAssetId = environment.nativeAssetId;
|
||||
nativeAssetId = this.stateService.network === 'liquidtestnet' ? environment.nativeTestAssetId : environment.nativeAssetId;
|
||||
|
||||
assets: AssetExtended[];
|
||||
assetsCache: AssetExtended[];
|
||||
searchForm: FormGroup;
|
||||
@@ -34,6 +36,7 @@ export class AssetsComponent implements OnInit {
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private seoService: SeoService,
|
||||
private stateService: StateService,
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
@@ -52,12 +55,22 @@ export class AssetsComponent implements OnInit {
|
||||
take(1),
|
||||
mergeMap(([assets, qp]) => {
|
||||
this.assets = Object.values(assets);
|
||||
// @ts-ignore
|
||||
this.assets.push({
|
||||
name: 'Liquid Bitcoin',
|
||||
ticker: 'L-BTC',
|
||||
asset_id: this.nativeAssetId,
|
||||
});
|
||||
if (this.stateService.network === 'liquid') {
|
||||
// @ts-ignore
|
||||
this.assets.push({
|
||||
name: 'Liquid Bitcoin',
|
||||
ticker: 'L-BTC',
|
||||
asset_id: this.nativeAssetId,
|
||||
});
|
||||
} else if (this.stateService.network === 'liquidtestnet') {
|
||||
// @ts-ignore
|
||||
this.assets.push({
|
||||
name: 'Test Liquid Bitcoin',
|
||||
ticker: 'tL-BTC',
|
||||
asset_id: this.nativeAssetId,
|
||||
});
|
||||
}
|
||||
|
||||
this.assets = this.assets.sort((a: any, b: any) => a.name.localeCompare(b.name));
|
||||
this.assetsCache = this.assets;
|
||||
this.searchForm.get('searchText').enable();
|
||||
|
||||
@@ -33,7 +33,7 @@ export class BisqMainDashboardComponent implements OnInit {
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.seoService.setTitle(`Markets`);
|
||||
this.seoService.resetTitle();
|
||||
this.websocketService.want(['blocks']);
|
||||
|
||||
this.usdPrice$ = this.stateService.conversions$.asObservable().pipe(
|
||||
|
||||
@@ -102,6 +102,10 @@
|
||||
<img class="image" src="/resources/profile/ronindojo.png" />
|
||||
<span>RoninDojo</span>
|
||||
</a>
|
||||
<a href="https://github.com/runcitadel/dashboard" target="_blank" title="Citadel">
|
||||
<img class="image" src="/resources/profile/runcitadel.svg" />
|
||||
<span>Citadel</span>
|
||||
</a>
|
||||
<a href="https://github.com/spesmilo/electrum" target="_blank" title="Electrum Wallet">
|
||||
<img class="image" src="/resources/profile/electrum.jpg" />
|
||||
<span>Electrum</span>
|
||||
@@ -142,10 +146,6 @@
|
||||
<img class="image" src="/resources/profile/satpile.jpg" />
|
||||
<span>Satpile</span>
|
||||
</a>
|
||||
<a href="https://github.com/btcontract/lnwallet" target="_blank" title="Bitcoin Lightning Wallet">
|
||||
<img class="image" src="/resources/profile/blw.png" />
|
||||
<span>BLW</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
img {
|
||||
box-shadow: 0px 0px 20px #1bd8f4;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
img, span{
|
||||
@@ -180,4 +180,4 @@
|
||||
|
||||
.no-about-margin {
|
||||
height: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ export class AddressComponent implements OnInit, OnDestroy {
|
||||
.pipe(
|
||||
filter((address) => !!address),
|
||||
tap((address: Address) => {
|
||||
if (this.stateService.network === 'liquid' && /^([m-zA-HJ-NP-Z1-9]{26,35}|[a-z]{2,5}1[ac-hj-np-z02-9]{8,100}|[a-km-zA-HJ-NP-Z1-9]{80})$/.test(address.address)) {
|
||||
if ((this.stateService.network === 'liquid' || this.stateService.network === 'liquidtestnet') && /^([m-zA-HJ-NP-Z1-9]{26,35}|[a-z]{2,5}1[ac-hj-np-z02-9]{8,100}|[a-km-zA-HJ-NP-Z1-9]{80})$/.test(address.address)) {
|
||||
this.apiService.validateAddress$(address.address)
|
||||
.subscribe((addressInfo) => {
|
||||
this.addressInfo = addressInfo;
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
<span class="fiat">{{ conversions.USD * (satoshis / 100000000) | currency:'USD':'symbol':'1.2-2' }}</span>
|
||||
</ng-container>
|
||||
<ng-template #viewFiatVin>
|
||||
<ng-template [ngIf]="network === 'liquid' && (satoshis === undefined || satoshis === null)" [ngIfElse]="default">
|
||||
<ng-template [ngIf]="(network === 'liquid' || network === 'liquidtestnet') && (satoshis === undefined || satoshis === null)" [ngIfElse]="default">
|
||||
<span i18n="shared.confidential">Confidential</span>
|
||||
</ng-template>
|
||||
<ng-template #default>
|
||||
‎{{ satoshis / 100000000 | number : digitsInfo }}
|
||||
<span class="symbol"><ng-template [ngIf]="network === 'liquid'">L-</ng-template>
|
||||
<ng-template [ngIf]="network === 'liquidtestnet'">tL-</ng-template>
|
||||
<ng-template [ngIf]="network === 'testnet'">t</ng-template>
|
||||
<ng-template [ngIf]="network === 'signet'">s</ng-template>BTC</span>
|
||||
</ng-template>
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<td i18n="asset.issuer|Liquid Asset issuer">Issuer</td>
|
||||
<td><a target="_blank" href="{{ 'http://' + assetContract[0] }}">{{ assetContract[0] }}</a></td>
|
||||
</tr>
|
||||
<tr *ngIf="!isNativeAsset">
|
||||
<tr *ngIf="asset.issuance_txin">
|
||||
<td i18n="asset.issuance-tx|Liquid Asset issuance TX">Issuance TX</td>
|
||||
<td><a [routerLink]="['/tx/' | relativeUrl, asset.issuance_txin.txid]">{{ asset.issuance_txin.txid | shortenString : 13 }}</a> <app-clipboard class="d-none d-sm-inline-block" [text]="asset.issuance_txin.txid"></app-clipboard></td>
|
||||
</tr>
|
||||
@@ -42,15 +42,15 @@
|
||||
<div class="col">
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr *ngIf="isNativeAsset">
|
||||
<tr *ngIf="isNativeAsset && asset.chain_stats.peg_in_amount">
|
||||
<td i18n="asset.pegged-in|Liquid Asset pegged-in amount">Pegged in</td>
|
||||
<td>{{ formatAmount(asset.chain_stats.peg_in_amount, assetContract[3]) | number: '1.0-' + assetContract[3] }} {{ assetContract[1] }}</td>
|
||||
</tr>
|
||||
<tr *ngIf="isNativeAsset">
|
||||
<tr *ngIf="isNativeAsset && asset.chain_stats.peg_out_amount">
|
||||
<td i18n="asset.pegged-out|Liquid Asset pegged-out amount">Pegged out</td>
|
||||
<td>{{ formatAmount(asset.chain_stats.peg_out_amount, assetContract[3]) | number: '1.0-' + assetContract[3] }} {{ assetContract[1] }}</td>
|
||||
</tr>
|
||||
<tr *ngIf="!isNativeAsset">
|
||||
<tr *ngIf="asset.chain_stats.issued_amount">
|
||||
<td i18n="asset.issued-amount|Liquid Asset issued amount">Issued amount</td>
|
||||
<td *ngIf="!blindedIssuance; else confidentialTd">{{ formatAmount(asset.chain_stats.issued_amount, assetContract[3]) | number: '1.0-' + assetContract[3] }} {{ assetContract[1] }}</td>
|
||||
</tr>
|
||||
@@ -58,11 +58,11 @@
|
||||
<td i18n="asset.burned-amount|Liquid Asset burned amount">Burned amount</td>
|
||||
<td *ngIf="!blindedIssuance; else confidentialTd">{{ formatAmount(asset.chain_stats.burned_amount, assetContract[3]) | number: '1.0-' + assetContract[3] }} {{ assetContract[1] }}</td>
|
||||
</tr>
|
||||
<tr *ngIf="!isNativeAsset">
|
||||
<tr *ngIf="asset.chain_stats.issued_amount">
|
||||
<td i18n="asset.circulating-amount|Liquid Asset circulating amount">Circulating amount</td>
|
||||
<td *ngIf="!blindedIssuance; else confidentialTd">{{ formatAmount(asset.chain_stats.issued_amount - asset.chain_stats.burned_amount, assetContract[3]) | number: '1.0-' + assetContract[3] }} {{ assetContract[1] }}</td>
|
||||
</tr>
|
||||
<tr *ngIf="isNativeAsset">
|
||||
<tr *ngIf="isNativeAsset && asset.chain_stats.peg_in_amount">
|
||||
<td i18n="asset.circulating-amount|Liquid Asset circulating amount">Circulating amount</td>
|
||||
<td>{{ formatAmount(asset.chain_stats.peg_in_amount - asset.chain_stats.burned_amount - asset.chain_stats.peg_out_amount, assetContract[3]) | number: '1.0-' + assetContract[3] }} {{ assetContract[1] }}</td>
|
||||
</tr>
|
||||
@@ -78,7 +78,7 @@
|
||||
<div class="title-tx">
|
||||
<h2>
|
||||
<ng-template [ngIf]="transactions?.length" i18n="asset.M_of_N">{{ (transactions?.length | number) || '?' }} of {{ txCount | number }} </ng-template>
|
||||
<ng-template [ngIf]="isNativeAsset" [ngIfElse]="defaultAsset" i18n="Liquid native asset transactions title">Peg In/Out and Burn Transactions</ng-template>
|
||||
<ng-template [ngIf]="isNativeAsset && network === 'liquid'" [ngIfElse]="defaultAsset" i18n="Liquid native asset transactions title">Peg In/Out and Burn Transactions</ng-template>
|
||||
<ng-template #defaultAsset i18n="Default asset transactions title">Issuance and Burn Transactions</ng-template>
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
@@ -20,7 +20,7 @@ import { moveDec } from 'src/app/bitcoin.utils';
|
||||
})
|
||||
export class AssetComponent implements OnInit, OnDestroy {
|
||||
network = '';
|
||||
nativeAssetId = environment.nativeAssetId;
|
||||
nativeAssetId = this.stateService.network === 'liquidtestnet' ? environment.nativeTestAssetId : environment.nativeAssetId;
|
||||
|
||||
asset: Asset;
|
||||
blindedIssuance: boolean;
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
</ng-container>
|
||||
</a>
|
||||
|
||||
<div ngbDropdown (window:resize)="onResize($event)" class="dropdown-container" *ngIf="env.TESTNET_ENABLED || env.SIGNET_ENABLED || env.LIQUID_ENABLED || env.BISQ_ENABLED">
|
||||
<div ngbDropdown (window:resize)="onResize($event)" class="dropdown-container" *ngIf="env.TESTNET_ENABLED || env.SIGNET_ENABLED || env.LIQUID_ENABLED || env.BISQ_ENABLED || env.LIQUID_TESTNET_ENABLED">
|
||||
<button ngbDropdownToggle type="button" class="btn btn-secondary dropdown-toggle-split" aria-haspopup="true">
|
||||
<img src="./resources/bisq-logo.png" style="width: 25px; height: 25px;" class="mr-1">
|
||||
</button>
|
||||
@@ -21,6 +21,7 @@
|
||||
<h6 class="dropdown-header" i18n="master-page.layer2-networks-header">Layer 2 Networks</h6>
|
||||
<button ngbDropdownItem class="mainnet active" routerLink="/"><img src="./resources/bisq-logo.png" style="width: 30px;" class="mr-1"> Bisq</button>
|
||||
<a href="https://liquid.network" ngbDropdownItem *ngIf="env.LIQUID_ENABLED" class="liquid"><img src="./resources/liquid-logo.png" style="width: 30px;" class="mr-1"> Liquid</a>
|
||||
<a href="https://liquid.network/testnet" ngbDropdownItem *ngIf="env.LIQUID_TESTNET_ENABLED" class="liquidtestnet"><img src="./resources/liquidtestnet-logo.png" style="width: 30px;" class="mr-1"> Liquid Testnet</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -102,6 +102,10 @@ nav {
|
||||
background-color: #116761;
|
||||
}
|
||||
|
||||
.liquidtestnet.active {
|
||||
background-color: #494a4a;
|
||||
}
|
||||
|
||||
.testnet.active {
|
||||
background-color: #1d486f;
|
||||
}
|
||||
|
||||
@@ -81,12 +81,12 @@
|
||||
<ng-template [ngIf]="fees !== undefined" [ngIfElse]="loadingFees">
|
||||
<tr>
|
||||
<td i18n="block.total-fees|Total fees in a block">Total fees</td>
|
||||
<td *ngIf="network !== 'liquid'; else liquidTotalFees"><app-amount [satoshis]="fees * 100000000" digitsInfo="1.2-2" [noFiat]="true"></app-amount> <span class="fiat"><app-fiat [value]="fees * 100000000" digitsInfo="1.0-0"></app-fiat></span></td>
|
||||
<td *ngIf="network !== 'liquid' && network !== 'liquidtestnet'; else liquidTotalFees"><app-amount [satoshis]="fees * 100000000" digitsInfo="1.2-2" [noFiat]="true"></app-amount> <span class="fiat"><app-fiat [value]="fees * 100000000" digitsInfo="1.0-0"></app-fiat></span></td>
|
||||
<ng-template #liquidTotalFees>
|
||||
<td>{{ fees * 100000000 | number }} L-sat (<app-fiat [value]="fees * 100000000" digitsInfo="1.2-2"></app-fiat>)</td>
|
||||
<td><app-amount [satoshis]="fees * 100000000" digitsInfo="1.2-2" [noFiat]="true"></app-amount> <app-fiat [value]="fees * 100000000" digitsInfo="1.2-2"></app-fiat></td>
|
||||
</ng-template>
|
||||
</tr>
|
||||
<tr *ngIf="network !== 'liquid'">
|
||||
<tr *ngIf="network !== 'liquid' && network !== 'liquidtestnet'">
|
||||
<td i18n="block.subsidy-and-fees|Total subsidy and fees in a block">Subsidy + fees:</td>
|
||||
<td>
|
||||
<app-amount [satoshis]="(blockSubsidy + fees) * 100000000" digitsInfo="1.2-2" [noFiat]="true"></app-amount> <span class="fiat"><app-fiat [value]="(blockSubsidy + fees) * 100000000" digitsInfo="1.0-0"></app-fiat></span>
|
||||
@@ -98,7 +98,7 @@
|
||||
<td i18n="block.total-fees|Total fees in a block">Total fees</td>
|
||||
<td style="width: 75%;"><span class="skeleton-loader"></span></td>
|
||||
</tr>
|
||||
<tr *ngIf="network !== 'liquid'">
|
||||
<tr *ngIf="network !== 'liquid' && network !== 'liquidtestnet'">
|
||||
<td i18n="block.subsidy-and-fees|Total subsidy and fees in a block">Subsidy + fees:</td>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
</tr>
|
||||
@@ -125,7 +125,7 @@
|
||||
<td class="td-width" i18n="transaction.version">Version</td>
|
||||
<td>{{ block.version | decimal2hex }} <span *ngIf="displayTaprootStatus() && hasTaproot(block.version)" class="badge badge-success ml-1" >Taproot</span></td>
|
||||
</tr>
|
||||
<tr *ngIf="network !== 'liquid'">
|
||||
<tr *ngIf="network !== 'liquid' && network !== 'liquidtestnet'">
|
||||
<td i18n="block.bits">Bits</td>
|
||||
<td>{{ block.bits | decimal2hex }}</td>
|
||||
</tr>
|
||||
@@ -136,7 +136,7 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-sm" *ngIf="network !== 'liquid'">
|
||||
<div class="col-sm" *ngIf="network !== 'liquid' && network !== 'liquidtestnet'">
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
|
||||
@@ -8,10 +8,12 @@ import { Observable, of, Subscription } from 'rxjs';
|
||||
import { StateService } from '../../services/state.service';
|
||||
import { SeoService } from 'src/app/services/seo.service';
|
||||
import { WebsocketService } from 'src/app/services/websocket.service';
|
||||
import { RelativeUrlPipe } from 'src/app/shared/pipes/relative-url/relative-url.pipe';
|
||||
|
||||
@Component({
|
||||
selector: 'app-block',
|
||||
templateUrl: './block.component.html',
|
||||
providers: [RelativeUrlPipe],
|
||||
styleUrls: ['./block.component.scss']
|
||||
})
|
||||
export class BlockComponent implements OnInit, OnDestroy {
|
||||
@@ -51,6 +53,7 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||
private stateService: StateService,
|
||||
private seoService: SeoService,
|
||||
private websocketService: WebsocketService,
|
||||
private relativeUrlPipe: RelativeUrlPipe,
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
@@ -194,7 +197,7 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||
if (this.showNextBlocklink) {
|
||||
this.navigateToNextBlock();
|
||||
} else {
|
||||
this.router.navigate([(this.network && this.stateService.env.BASE_MODULE === 'mempool' ? '/' + this.network : '') + '/mempool-block/', '0']);
|
||||
this.router.navigate([this.relativeUrlPipe.transform('/mempool-block'), '0']);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -210,7 +213,7 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
setBlockSubsidy() {
|
||||
if (this.network === 'liquid') {
|
||||
if (this.network === 'liquid' || this.network === 'liquidtestnet') {
|
||||
this.blockSubsidy = 0;
|
||||
return;
|
||||
}
|
||||
@@ -277,13 +280,13 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||
return;
|
||||
}
|
||||
const block = this.latestBlocks.find((b) => b.height === this.nextBlockHeight - 2);
|
||||
this.router.navigate([(this.network && this.stateService.env.BASE_MODULE === 'mempool' ? '/' + this.network : '') + '/block/',
|
||||
this.router.navigate([this.relativeUrlPipe.transform('/block/'),
|
||||
block ? block.id : this.block.previousblockhash], { state: { data: { block, blockHeight: this.nextBlockHeight - 2 } } });
|
||||
}
|
||||
|
||||
navigateToNextBlock() {
|
||||
const block = this.latestBlocks.find((b) => b.height === this.nextBlockHeight);
|
||||
this.router.navigate([(this.network && this.stateService.env.BASE_MODULE === 'mempool' ? '/' + this.network : '') + '/block/',
|
||||
this.router.navigate([this.relativeUrlPipe.transform('/block/'),
|
||||
block ? block.id : this.nextBlockHeight], { state: { data: { block, blockHeight: this.nextBlockHeight } } });
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ export class BlockchainBlocksComponent implements OnInit, OnDestroy {
|
||||
'': ['#9339f4', '#105fb0'],
|
||||
bisq: ['#9339f4', '#105fb0'],
|
||||
liquid: ['#116761', '#183550'],
|
||||
'liquidtestnet': ['#494a4a', '#272e46'],
|
||||
testnet: ['#1d486f', '#183550'],
|
||||
signet: ['#6f1d5d', '#471850'],
|
||||
};
|
||||
@@ -47,7 +48,7 @@ export class BlockchainBlocksComponent implements OnInit, OnDestroy {
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
if (this.stateService.network === 'liquid') {
|
||||
if (this.stateService.network === 'liquid' || this.stateService.network === 'liquidtestnet') {
|
||||
this.feeRounding = '1.0-1';
|
||||
}
|
||||
this.emptyBlocks.forEach((b) => this.emptyBlockStyles.push(this.getStyleForEmptyBlock(b)));
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
top: 75px;
|
||||
}
|
||||
|
||||
.position-container.liquid {
|
||||
.position-container.liquid, .position-container.liquidtestnet {
|
||||
left: 420px;
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
.position-container {
|
||||
left: 95%;
|
||||
}
|
||||
.position-container.liquid {
|
||||
.position-container.liquid, .position-container.liquidtestnet {
|
||||
left: 50%;
|
||||
}
|
||||
.position-container.loading {
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-address-transactions-mempool" (click)="collapseItem.toggle()">GET Address Transactions Mempool</a>
|
||||
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-address-utxo" (click)="collapseItem.toggle()">GET Address UTXO</a>
|
||||
|
||||
<ng-template [ngIf]="network.val === 'liquid'">
|
||||
<ng-template [ngIf]="network.val === 'liquid' || network.val === 'liquidtestnet'">
|
||||
<p>Assets</p>
|
||||
<a [routerLink]="['./']" fragment="get-assets" (click)="collapseItem.toggle()">GET Assets</a>
|
||||
<a [routerLink]="['./']" fragment="get-assets-icons" (click)="collapseItem.toggle()">GET Assets Icons</a>
|
||||
@@ -66,7 +66,7 @@
|
||||
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-cpfp" (click)="collapseItem.toggle()">GET Children Pay for Parent</a>
|
||||
<a [routerLink]="['./']" fragment="get-transaction" (click)="collapseItem.toggle()">GET Transaction</a>
|
||||
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-transaction-hex" (click)="collapseItem.toggle()">GET Transaction Hex</a>
|
||||
<a *ngIf="network.val !== 'bisq' && network.val !== 'liquid'" [routerLink]="['./']" fragment="get-transaction-merkleblock-proof" (click)="collapseItem.toggle()">GET Transaction Merkleblock Proof</a>
|
||||
<a *ngIf="network.val !== 'bisq' && network.val !== 'liquid' && network.val !== 'liquidtestnet'" [routerLink]="['./']" fragment="get-transaction-merkleblock-proof" (click)="collapseItem.toggle()">GET Transaction Merkleblock Proof</a>
|
||||
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-transaction-merkle-proof" (click)="collapseItem.toggle()">GET Transaction Merkle Proof</a>
|
||||
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-transaction-outspend" (click)="collapseItem.toggle()">GET Transaction Outspend</a>
|
||||
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-transaction-outspends" (click)="collapseItem.toggle()">GET Transaction Outspends</a>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
<div id="mobile-top-doc-nav" #mobileFixedApiNav class="hide-on-desktop"><app-api-docs-nav [network]="{ val: network$ | async }"></app-api-docs-nav></div>
|
||||
|
||||
<div class="api-category" *ngIf="network.val !== 'bisq' && network.val !== 'liquid'">
|
||||
<div class="api-category" *ngIf="network.val !== 'bisq' && network.val !== 'liquid' && network.val !== 'liquidtestnet'">
|
||||
|
||||
<div class="endpoint-container" id="get-difficulty-adjustment">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-difficulty-adjustment">GET Difficulty Adjustment <span>General</span></a>
|
||||
@@ -228,14 +228,14 @@
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Get the list of unspent transaction outputs associated with the address/scripthash. Available fields: <code>txid</code>, <code>vout</code>, <code>value</code>, and <code>status</code> (with the status of the funding tx).<ng-container *ngIf="network.val === 'liquid'">There is also a <code>valuecommitment</code> field that may appear in place of <code>value</code>, plus the following additional fields: <code>asset</code>/<code>assetcommitment</code>, <code>nonce</code>/<code>noncecommitment</code>, <code>surjection_proof</code>, and <code>range_proof</code>.</ng-container></div>
|
||||
<div i18n>Get the list of unspent transaction outputs associated with the address/scripthash. Available fields: <code>txid</code>, <code>vout</code>, <code>value</code>, and <code>status</code> (with the status of the funding tx).<ng-container *ngIf="network.val === 'liquid' || network.val === 'liquidtestnet'">There is also a <code>valuecommitment</code> field that may appear in place of <code>value</code>, plus the following additional fields: <code>asset</code>/<code>assetcommitment</code>, <code>nonce</code>/<code>noncecommitment</code>, <code>surjection_proof</code>, and <code>range_proof</code>.</ng-container></div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.addressUTXO" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="api-category" *ngIf="network.val === 'liquid'">
|
||||
<div class="api-category" *ngIf="network.val === 'liquid' || network.val === 'liquidtestnet'">
|
||||
|
||||
<div class="endpoint-container" id="get-assets">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-assets">GET Assets <span>Assets</span></a>
|
||||
@@ -313,7 +313,7 @@
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns details about a block. Available fields: <code>id</code>, <code>height</code>, <code>version</code>, <code>timestamp</code>, <code>bits</code>, <code>nonce</code>, <code>merkle_root</code>, <code>tx_count</code>, <code>size</code>, <code>weight</code>,<ng-container *ngIf="network.val === 'liquid'"> <code>proof</code>,</ng-container> and <code>previousblockhash</code>.</div>
|
||||
<div i18n>Returns details about a block. Available fields: <code>id</code>, <code>height</code>, <code>version</code>, <code>timestamp</code>, <code>bits</code>, <code>nonce</code>, <code>merkle_root</code>, <code>tx_count</code>, <code>size</code>, <code>weight</code>,<ng-container *ngIf="network.val === 'liquid' || network.val === 'liquidtestnet'"> <code>proof</code>,</ng-container> and <code>previousblockhash</code>.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.block" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
@@ -577,7 +577,7 @@
|
||||
<app-code-template [hostname]="hostname" [code]="code.transactionHex" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
<div class="endpoint-container" *ngIf="network.val !== 'bisq' && network.val !== 'liquid'" id="get-transaction-merkleblock-proof">
|
||||
<div class="endpoint-container" *ngIf="network.val !== 'bisq' && network.val !== 'liquid' && network.val !== 'liquidtestnet'" id="get-transaction-merkleblock-proof">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-transaction-merkleblock-proof">GET Transaction Merkleblock Proof <span>Transactions</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
|
||||
@@ -3663,7 +3663,7 @@ export class ApiDocsComponent implements OnInit {
|
||||
};
|
||||
|
||||
this.network$.subscribe((network) => {
|
||||
this.active = (network === 'liquid') ? 2 : 0;
|
||||
this.active = (network === 'liquid' || network === 'liquidtestnet') ? 2 : 0;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3679,7 +3679,7 @@ export class ApiDocsComponent implements OnInit {
|
||||
if (network === 'signet') {
|
||||
curlResponse = code.codeSampleSignet.curl;
|
||||
}
|
||||
if (network === 'liquid') {
|
||||
if (network === 'liquid' || network === 'liquidtestnet') {
|
||||
curlResponse = code.codeSampleLiquid.curl;
|
||||
}
|
||||
if (network === 'bisq') {
|
||||
|
||||
@@ -26,7 +26,7 @@ export class CodeTemplateComponent implements OnInit {
|
||||
if (this.network === 'bisq') {
|
||||
npmLink = `https://github.com/mempool/mempool.js/tree/main/npm-bisq-js`;
|
||||
}
|
||||
if (this.network === 'liquid') {
|
||||
if (this.network === 'liquid' || this.network === 'liquidtestnet') {
|
||||
npmLink = `https://github.com/mempool/mempool.js/tree/main/npm-liquid-js`;
|
||||
}
|
||||
return npmLink;
|
||||
@@ -37,7 +37,7 @@ export class CodeTemplateComponent implements OnInit {
|
||||
if (this.network === 'bisq') {
|
||||
npmLink = `https://www.npmjs.org/package/@mempool/bisq.js`;
|
||||
}
|
||||
if (this.network === 'liquid') {
|
||||
if (this.network === 'liquid' || this.network === 'liquidtestnet') {
|
||||
npmLink = `https://www.npmjs.org/package/@mempool/liquid.js`;
|
||||
}
|
||||
return npmLink;
|
||||
@@ -50,7 +50,7 @@ export class CodeTemplateComponent implements OnInit {
|
||||
} else {
|
||||
codeText = codeText.replace('%{0}', 'bitcoin');
|
||||
}
|
||||
if(['', 'main', 'liquid', 'bisq'].includes(this.network)) {
|
||||
if(['', 'main', 'liquid', 'bisq', 'liquidtestnet'].includes(this.network)) {
|
||||
codeText = codeText.replace('mempoolJS();', `mempoolJS({
|
||||
hostname: '${document.location.hostname}'
|
||||
});`);
|
||||
@@ -119,7 +119,7 @@ export class CodeTemplateComponent implements OnInit {
|
||||
if (this.network === 'signet') {
|
||||
codeText = this.replaceJSPlaceholder(codeText, code.codeSampleSignet.esModule);
|
||||
}
|
||||
if (this.network === 'liquid') {
|
||||
if (this.network === 'liquid' || this.network === 'liquidtestnet') {
|
||||
codeText = this.replaceJSPlaceholder(codeText, code.codeSampleLiquid.esModule);
|
||||
}
|
||||
if (this.network === 'bisq') {
|
||||
@@ -157,7 +157,7 @@ init();`;
|
||||
if (this.network === 'signet') {
|
||||
codeText = this.replaceJSPlaceholder(codeText, code.codeSampleSignet.esModule);
|
||||
}
|
||||
if (this.network === 'liquid') {
|
||||
if (this.network === 'liquid' || this.network === 'liquidtestnet') {
|
||||
codeText = this.replaceJSPlaceholder(codeText, code.codeSampleLiquid.esModule);
|
||||
}
|
||||
if (this.network === 'bisq') {
|
||||
@@ -237,7 +237,7 @@ yarn add @mempool/liquid.js`;
|
||||
if (this.network === 'signet') {
|
||||
return this.replaceCurlPlaceholder(code.codeTemplate.curl, code.codeSampleSignet);
|
||||
}
|
||||
if (this.network === 'liquid') {
|
||||
if (this.network === 'liquid' || this.network === 'liquidtestnet') {
|
||||
return this.replaceCurlPlaceholder(code.codeTemplate.curl, code.codeSampleLiquid);
|
||||
}
|
||||
if (this.network === 'bisq') {
|
||||
@@ -259,7 +259,7 @@ yarn add @mempool/liquid.js`;
|
||||
if (this.network === 'signet') {
|
||||
return code.codeSampleSignet.response;
|
||||
}
|
||||
if (this.network === 'liquid') {
|
||||
if (this.network === 'liquid' || this.network === 'liquidtestnet') {
|
||||
return code.codeSampleLiquid.response;
|
||||
}
|
||||
if (this.network === 'bisq') {
|
||||
|
||||
@@ -26,7 +26,7 @@ export class FeesBoxComponent implements OnInit {
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.defaultFee = this.stateService.network === 'liquid' ? 0.1 : 1;
|
||||
this.defaultFee = this.stateService.network === 'liquid' || this.stateService.network === 'liquidtestnet' ? 0.1 : 1;
|
||||
|
||||
this.isLoadingWebSocket$ = this.stateService.isLoadingWebSocket$;
|
||||
this.feeEstimations$ = this.stateService.mempoolBlocks$
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<ng-container *ngIf="{ val: network$ | async } as network">
|
||||
<header>
|
||||
<nav class="navbar navbar-expand-md navbar-dark bg-dark">
|
||||
<a class="navbar-brand" [routerLink]="['/' | relativeUrl]" style="position: relative;">
|
||||
@@ -10,9 +11,9 @@
|
||||
</ng-container>
|
||||
</a>
|
||||
|
||||
<div ngbDropdown (window:resize)="onResize($event)" class="dropdown-container" *ngIf="env.TESTNET_ENABLED || env.SIGNET_ENABLED || env.LIQUID_ENABLED || env.BISQ_ENABLED">
|
||||
<div ngbDropdown (window:resize)="onResize($event)" class="dropdown-container" *ngIf="env.TESTNET_ENABLED || env.SIGNET_ENABLED || env.LIQUID_ENABLED || env.BISQ_ENABLED || env.LIQUID_TESTNET_ENABLED">
|
||||
<button ngbDropdownToggle type="button" class="btn btn-secondary dropdown-toggle-split" aria-haspopup="true">
|
||||
<img src="./resources/liquid-logo.png" style="width: 25px; height: 25px;" class="mr-1">
|
||||
<img src="./resources/{{ network.val === '' ? 'liquid' : network.val }}-logo.png" style="width: 25px; height: 25px;" class="mr-1">
|
||||
</button>
|
||||
<div ngbDropdownMenu [ngClass]="{'dropdown-menu-right' : isMobile}">
|
||||
<a href="https://mempool.space" ngbDropdownItem class="mainnet"><img src="./resources/bitcoin-logo.png" style="width: 30px;" class="mr-1"> Mainnet</a>
|
||||
@@ -20,12 +21,13 @@
|
||||
<a href="https://mempool.space/testnet" ngbDropdownItem *ngIf="env.TESTNET_ENABLED" class="testnet"><img src="./resources/testnet-logo.png" style="width: 30px;" class="mr-1"> Testnet</a>
|
||||
<h6 class="dropdown-header" i18n="master-page.layer2-networks-header">Layer 2 Networks</h6>
|
||||
<a href="https://bisq.markets" ngbDropdownItem class="mainnet"><img src="./resources/bisq-logo.png" style="width: 30px;" class="mr-1"> Bisq</a>
|
||||
<button ngbDropdownItem class="liquid active" routerLink="/"><img src="./resources/liquid-logo.png" style="width: 30px;" class="mr-1"> Liquid</button>
|
||||
<button ngbDropdownItem class="liquid" [class.active]="network.val === 'liquid'" routerLink="/"><img src="./resources/liquid-logo.png" style="width: 30px;" class="mr-1"> Liquid</button>
|
||||
<button ngbDropdownItem *ngIf="env.LIQUID_TESTNET_ENABLED" class="liquidtestnet" [class.active]="network.val === 'liquidtestnet'" routerLink="/testnet"><img src="./resources/liquidtestnet-logo.png" style="width: 30px;" class="mr-1"> Liquid Testnet</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="navbar-collapse" id="navbarCollapse">
|
||||
<ul class="navbar-nav liquid">
|
||||
<ul class="navbar-nav {{ network.val }}">
|
||||
<li class="nav-item" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}">
|
||||
<a class="nav-link" [routerLink]="['/' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'tachometer-alt']" [fixedWidth]="true" i18n-title="master-page.dashboard" title="Dashboard"></fa-icon></a>
|
||||
</li>
|
||||
@@ -41,7 +43,7 @@
|
||||
</li>
|
||||
-->
|
||||
<li class="nav-item" routerLinkActive="active">
|
||||
<a class="nav-link" [routerLink]="['/assets']" (click)="collapse()"><fa-icon [icon]="['fas', 'database']" [fixedWidth]="true" i18n-title="master-page.assets" title="Assets"></fa-icon></a>
|
||||
<a class="nav-link" [routerLink]="['/assets' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'database']" [fixedWidth]="true" i18n-title="master-page.assets" title="Assets"></fa-icon></a>
|
||||
</li>
|
||||
<li [hidden]="isMobile" class="nav-item mr-2" routerLinkActive="active">
|
||||
<a class="nav-link" [routerLink]="['/docs' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'book']" [fixedWidth]="true" i18n-title="master-page.docs" title="Docs"></fa-icon></a>
|
||||
@@ -60,3 +62,4 @@
|
||||
<router-outlet></router-outlet>
|
||||
|
||||
<br>
|
||||
</ng-container>
|
||||
@@ -102,6 +102,10 @@ nav {
|
||||
background-color: #116761;
|
||||
}
|
||||
|
||||
.liquidtestnet.active {
|
||||
background-color: #494a4a;
|
||||
}
|
||||
|
||||
.testnet.active {
|
||||
background-color: #1d486f;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Env, StateService } from '../../services/state.service';
|
||||
import { Observable} from 'rxjs';
|
||||
import { merge, Observable, of} from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'app-liquid-master-page',
|
||||
@@ -13,6 +13,7 @@ export class LiquidMasterPageComponent implements OnInit {
|
||||
navCollapsed = false;
|
||||
isMobile = window.innerWidth <= 767.98;
|
||||
officialMempoolSpace = this.stateService.env.OFFICIAL_MEMPOOL_SPACE;
|
||||
network$: Observable<string>;
|
||||
|
||||
constructor(
|
||||
private stateService: StateService,
|
||||
@@ -21,6 +22,7 @@ export class LiquidMasterPageComponent implements OnInit {
|
||||
ngOnInit() {
|
||||
this.env = this.stateService.env;
|
||||
this.connectionState$ = this.stateService.connectionState$;
|
||||
this.network$ = merge(of(''), this.stateService.networkChanged$);
|
||||
}
|
||||
|
||||
collapse(): void {
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</ng-container>
|
||||
</a>
|
||||
|
||||
<div (window:resize)="onResize($event)" ngbDropdown class="dropdown-container" *ngIf="env.TESTNET_ENABLED || env.SIGNET_ENABLED || env.LIQUID_ENABLED || env.BISQ_ENABLED">
|
||||
<div (window:resize)="onResize($event)" ngbDropdown class="dropdown-container" *ngIf="env.TESTNET_ENABLED || env.SIGNET_ENABLED || env.LIQUID_ENABLED || env.BISQ_ENABLED || env.LIQUID_TESTNET_ENABLED">
|
||||
<button ngbDropdownToggle type="button" class="btn btn-secondary dropdown-toggle-split" aria-haspopup="true">
|
||||
<img src="./resources/{{ network.val === '' ? 'bitcoin' : network.val }}-logo.png" style="width: 25px; height: 25px;" class="mr-1">
|
||||
</button>
|
||||
@@ -24,6 +24,8 @@
|
||||
<button ngbDropdownItem *ngIf="env.BISQ_ENABLED && !env.OFFICIAL_MEMPOOL_SPACE" class="bisq" [class.active]="network.val === 'bisq'" routerLink="/bisq"><img src="./resources/bisq-logo.png" style="width: 30px;" class="mr-1"> Bisq</button>
|
||||
<a href="https://liquid.network" ngbDropdownItem *ngIf="env.LIQUID_ENABLED && env.OFFICIAL_MEMPOOL_SPACE" class="liquid" [class.active]="network.val === 'liquid'"><img src="./resources/liquid-logo.png" style="width: 30px;" class="mr-1"> Liquid</a>
|
||||
<button ngbDropdownItem *ngIf="env.LIQUID_ENABLED && !env.OFFICIAL_MEMPOOL_SPACE" class="liquid" [class.active]="network.val === 'liquid'" routerLink="/liquid"><img src="./resources/liquid-logo.png" style="width: 30px;" class="mr-1"> Liquid</button>
|
||||
<a href="https://liquid.network/testnet" ngbDropdownItem *ngIf="env.LIQUID_TESTNET_ENABLED && env.OFFICIAL_MEMPOOL_SPACE" class="liquidtestnet" [class.active]="network.val === 'liquid'"><img src="./resources/liquidtestnet-logo.png" style="width: 30px;" class="mr-1"> Liquid Testnet</a>
|
||||
<button ngbDropdownItem *ngIf="env.LIQUID_TESTNET_ENABLED && !env.OFFICIAL_MEMPOOL_SPACE" class="liquidtestnet" [class.active]="network.val === 'liquidtestnet'" routerLink="/liquidtestnet"><img src="./resources/liquidtestnet-logo.png" style="width: 30px;" class="mr-1"> Liquid Testnet</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -54,8 +56,8 @@
|
||||
<a class="nav-link" [routerLink]="['/tv' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'tv']" [fixedWidth]="true" i18n-title="master-page.tvview" title="TV view"></fa-icon></a>
|
||||
</li>
|
||||
</ng-template>
|
||||
<li *ngIf="network.val === 'liquid'" class="nav-item" routerLinkActive="active">
|
||||
<a class="nav-link" [routerLink]="['/liquid/assets']" (click)="collapse()"><fa-icon [icon]="['fas', 'database']" [fixedWidth]="true" i18n-title="master-page.assets" title="Assets"></fa-icon></a>
|
||||
<li *ngIf="network.val === 'liquid' || network.val === 'liquidtestnet'" class="nav-item" routerLinkActive="active">
|
||||
<a class="nav-link" [routerLink]="['/assets' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'database']" [fixedWidth]="true" i18n-title="master-page.assets" title="Assets"></fa-icon></a>
|
||||
</li>
|
||||
<li class="nav-item" routerLinkActive="active">
|
||||
<a class="nav-link" [routerLink]="['/docs' | relativeUrl ]" (click)="collapse()"><fa-icon [icon]="['fas', 'book']" [fixedWidth]="true" i18n-title="documentation.title" title="Documentation"></fa-icon></a>
|
||||
|
||||
@@ -110,6 +110,10 @@ nav {
|
||||
background-color: #116761;
|
||||
}
|
||||
|
||||
.liquidtestnet.active {
|
||||
background-color: #494a4a;
|
||||
}
|
||||
|
||||
.testnet.active {
|
||||
background-color: #1d486f;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<ng-template #transactionsPlural let-i i18n="shared.transaction-count.plural">{{ i }} transactions</ng-template>
|
||||
</div>
|
||||
<div class="time-difference" *ngIf="projectedBlock.blockVSize <= stateService.blockVSize; else mergedBlock">
|
||||
<ng-template [ngIf]="network === 'liquid'" [ngIfElse]="timeDiffMainnet">
|
||||
<ng-template [ngIf]="network === 'liquid' || network === 'liquidtestnet'" [ngIfElse]="timeDiffMainnet">
|
||||
<app-time-until [time]="(1 * i) + now + 61000" [fastRender]="false" [fixedRender]="true"></app-time-until>
|
||||
</ng-template>
|
||||
<ng-template #timeDiffMainnet>
|
||||
|
||||
@@ -6,11 +6,13 @@ import { Router } from '@angular/router';
|
||||
import { take, map, switchMap } from 'rxjs/operators';
|
||||
import { feeLevels, mempoolFeeColors } from 'src/app/app.constants';
|
||||
import { specialBlocks } from 'src/app/app.constants';
|
||||
import { RelativeUrlPipe } from 'src/app/shared/pipes/relative-url/relative-url.pipe';
|
||||
|
||||
@Component({
|
||||
selector: 'app-mempool-blocks',
|
||||
templateUrl: './mempool-blocks.component.html',
|
||||
styleUrls: ['./mempool-blocks.component.scss'],
|
||||
providers: [RelativeUrlPipe],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class MempoolBlocksComponent implements OnInit, OnDestroy {
|
||||
@@ -52,10 +54,11 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
|
||||
private router: Router,
|
||||
public stateService: StateService,
|
||||
private cd: ChangeDetectorRef,
|
||||
private relativeUrlPipe: RelativeUrlPipe,
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
if (this.stateService.network === 'liquid') {
|
||||
if (this.stateService.network === 'liquid' || this.stateService.network === 'liquidtestnet') {
|
||||
this.feeRounding = '1.0-1';
|
||||
}
|
||||
this.mempoolEmptyBlocks.forEach((b) => {
|
||||
@@ -166,19 +169,19 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
|
||||
|
||||
if (event.key === 'ArrowRight') {
|
||||
if (this.mempoolBlocks[this.markIndex - 1]) {
|
||||
this.router.navigate([(this.network ? '/' + this.network : '') + '/mempool-block/', this.markIndex - 1]);
|
||||
this.router.navigate([this.relativeUrlPipe.transform('mempool-block/'), this.markIndex - 1]);
|
||||
} else {
|
||||
this.stateService.blocks$
|
||||
.pipe(take(this.stateService.env.MEMPOOL_BLOCKS_AMOUNT))
|
||||
.subscribe(([block]) => {
|
||||
if (this.stateService.latestBlockHeight === block.height) {
|
||||
this.router.navigate([(this.network ? '/' + this.network : '') + '/block/', block.id], { state: { data: { block } }});
|
||||
this.router.navigate([this.relativeUrlPipe.transform('/block/'), block.id], { state: { data: { block } }});
|
||||
}
|
||||
});
|
||||
}
|
||||
} else if (event.key === 'ArrowLeft') {
|
||||
if (this.mempoolBlocks[this.markIndex + 1]) {
|
||||
this.router.navigate([(this.network ? '/' + this.network : '') + '/mempool-block/', this.markIndex + 1]);
|
||||
this.router.navigate([this.relativeUrlPipe.transform('/mempool-block/'), this.markIndex + 1]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -371,7 +371,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges {
|
||||
this.feeLimitIndex = i;
|
||||
}
|
||||
if (feeLevels[i] <= this.limitFee) {
|
||||
if (this.stateService.network === 'liquid') {
|
||||
if (this.stateService.network === 'liquid' || this.stateService.network === 'liquidtestnet') {
|
||||
this.feeLevelsOrdered.push(`${(feeLevels[i] / 10).toFixed(1)} - ${(feeLevels[i + 1] / 10).toFixed(1)}`);
|
||||
} else {
|
||||
this.feeLevelsOrdered.push(`${feeLevels[i]} - ${feeLevels[i + 1]}`);
|
||||
|
||||
@@ -7,11 +7,13 @@ import { Observable, of, Subject, merge } from 'rxjs';
|
||||
import { debounceTime, distinctUntilChanged, switchMap, filter, catchError, map } from 'rxjs/operators';
|
||||
import { ElectrsApiService } from 'src/app/services/electrs-api.service';
|
||||
import { NgbTypeahead } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { RelativeUrlPipe } from 'src/app/shared/pipes/relative-url/relative-url.pipe';
|
||||
|
||||
@Component({
|
||||
selector: 'app-search-form',
|
||||
templateUrl: './search-form.component.html',
|
||||
styleUrls: ['./search-form.component.scss'],
|
||||
providers: [RelativeUrlPipe],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class SearchFormComponent implements OnInit {
|
||||
@@ -38,6 +40,7 @@ export class SearchFormComponent implements OnInit {
|
||||
private assetsService: AssetsService,
|
||||
private stateService: StateService,
|
||||
private electrsApiService: ElectrsApiService,
|
||||
private relativeUrlPipe: RelativeUrlPipe,
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
@@ -48,7 +51,7 @@ export class SearchFormComponent implements OnInit {
|
||||
searchText: ['', Validators.required],
|
||||
});
|
||||
|
||||
if (this.network === 'liquid') {
|
||||
if (this.network === 'liquid' || this.network === 'liquidtestnet') {
|
||||
this.assetsService.getAssetsMinimalJson$
|
||||
.subscribe((assets) => {
|
||||
this.assets = assets;
|
||||
@@ -101,7 +104,7 @@ export class SearchFormComponent implements OnInit {
|
||||
this.navigate('/block/', searchText);
|
||||
} else if (this.regexTransaction.test(searchText)) {
|
||||
const matches = this.regexTransaction.exec(searchText);
|
||||
if (this.network === 'liquid') {
|
||||
if (this.network === 'liquid' || this.network === 'liquidtestnet') {
|
||||
if (this.assets[matches[1]]) {
|
||||
this.navigate('/asset/', matches[1]);
|
||||
}
|
||||
@@ -125,7 +128,7 @@ export class SearchFormComponent implements OnInit {
|
||||
}
|
||||
|
||||
navigate(url: string, searchText: string, extras?: any) {
|
||||
this.router.navigate([(this.network && this.stateService.env.BASE_MODULE === 'mempool' ? '/' + this.network : '') + url, searchText], extras);
|
||||
this.router.navigate([this.relativeUrlPipe.transform(url), searchText], extras);
|
||||
this.searchTriggered.emit();
|
||||
this.searchForm.setValue({
|
||||
searchText: '',
|
||||
|
||||
@@ -117,7 +117,7 @@
|
||||
|
||||
</ng-template>
|
||||
|
||||
<ng-template [ngIf]="paymentForm.get('method').value === 'lbtc'">
|
||||
<ng-template [ngIf]="paymentForm.get('method').value === 'lbtc' || paymentForm.get('method').value === 'tlbtc'">
|
||||
|
||||
<div class="qr-wrapper">
|
||||
<a [href]="bypassSecurityTrustUrl('liquidnetwork:' + donationObj.addresses.LBTC + '?amount=' + donationObj.amount + '&assetid=6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d')" target="_blank">
|
||||
|
||||
@@ -41,17 +41,11 @@
|
||||
</button>
|
||||
<div class="dropdown-fees" ngbDropdownMenu aria-labelledby="dropdownFees">
|
||||
<ul>
|
||||
<ng-template ngFor let-fee let-i="index" [ngForOf]="feeLevels">
|
||||
<ng-template [ngIf]="fee <= 400">
|
||||
<li (click)="filterFees(fee)" [class]="filterFeeIndex > fee ? 'inactive' : ''">
|
||||
<ng-template [ngIf]="inverted">
|
||||
<span class="square" [ngStyle]="{'backgroundColor': chartColors[i]}"></span>
|
||||
<span class="fee-text" >{{feeLevels[i]}} - {{ feeLevels[i + 1] }}</span>
|
||||
</ng-template>
|
||||
<ng-template [ngIf]="!inverted">
|
||||
<span class="square" [ngStyle]="{'backgroundColor': chartColors[i - 1]}"></span>
|
||||
<span class="fee-text" >{{feeLevels[i]}} - {{ feeLevels[i - 1] }}</span>
|
||||
</ng-template>
|
||||
<ng-template ngFor let-feeData let-i="index" [ngForOf]="feeLevelDropdownData">
|
||||
<ng-template [ngIf]="feeData.fee <= 400">
|
||||
<li (click)="filterFeeIndex = feeData.fee" [class]="filterFeeIndex > feeData.fee ? 'inactive' : ''">
|
||||
<span class="square" [ngStyle]="{'backgroundColor': feeData.color}"></span>
|
||||
<span class="fee-text">{{ feeData.range }}</span>
|
||||
</li>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
|
||||
@@ -37,6 +37,7 @@ export class StatisticsComponent implements OnInit {
|
||||
radioGroupForm: FormGroup;
|
||||
graphWindowPreference: string;
|
||||
inverted: boolean;
|
||||
feeLevelDropdownData = [];
|
||||
|
||||
constructor(
|
||||
@Inject(LOCALE_ID) private locale: string,
|
||||
@@ -51,19 +52,10 @@ export class StatisticsComponent implements OnInit {
|
||||
|
||||
ngOnInit() {
|
||||
this.inverted = this.storageService.getValue('inverted-graph') === 'true';
|
||||
if (!this.inverted) {
|
||||
this.feeLevels = [...feeLevels].reverse();
|
||||
this.chartColors = [...chartColors].reverse();
|
||||
}
|
||||
this.setFeeLevelDropdownData();
|
||||
this.seoService.setTitle($localize`:@@5d4f792f048fcaa6df5948575d7cb325c9393383:Graphs`);
|
||||
this.stateService.networkChanged$.subscribe((network) => this.network = network);
|
||||
this.graphWindowPreference = this.storageService.getValue('graphWindowPreference') ? this.storageService.getValue('graphWindowPreference').trim() : '2h';
|
||||
const isMobile = window.innerWidth <= 767.98;
|
||||
let labelHops = isMobile ? 48 : 24;
|
||||
|
||||
if (isMobile) {
|
||||
labelHops = 96;
|
||||
}
|
||||
|
||||
this.radioGroupForm = this.formBuilder.group({
|
||||
dateSpan: this.graphWindowPreference
|
||||
@@ -147,11 +139,27 @@ export class StatisticsComponent implements OnInit {
|
||||
document.location.reload();
|
||||
}
|
||||
|
||||
filterFees(index: number) {
|
||||
this.filterFeeIndex = index;
|
||||
}
|
||||
|
||||
filterClick() {
|
||||
this.dropDownOpen = !this.dropDownOpen;
|
||||
setFeeLevelDropdownData() {
|
||||
let _feeLevels = feeLevels
|
||||
let _chartColors = chartColors;
|
||||
if (!this.inverted) {
|
||||
_feeLevels = [...feeLevels].reverse();
|
||||
_chartColors = [...chartColors].reverse();
|
||||
}
|
||||
_feeLevels.forEach((fee, i) => {
|
||||
if (this.inverted) {
|
||||
this.feeLevelDropdownData.push({
|
||||
fee: fee,
|
||||
range: this.stateService.isLiquid() ? `${(_feeLevels[i] / 10).toFixed(1)} - ${(_feeLevels[i + 1] / 10).toFixed(1)}` : `${_feeLevels[i]} - ${_feeLevels[i + 1]}`,
|
||||
color: _chartColors[i],
|
||||
});
|
||||
} else {
|
||||
this.feeLevelDropdownData.push({
|
||||
fee: fee,
|
||||
range: this.stateService.isLiquid() ? `${(_feeLevels[i] / 10).toFixed(1)} - ${(_feeLevels[i - 1] / 10).toFixed(1)}` : `${_feeLevels[i]} - ${_feeLevels[i - 1]}`,
|
||||
color: _chartColors[i - 1],
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
<td><app-time-span [time]="tx.status.block_time - transactionTime" [fastRender]="true"></app-time-span></td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
<tr *ngIf="network !== 'liquid'">
|
||||
<tr *ngIf="network !== 'liquid' && network !== 'liquidtestnet'">
|
||||
<td class="td-width" i18n="transaction.features|Transaction features">Features</td>
|
||||
<td>
|
||||
<app-tx-features [tx]="tx"></app-tx-features>
|
||||
@@ -114,7 +114,7 @@
|
||||
<span i18n="transaction.eta.in-several-hours|Transaction ETA in several hours or more">In several hours (or more)</span>
|
||||
</ng-template>
|
||||
<ng-template #belowBlockLimit>
|
||||
<ng-template [ngIf]="network === 'liquid'" [ngIfElse]="timeEstimateDefault">
|
||||
<ng-template [ngIf]="network === 'liquid' || network === 'liquidtestnet'" [ngIfElse]="timeEstimateDefault">
|
||||
<app-time-until [time]="(60 * 1000 * txInBlockIndex) + now" [fastRender]="false" [fixedRender]="true"></app-time-until>
|
||||
</ng-template>
|
||||
<ng-template #timeEstimateDefault>
|
||||
@@ -124,7 +124,7 @@
|
||||
</ng-template>
|
||||
</td>
|
||||
</tr>
|
||||
<tr *ngIf="network !== 'liquid'">
|
||||
<tr *ngIf="network !== 'liquid' && network !== 'liquidtestnet'">
|
||||
<td class="td-width" i18n="transaction.features|Transaction Features">Features</td>
|
||||
<td>
|
||||
<app-tx-features [tx]="tx"></app-tx-features>
|
||||
|
||||
@@ -159,7 +159,7 @@ export class TransactionComponent implements OnInit, OnDestroy {
|
||||
);
|
||||
}),
|
||||
switchMap((tx) => {
|
||||
if (this.network === 'liquid') {
|
||||
if (this.network === 'liquid' || this.network === 'liquidtestnet') {
|
||||
return from(this.liquidUnblinding.checkUnblindedTx(tx))
|
||||
.pipe(
|
||||
catchError((error) => {
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
</td>
|
||||
<td>
|
||||
<div [ngSwitch]="true">
|
||||
<ng-container *ngSwitchCase="vin.is_coinbase"><span i18n="transactions-list.coinbase">Coinbase</span><ng-template [ngIf]="network !== 'liquid'"> <span i18n="transactions-list.newly-generated-coins">(Newly Generated Coins)</span></ng-template><br /><a placement="bottom" [ngbTooltip]="vin.scriptsig | hex2ascii"><span class="badge badge-secondary scriptmessage longer">{{ vin.scriptsig | hex2ascii }}</span></a></ng-container>
|
||||
<ng-container *ngSwitchCase="vin.is_coinbase"><span i18n="transactions-list.coinbase">Coinbase</span><ng-template [ngIf]="network !== 'liquid' && network !== 'liquidtestnet'"> <span i18n="transactions-list.newly-generated-coins">(Newly Generated Coins)</span></ng-template><br /><a placement="bottom" [ngbTooltip]="vin.scriptsig | hex2ascii"><span class="badge badge-secondary scriptmessage longer">{{ vin.scriptsig | hex2ascii }}</span></a></ng-container>
|
||||
<ng-container *ngSwitchCase="vin.is_pegin">
|
||||
<span i18n="transactions-list.peg-in">Peg-in</span>
|
||||
</ng-container>
|
||||
@@ -56,13 +56,18 @@
|
||||
<span>P2PK</span>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchDefault>
|
||||
<a [routerLink]="['/address/' | relativeUrl, vin.prevout.scriptpubkey_address]" title="{{ vin.prevout.scriptpubkey_address }}">
|
||||
<span class="d-block d-lg-none">{{ vin.prevout.scriptpubkey_address | shortenString : 16 }}</span>
|
||||
<span class="d-none d-lg-block">{{ vin.prevout.scriptpubkey_address | shortenString : 35 }}</span>
|
||||
</a>
|
||||
<div>
|
||||
<app-address-labels [vin]="vin"></app-address-labels>
|
||||
</div>
|
||||
<ng-template [ngIf]="!vin.prevout" [ngIfElse]="defaultAddress">
|
||||
<span>{{ vin.issuance ? 'Issuance' : 'UNKNOWN' }}</span>
|
||||
</ng-template>
|
||||
<ng-template #defaultAddress>
|
||||
<a [routerLink]="['/address/' | relativeUrl, vin.prevout.scriptpubkey_address]" title="{{ vin.prevout.scriptpubkey_address }}">
|
||||
<span class="d-block d-lg-none">{{ vin.prevout.scriptpubkey_address | shortenString : 16 }}</span>
|
||||
<span class="d-none d-lg-block">{{ vin.prevout.scriptpubkey_address | shortenString : 35 }}</span>
|
||||
</a>
|
||||
<div>
|
||||
<app-address-labels [vin]="vin"></app-address-labels>
|
||||
</div>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
</div>
|
||||
</td>
|
||||
@@ -244,7 +249,7 @@
|
||||
|
||||
</span>
|
||||
<button type="button" class="btn btn-sm btn-primary mt-2" (click)="switchCurrency()">
|
||||
<ng-template [ngIf]="network === 'liquid'" [ngIfElse]="defaultAmount" i18n="shared.confidential">Confidential</ng-template>
|
||||
<ng-template [ngIf]="network === 'liquid' || network === 'liquidtestnet'" [ngIfElse]="defaultAmount" i18n="shared.confidential">Confidential</ng-template>
|
||||
<ng-template #defaultAmount>
|
||||
<app-amount [satoshis]="getTotalTxOutput(tx)"></app-amount>
|
||||
</ng-template>
|
||||
|
||||
@@ -15,7 +15,7 @@ import { map } from 'rxjs/operators';
|
||||
})
|
||||
export class TransactionsListComponent implements OnInit, OnChanges {
|
||||
network = '';
|
||||
nativeAssetId = environment.nativeAssetId;
|
||||
nativeAssetId = this.stateService.network === 'liquidtestnet' ? environment.nativeTestAssetId : environment.nativeAssetId;
|
||||
displayDetails = false;
|
||||
|
||||
@Input() transactions: Transaction[];
|
||||
@@ -41,7 +41,7 @@ export class TransactionsListComponent implements OnInit, OnChanges {
|
||||
this.latestBlock$ = this.stateService.blocks$.pipe(map(([block]) => block));
|
||||
this.stateService.networkChanged$.subscribe((network) => this.network = network);
|
||||
|
||||
if (this.network === 'liquid') {
|
||||
if (this.network === 'liquid' || this.network === 'liquidtestnet') {
|
||||
this.assetsService.getAssetsMinimalJson$.subscribe((assets) => {
|
||||
this.assetsMinimal = assets;
|
||||
});
|
||||
@@ -99,7 +99,7 @@ export class TransactionsListComponent implements OnInit, OnChanges {
|
||||
}
|
||||
|
||||
switchCurrency() {
|
||||
if (this.network === 'liquid') {
|
||||
if (this.network === 'liquid' || this.network === 'liquidtestnet') {
|
||||
return;
|
||||
}
|
||||
const oldvalue = !this.stateService.viewFiat$.value;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="container-xl dashboard-container">
|
||||
<div class="row row-cols-1 row-cols-md-2" *ngIf="{ value: (mempoolInfoData$ | async) } as mempoolInfoData">
|
||||
<ng-template [ngIf]="collapseLevel === 'three'" [ngIfElse]="expanded">
|
||||
<div class="col card-wrapper" *ngIf="(network$ | async) !== 'liquid'">
|
||||
<div class="col card-wrapper" *ngIf="(network$ | async) !== 'liquid' && (network$ | async) !== 'liquidtestnet'">
|
||||
<div class="main-title" i18n="fees-box.transaction-fees">Transaction Fees</div>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
@@ -10,7 +10,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col" *ngIf="(network$ | async) !== 'liquid'">
|
||||
<div class="col" *ngIf="(network$ | async) !== 'liquid' && (network$ | async) !== 'liquidtestnet'">
|
||||
<ng-container *ngTemplateOutlet="difficultyEpoch"></ng-container>
|
||||
</div>
|
||||
<div class="col">
|
||||
@@ -29,7 +29,7 @@
|
||||
</div>
|
||||
</ng-template>
|
||||
<ng-template #expanded>
|
||||
<div class="col card-wrapper" *ngIf="(network$ | async) !== 'liquid'">
|
||||
<div class="col card-wrapper" *ngIf="(network$ | async) !== 'liquid' && (network$ | async) !== 'liquidtestnet'">
|
||||
<div class="main-title" i18n="fees-box.transaction-fees">Transaction Fees</div>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
@@ -37,7 +37,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col" *ngIf="(network$ | async) !== 'liquid'">
|
||||
<div class="col" *ngIf="(network$ | async) !== 'liquid' && (network$ | async) !== 'liquidtestnet'">
|
||||
<ng-container *ngTemplateOutlet="difficultyEpoch"></ng-container>
|
||||
</div>
|
||||
<div class="col">
|
||||
@@ -125,7 +125,7 @@
|
||||
<tbody>
|
||||
<tr *ngFor="let transaction of transactions$ | async; let i = index;">
|
||||
<td class="table-cell-txid"><a [routerLink]="['/tx' | relativeUrl, transaction.txid]">{{ transaction.txid | shortenString : 10 }}</a></td>
|
||||
<td class="table-cell-satoshis"><app-amount *ngIf="(network$ | async) !== 'liquid'; else liquidAmount" [satoshis]="transaction.value" digitsInfo="1.2-4" [noFiat]="true"></app-amount><ng-template #liquidAmount i18n="shared.confidential">Confidential</ng-template></td>
|
||||
<td class="table-cell-satoshis"><app-amount *ngIf="(network$ | async) !== 'liquid' && (network$ | async) !== 'liquidtestnet'; else liquidAmount" [satoshis]="transaction.value" digitsInfo="1.2-4" [noFiat]="true"></app-amount><ng-template #liquidAmount i18n="shared.confidential">Confidential</ng-template></td>
|
||||
<td class="table-cell-fiat" *ngIf="(network$ | async) === ''" ><app-fiat [value]="transaction.value" digitsInfo="1.0-0"></app-fiat></td>
|
||||
<td class="table-cell-fees">{{ transaction.fee / transaction.vsize | feeRounding }} <span class="symbol" i18n="shared.sat-vbyte|sat/vB">sat/vB</span></td>
|
||||
</tr>
|
||||
|
||||
@@ -262,7 +262,7 @@ export class DashboardComponent implements OnInit {
|
||||
share(),
|
||||
);
|
||||
|
||||
if (this.stateService.network === 'liquid') {
|
||||
if (this.stateService.network === 'liquid' || this.stateService.network === 'liquidtestnet') {
|
||||
this.liquidPegsMonth$ = this.apiService.listLiquidPegsMonth$()
|
||||
.pipe(
|
||||
map((pegs) => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Observable } from 'rxjs';
|
||||
import { shareReplay } from 'rxjs/operators';
|
||||
import { map, shareReplay, switchMap } from 'rxjs/operators';
|
||||
import { StateService } from './state.service';
|
||||
|
||||
@Injectable({
|
||||
@@ -21,8 +21,23 @@ export class AssetsService {
|
||||
apiBaseUrl = this.stateService.env.NGINX_PROTOCOL + '://' + this.stateService.env.NGINX_HOSTNAME + ':' + this.stateService.env.NGINX_PORT;
|
||||
}
|
||||
|
||||
this.getAssetsJson$ = this.httpClient.get(apiBaseUrl + '/resources/assets.json').pipe(shareReplay());
|
||||
this.getAssetsMinimalJson$ = this.httpClient.get(apiBaseUrl + '/resources/assets.minimal.json').pipe(shareReplay());
|
||||
this.getMiningPools$ = this.httpClient.get(apiBaseUrl + '/resources/pools.json').pipe(shareReplay());
|
||||
this.getAssetsJson$ = this.stateService.networkChanged$
|
||||
.pipe(
|
||||
switchMap(() => this.httpClient.get(`${apiBaseUrl}/resources/assets${this.stateService.network === 'liquidtestnet' ? '-testnet' : ''}.json`)),
|
||||
shareReplay(1),
|
||||
);
|
||||
this.getAssetsMinimalJson$ = this.stateService.networkChanged$
|
||||
.pipe(
|
||||
switchMap(() => this.httpClient.get(`${apiBaseUrl}/resources/assets${this.stateService.network === 'liquidtestnet' ? '-testnet' : ''}.minimal.json`)),
|
||||
map((assetsMinimal) => {
|
||||
if (this.stateService.network === 'liquidtestnet') {
|
||||
// Hard coding the Liquid Testnet native asset
|
||||
assetsMinimal['144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49'] = [null, "tL-BTC", "Test Liquid Bitcoin", 8];
|
||||
}
|
||||
return assetsMinimal;
|
||||
}),
|
||||
shareReplay(1),
|
||||
);
|
||||
this.getMiningPools$ = this.httpClient.get(apiBaseUrl + '/resources/pools.json').pipe(shareReplay(1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,8 +27,14 @@ export class SeoService {
|
||||
}
|
||||
|
||||
getTitle(): string {
|
||||
if (this.network === 'testnet')
|
||||
return 'mempool - Bitcoin Testnet';
|
||||
if (this.network === 'signet')
|
||||
return 'mempool - Bitcoin Signet';
|
||||
if (this.network === 'liquid')
|
||||
return 'mempool - Liquid Network';
|
||||
if (this.network === 'liquidtestnet')
|
||||
return 'mempool - Liquid Testnet';
|
||||
if (this.network === 'bisq')
|
||||
return 'mempool - Bisq Markets';
|
||||
return 'mempool - ' + (this.network ? this.ucfirst(this.network) : 'Bitcoin') + ' Explorer';
|
||||
|
||||
@@ -19,6 +19,7 @@ export interface Env {
|
||||
TESTNET_ENABLED: boolean;
|
||||
SIGNET_ENABLED: boolean;
|
||||
LIQUID_ENABLED: boolean;
|
||||
LIQUID_TESTNET_ENABLED: boolean;
|
||||
BISQ_ENABLED: boolean;
|
||||
BISQ_SEPARATE_BACKEND: boolean;
|
||||
ITEMS_PER_PAGE: number;
|
||||
@@ -38,6 +39,7 @@ const defaultEnv: Env = {
|
||||
'TESTNET_ENABLED': false,
|
||||
'SIGNET_ENABLED': false,
|
||||
'LIQUID_ENABLED': false,
|
||||
'LIQUID_TESTNET_ENABLED': false,
|
||||
'BASE_MODULE': 'mempool',
|
||||
'BISQ_ENABLED': false,
|
||||
'BISQ_SEPARATE_BACKEND': false,
|
||||
@@ -116,7 +118,7 @@ export class StateService {
|
||||
|
||||
this.blocks$ = new ReplaySubject<[Block, boolean]>(this.env.KEEP_BLOCKS_AMOUNT);
|
||||
|
||||
if (this.env.BASE_MODULE !== 'mempool') {
|
||||
if (this.env.BASE_MODULE === 'bisq') {
|
||||
this.network = this.env.BASE_MODULE;
|
||||
this.networkChanged$.next(this.env.BASE_MODULE);
|
||||
}
|
||||
@@ -125,10 +127,10 @@ export class StateService {
|
||||
}
|
||||
|
||||
setNetworkBasedonUrl(url: string) {
|
||||
if (this.env.BASE_MODULE !== 'mempool') {
|
||||
if (this.env.BASE_MODULE !== 'mempool' && this.env.BASE_MODULE !== 'liquid') {
|
||||
return;
|
||||
}
|
||||
const networkMatches = url.match(/\/(bisq|testnet|liquid|signet)/);
|
||||
const networkMatches = url.match(/\/(bisq|testnet|liquidtestnet|liquid|signet)/);
|
||||
switch (networkMatches && networkMatches[1]) {
|
||||
case 'liquid':
|
||||
if (this.network !== 'liquid') {
|
||||
@@ -136,6 +138,12 @@ export class StateService {
|
||||
this.networkChanged$.next('liquid');
|
||||
}
|
||||
return;
|
||||
case 'liquidtestnet':
|
||||
if (this.network !== 'liquidtestnet') {
|
||||
this.network = 'liquidtestnet';
|
||||
this.networkChanged$.next('liquidtestnet');
|
||||
}
|
||||
return;
|
||||
case 'signet':
|
||||
if (this.network !== 'signet') {
|
||||
this.network = 'signet';
|
||||
@@ -144,8 +152,13 @@ export class StateService {
|
||||
return;
|
||||
case 'testnet':
|
||||
if (this.network !== 'testnet') {
|
||||
this.network = 'testnet';
|
||||
this.networkChanged$.next('testnet');
|
||||
if (this.env.BASE_MODULE === 'liquid') {
|
||||
this.network = 'liquidtestnet';
|
||||
this.networkChanged$.next('liquidtestnet');
|
||||
} else {
|
||||
this.network = 'testnet';
|
||||
this.networkChanged$.next('testnet');
|
||||
}
|
||||
}
|
||||
return;
|
||||
case 'bisq':
|
||||
@@ -155,7 +168,12 @@ export class StateService {
|
||||
}
|
||||
return;
|
||||
default:
|
||||
if (this.network !== '') {
|
||||
if (this.env.BASE_MODULE !== 'mempool') {
|
||||
if (this.network !== this.env.BASE_MODULE) {
|
||||
this.network = this.env.BASE_MODULE;
|
||||
this.networkChanged$.next(this.env.BASE_MODULE);
|
||||
}
|
||||
} else if (this.network !== '') {
|
||||
this.network = '';
|
||||
this.networkChanged$.next('');
|
||||
}
|
||||
@@ -182,4 +200,8 @@ export class StateService {
|
||||
setBlockScrollingInProgress(value: boolean) {
|
||||
this.blockScrolling$.next(value);
|
||||
}
|
||||
|
||||
isLiquid() {
|
||||
return this.network === 'liquid' || this.network === 'liquidtestnet';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,10 +11,13 @@ export class RelativeUrlPipe implements PipeTransform {
|
||||
) { }
|
||||
|
||||
transform(value: string): string {
|
||||
if (this.stateService.env.BASE_MODULE !== 'mempool') {
|
||||
return '/' + value;
|
||||
let network = this.stateService.network;
|
||||
if (this.stateService.env.BASE_MODULE === 'liquid' && network === 'liquidtestnet') {
|
||||
network = 'testnet';
|
||||
} else if (this.stateService.env.BASE_MODULE !== 'mempool') {
|
||||
network = '';
|
||||
}
|
||||
return (this.stateService.network ? '/' + this.stateService.network : '') + value;
|
||||
return (network ? '/' + network : '') + value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export const environment = {
|
||||
production: true,
|
||||
nativeAssetId: '6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d',
|
||||
nativeTestAssetId: '144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49',
|
||||
};
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
export const environment = {
|
||||
production: false,
|
||||
nativeAssetId: '6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d',
|
||||
nativeTestAssetId: '144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49',
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<title>mempool - Bisq Markets</title>
|
||||
<base href="/">
|
||||
|
||||
<meta name="description" content="An open-source explorer developed for the Bisq community.">
|
||||
<meta name="description" content="The Mempool Open Source Project™ - our self-hosted explorer for the Bisq Network.">
|
||||
|
||||
<meta property="og:image" content="https://bisq.markets/resources/bisq/bisq-markets-preview.png" />
|
||||
<meta property="og:image:type" content="image/jpeg" />
|
||||
@@ -13,8 +13,8 @@
|
||||
<meta property="twitter:card" content="summary_large_image">
|
||||
<meta property="twitter:site" content="https://bisq.markets/">
|
||||
<meta property="twitter:creator" content="@bisq_network">
|
||||
<meta property="twitter:title" content="mempool - Bisq Markets">
|
||||
<meta property="twitter:description" content="An open-source explorer developed for the Bisq community.">
|
||||
<meta property="twitter:title" content="The Mempool Open Source Project™">
|
||||
<meta property="twitter:description" content="Our self-hosted markets explorer for the Bisq community.">
|
||||
<meta property="twitter:image:src" content="https://bisq.markets/resources/bisq/bisq-markets-preview.png" />
|
||||
<meta property="twitter:domain" content="bisq.markets">
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<title>mempool - Liquid Network</title>
|
||||
<base href="/">
|
||||
|
||||
<meta name="description" content="An open-source explorer developed for the Liquid Network.">
|
||||
<meta name="description" content="The Mempool Open Source Project™ - our self-hosted explorer for the Liquid Network.">
|
||||
<meta property="og:image" content="https://liquid.network/resources/liquid/liquid-network-preview.png" />
|
||||
<meta property="og:image:type" content="image/png" />
|
||||
<meta property="og:image:width" content="1000" />
|
||||
@@ -13,8 +13,8 @@
|
||||
<meta property="twitter:card" content="summary_large_image">
|
||||
<meta property="twitter:site" content="@mempool">
|
||||
<meta property="twitter:creator" content="@mempool">
|
||||
<meta property="twitter:title" content="mempool - Liquid Network">
|
||||
<meta property="twitter:description" content="An open-source explorer developed for the Liquid Network.">
|
||||
<meta property="twitter:title" content="The Mempool Open Source Project™">
|
||||
<meta property="twitter:description" content="Our self-hosted network explorer for the Liquid community.">
|
||||
<meta property="twitter:image:src" content="https://liquid.network/resources/liquid/liquid-network-preview.png" />
|
||||
<meta property="twitter:domain" content="liquid.network">
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<title>mempool - Bitcoin Explorer</title>
|
||||
<base href="/">
|
||||
|
||||
<meta name="description" content="An open-source explorer developed for the Bitcoin community, focusing on the emerging transaction fee market to help our transition into a multi-layer ecosystem." />
|
||||
<meta name="description" content="The Mempool Open Source Project™ - our self-hosted explorer for the Bitcoin community." />
|
||||
<meta property="og:image" content="https://mempool.space/resources/mempool-space-preview.png" />
|
||||
<meta property="og:image:type" content="image/png" />
|
||||
<meta property="og:image:width" content="1000" />
|
||||
@@ -13,8 +13,8 @@
|
||||
<meta property="twitter:card" content="summary_large_image">
|
||||
<meta property="twitter:site" content="@mempool">
|
||||
<meta property="twitter:creator" content="@mempool">
|
||||
<meta property="twitter:title" content="mempool">
|
||||
<meta property="twitter:description" content="An open-source explorer developed for the Bitcoin community, focusing on the transition into a multi-layer ecosystem." />
|
||||
<meta property="twitter:title" content="The Mempool Open Source Project™">
|
||||
<meta property="twitter:description" content="Our self-hosted mempool explorer for the Bitcoin community." />
|
||||
<meta property="twitter:image:src" content="https://mempool.space/resources/mempool-space-preview.png" />
|
||||
<meta property="twitter:domain" content="mempool.space">
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
BIN
frontend/src/resources/liquidtestnet-logo.png
Normal file
BIN
frontend/src/resources/liquidtestnet-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
17
frontend/src/resources/profile/runcitadel.svg
Normal file
17
frontend/src/resources/profile/runcitadel.svg
Normal file
@@ -0,0 +1,17 @@
|
||||
<svg width="897" height="1072" viewBox="0 0 897 1072" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M304.742 112.004L448.742 29.7188V157.719L304.742 240.004V112.004Z" fill="#40607D"/>
|
||||
<path d="M616.742 224L592.742 240V112L616.742 95.9999V224Z" fill="#40607D"/>
|
||||
<path d="M52.6641 256.045L192.744 176V304L164.728 320.009L52.6641 256.045Z" fill="#40607D"/>
|
||||
<path d="M168.742 480L192.742 464V411.789L280.742 462.736V416L304.742 400L448.742 482.4V208L192.742 356.21V336L168.742 352V480Z" fill="#40607D"/>
|
||||
<path d="M728.742 352L896.741 256V699.935C896.741 771.706 858.284 837.975 795.969 873.583L448.742 1072V511.999L616.742 416V543.999L728.742 480V352Z" fill="#40607D"/>
|
||||
<path d="M448.742 157.719L592.742 240.004V112.004L448.742 29.7188V157.719Z" fill="#6891AA"/>
|
||||
<path d="M304.742 240L280.742 224V95.9999L304.742 112V240Z" fill="#6891AA"/>
|
||||
<path d="M168.742 352L0.742188 256V699.935C0.742188 771.706 39.1994 837.975 101.514 873.583L448.742 1072V511.999L280.742 416V543.999L168.742 480V352Z" fill="#6891AA"/>
|
||||
<path d="M704.741 464L728.741 480V352L704.741 336V356.21L448.742 208V482.4L592.742 400L616.742 416V462.736L704.741 411.789V464Z" fill="#6891AA"/>
|
||||
<path d="M732.757 320.009L704.741 304V176L844.821 256.045L732.757 320.009Z" fill="#6891AA"/>
|
||||
<path d="M448.742 29.6L592.742 112L616.742 95.9999L448.742 0L280.742 95.9999L304.742 112L448.742 29.6Z" fill="#A2BECE"/>
|
||||
<path d="M280.742 224L304.742 240L448.742 157.719L592.742 240.004L616.742 224L704.741 274.285V304L732.757 320.009L844.901 256L704.741 176L728.741 160L896.741 256L728.741 352L704.741 336V356.21L448.742 208L192.742 356.21V336L168.742 352L0.742188 256L168.742 160L192.742 176L52.5821 256L164.728 320.009L192.744 304V274.284L280.742 224Z" fill="#A2BECE"/>
|
||||
<path d="M592.742 400L616.742 416L448.742 511.999L280.742 416L304.742 400L448.742 482.4L592.742 400Z" fill="#A2BECE"/>
|
||||
<path d="M192.742 411.789L280.742 462.736V543.999L168.742 480L192.742 464V411.789Z" fill="#A2BECE"/>
|
||||
<path d="M728.741 480L616.742 543.999V462.736L704.741 411.789V464L728.741 480Z" fill="#A2BECE"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 32 KiB |
@@ -100,6 +100,10 @@ body {
|
||||
background-color: #6f1d5d !important;
|
||||
}
|
||||
|
||||
.navbar-nav.liquidtestnet > .active {
|
||||
background-color: #494a4a !important;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
@@ -42,9 +42,17 @@ if (configContent.BASE_MODULE && configContent.BASE_MODULE === 'liquid') {
|
||||
assetsMinimalJsonUrl = 'https://raw.githubusercontent.com/Blockstream/asset_registry_db/master/index.minimal.json';
|
||||
}
|
||||
|
||||
const testnetAssetsJsonUrl = 'https://raw.githubusercontent.com/Blockstream/asset_registry_testnet_db/master/index.json';
|
||||
const testnetAssetsMinimalJsonUrl = 'https://raw.githubusercontent.com/Blockstream/asset_registry_testnet_db/master/index.minimal.json';
|
||||
|
||||
console.log('Downloading assets');
|
||||
download(PATH + 'assets.json', assetsJsonUrl);
|
||||
console.log('Downloading assets minimal');
|
||||
download(PATH + 'assets.minimal.json', assetsMinimalJsonUrl);
|
||||
console.log('Downloading mining pools info');
|
||||
download(PATH + 'pools.json', poolsJsonUrl);
|
||||
console.log('Downloading testnet assets');
|
||||
download(PATH + 'assets-testnet.json', testnetAssetsJsonUrl);
|
||||
console.log('Downloading testnet assets minimal');
|
||||
download(PATH + 'assets-testnet.minimal.json', testnetAssetsMinimalJsonUrl);
|
||||
|
||||
|
||||
@@ -1,80 +1,88 @@
|
||||
# mempool.space v2 production website hosting
|
||||
# mempool enterprise production instance
|
||||
|
||||
These instructions are for setting up a serious production mempool website for Mainnet, Testnet, and Liquid. For home users, follow the main instructions instead.
|
||||
These instructions are for setting up a serious production mempool website for Bitcoin mainnet, testnet, signet, Liquid mainnet and testnet, and Bisq. For home users, you should use one-click installation methods instead, and for advanced manual deployments of mainnet only see the top-level installation instructions.
|
||||
|
||||
### Server Hardware
|
||||
|
||||
Mempool V2 is powered by electrs, which is a beast. I recommend a beefy server:
|
||||
Mempool V2 is powered by blockstream/electrs, which is a beast. I recommend a beefy server:
|
||||
|
||||
* 16C CPU (more is better)
|
||||
* 20C CPU (more is better)
|
||||
* 64G RAM (more is better)
|
||||
* 2TB SSD (NVMe is better)
|
||||
* 4TB SSD (NVMe is better)
|
||||
|
||||
### HDD vs SSD vs NVMe
|
||||
|
||||
If you don't have a fast SSD or NVMe backed disk, that's fine. What you do is, go online and buy some fast new NVMe drives and wait for them to arrive. After you install them, throw away your old HDDs and then proceed with the rest of this guide.
|
||||
|
||||
## FreeBSD 12
|
||||
## FreeBSD 13
|
||||
|
||||
The mempool.space site is powered by FreeBSD with ZFS root and ARC cache for maximum performance. Linux probably works fine too, but why settle?
|
||||
|
||||
### Filesystem
|
||||
|
||||
For maximum performance, I use 2x 1TB NVMe SSDs in a RAID 0 using ZFS with lots of RAM for the ARC L2 cache.
|
||||
For maximum performance, I use 2x 2TB NVMe SSDs in a RAID 0 using ZFS with lots of RAM for the ARC L2 cache.
|
||||
```
|
||||
# zpool list -v nvmraid
|
||||
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
|
||||
nvmraid 1.81T 1.04T 787G - - 0% 57% 1.00x ONLINE -
|
||||
nvd0 928G 535G 393G - - 0% 57%
|
||||
nvd1 928G 534G 394G - - 0% 57%
|
||||
% zpool list -v
|
||||
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
|
||||
nvm 3.62T 1.25T 2.38T - - 2% 34% 1.00x ONLINE -
|
||||
nvd0p3 1.81T 629G 1.20T - - 2% 33.9% - ONLINE
|
||||
nvd1p3 1.81T 646G 1.18T - - 2% 34.8% - ONLINE
|
||||
```
|
||||
|
||||
For maximum flexibility of configuration, I configure the partitions separately for each data folder:
|
||||
For maximum flexibility of configuration, I recommend partitions separately for each data folder:
|
||||
```
|
||||
Filesystem Size Used Avail Capacity Mounted on
|
||||
nvmraid/mempool 732G 3.0G 729G 0% /mempool
|
||||
nvmraid/mysql 730G 618M 729G 0% /mysql
|
||||
nvmraid/bisq 729G 88K 729G 0% /bisq
|
||||
nvmraid/elements 731G 1.8G 729G 0% /elements
|
||||
nvmraid/elements/liquidv1 737G 7.2G 729G 1% /elements/liquidv1
|
||||
nvmraid/elements/electrs 730G 434M 729G 0% /elements/electrs
|
||||
nvmraid/bitcoin 730G 694M 729G 0% /bitcoin
|
||||
nvmraid/bitcoin/chainstate 733G 3.9G 729G 1% /bitcoin/chainstate
|
||||
nvmraid/bitcoin/indexes 757G 27G 729G 4% /bitcoin/indexes
|
||||
nvmraid/bitcoin/electrs 730G 853M 729G 0% /bitcoin/electrs
|
||||
nvmraid/bitcoin/blocks 1.0T 306G 729G 30% /bitcoin/blocks
|
||||
nvmraid/bitcoin/testnet3 729G 13M 729G 0% /bitcoin/testnet3
|
||||
nvmraid/bitcoin/testnet3/blocks 756G 26G 729G 3% /bitcoin/testnet3/blocks
|
||||
nvmraid/bitcoin/testnet3/chainstate 731G 1.3G 729G 0% /bitcoin/testnet3/chainstate
|
||||
nvmraid/bitcoin/testnet3/indexes 733G 3.8G 729G 1% /bitcoin/testnet3/indexes
|
||||
nvmraid/electrs/liquid/cache 729G 39M 729G 0% /electrs/liquid/newindex/cache
|
||||
nvmraid/electrs/liquid/history 730G 737M 729G 0% /electrs/liquid/newindex/history
|
||||
nvmraid/electrs/liquid/txstore 736G 6.2G 729G 1% /electrs/liquid/newindex/txstore
|
||||
nvmraid/electrs/mainnet/cache 729G 44M 729G 0% /electrs/mainnet/newindex/cache
|
||||
nvmraid/electrs/mainnet/history 964G 234G 729G 24% /electrs/mainnet/newindex/history
|
||||
nvmraid/electrs/mainnet/txstore 1.1T 392G 729G 35% /electrs/mainnet/newindex/txstore
|
||||
nvmraid/electrs/testnet/cache 729G 40M 729G 0% /electrs/testnet/newindex/cache
|
||||
nvmraid/electrs/testnet/history 747G 18G 729G 2% /electrs/testnet/newindex/history
|
||||
nvmraid/electrs/testnet/txstore 764G 34G 729G 4% /electrs/testnet/newindex/txstore
|
||||
nvm/bisq 766G 1.1G 765G 0% /bisq
|
||||
nvm/bitcoin 766G 648M 765G 0% /bitcoin
|
||||
nvm/bitcoin/blocks 1.1T 375G 765G 33% /bitcoin/blocks
|
||||
nvm/bitcoin/chainstate 770G 4.5G 765G 1% /bitcoin/chainstate
|
||||
nvm/bitcoin/electrs 772G 7.3G 765G 1% /bitcoin/electrs
|
||||
nvm/bitcoin/indexes 799G 34G 765G 4% /bitcoin/indexes
|
||||
nvm/bitcoin/testnet3 765G 5.0M 765G 0% /bitcoin/testnet3
|
||||
nvm/bitcoin/testnet3/blocks 786G 21G 765G 3% /bitcoin/testnet3/blocks
|
||||
nvm/bitcoin/testnet3/chainstate 766G 1.1G 765G 0% /bitcoin/testnet3/chainstate
|
||||
nvm/bitcoin/testnet3/indexes 768G 2.9G 765G 0% /bitcoin/testnet3/indexes
|
||||
nvm/electrs 765G 128K 765G 0% /electrs
|
||||
nvm/electrs/liquid 765G 104K 765G 0% /electrs/liquid
|
||||
nvm/electrs/liquid/cache 765G 7.8M 765G 0% /electrs/liquid/newindex/cache
|
||||
nvm/electrs/liquid/history 766G 886M 765G 0% /electrs/liquid/newindex/history
|
||||
nvm/electrs/liquid/txstore 775G 10G 765G 1% /electrs/liquid/newindex/txstore
|
||||
nvm/electrs/liquidtestnet 765G 112K 765G 0% /electrs/liquidtestnet
|
||||
nvm/electrs/liquidtestnet/cache 765G 96K 765G 0% /electrs/liquidtestnet/newindex/cache
|
||||
nvm/electrs/liquidtestnet/history 765G 96K 765G 0% /electrs/liquidtestnet/newindex/history
|
||||
nvm/electrs/liquidtestnet/txstore 765G 96K 765G 0% /electrs/liquidtestnet/newindex/txstore
|
||||
nvm/electrs/mainnet 765G 112K 765G 0% /electrs/mainnet
|
||||
nvm/electrs/mainnet/cache 765G 4.4M 765G 0% /electrs/mainnet/newindex/cache
|
||||
nvm/electrs/mainnet/history 1.0T 300G 765G 28% /electrs/mainnet/newindex/history
|
||||
nvm/electrs/mainnet/txstore 1.3T 530G 765G 41% /electrs/mainnet/newindex/txstore
|
||||
nvm/electrs/signet 766G 522M 765G 0% /electrs/signet
|
||||
nvm/electrs/testnet 765G 104K 765G 0% /electrs/testnet
|
||||
nvm/electrs/testnet/cache 765G 1.6M 765G 0% /electrs/testnet/newindex/cache
|
||||
nvm/electrs/testnet/history 784G 19G 765G 2% /electrs/testnet/newindex/history
|
||||
nvm/electrs/testnet/txstore 803G 38G 765G 5% /electrs/testnet/newindex/txstore
|
||||
nvm/elements 766G 927M 765G 0% /elements
|
||||
nvm/elements/electrs 766G 716M 765G 0% /elements/electrs
|
||||
nvm/elements/liquidv1 777G 11G 765G 1% /elements/liquidv1
|
||||
nvm/mempool 789G 24G 765G 3% /mempool
|
||||
nvm/mysql 766G 648M 765G 0% /mysql
|
||||
tmpfs 1.0G 1.3M 1.0G 0% /var/cache/nginx
|
||||
tmpfs 3.0G 1.9G 1.1G 63% /bisq/statsnode-data/btc_mainnet/db/json
|
||||
```
|
||||
|
||||
### Build Dependencies
|
||||
|
||||
You'll probably need these:
|
||||
```
|
||||
pkg install -y zsh sudo git screen vim-console curl wget neovim rsync
|
||||
pkg install -y openssl openssh-portable open-vm-tools-nox11 py37-pip
|
||||
pkg install -y boost-libs autoconf automake gmake gcc libevent libtool pkgconf
|
||||
pkg install -y mariadb55-server mariadb55-client nginx py37-certbot-nginx npm
|
||||
pkg install -y zsh sudo git screen curl wget neovim rsync nginx openssl openssh-portable py38-pip py38-certbot-nginx boost-libs autoconf automake gmake gcc libevent libtool pkgconf mariadb105-server mariadb105-client
|
||||
```
|
||||
|
||||
### NodeJS / npm
|
||||
|
||||
I recommend to build nodejs / npm from source using nvm:
|
||||
```
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | zsh
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | zsh
|
||||
source $HOME/.zshrc
|
||||
nvm install node
|
||||
nvm install v16.10.0
|
||||
nvm alias default node
|
||||
```
|
||||
|
||||
@@ -107,11 +115,18 @@ DataDirectory /var/db/tor
|
||||
DataDirectoryGroupReadable 1
|
||||
|
||||
HiddenServiceDir /var/db/tor/mempool
|
||||
HiddenServicePort 80 127.0.0.1:80
|
||||
HiddenServicePort 80 127.0.0.1:81
|
||||
HiddenServiceVersion 3
|
||||
|
||||
HiddenServiceDir /var/db/tor/bisq
|
||||
HiddenServicePort 80 127.0.0.1:82
|
||||
HiddenServiceVersion 3
|
||||
|
||||
HiddenServiceDir /var/db/tor/liquid
|
||||
HiddenServicePort 80 127.0.0.1:83
|
||||
HiddenServiceVersion 3
|
||||
```
|
||||
|
||||
|
||||
### Bitcoin
|
||||
|
||||
Build [Bitcoin Core](https://github.com/bitcoin/bitcoin) from source. Alternatively, install the OS packages:
|
||||
@@ -121,25 +136,35 @@ pkg install -y bitcoin-daemon bitcoin-utils
|
||||
|
||||
Configure your bitcoin.conf like this:
|
||||
```
|
||||
datadir=/bitcoin
|
||||
server=1
|
||||
daemon=1
|
||||
txindex=1
|
||||
listen=1
|
||||
discover=1
|
||||
txindex=1
|
||||
dbcache=3700
|
||||
maxconnections=1337
|
||||
par=16
|
||||
dbcache=4096
|
||||
maxmempool=1337
|
||||
mempoolexpiry=999999
|
||||
maxconnections=42
|
||||
onion=127.0.0.1:9050
|
||||
rpcallowip=127.0.0.1
|
||||
rpcuser=0cd862dce678b830bd2aa36f10b9b6b2
|
||||
rpcpassword=2d89d36cac4a13c87b5d19ef8f577e37
|
||||
rpcuser=foo
|
||||
rpcpassword=bar
|
||||
|
||||
[main]
|
||||
bind=127.0.0.1:8333
|
||||
rpcbind=127.0.0.1:8332
|
||||
whitelist=bloomfilter@127.0.0.1
|
||||
|
||||
[test]
|
||||
daemon=1
|
||||
bind=127.0.0.1:18333
|
||||
rpcbind=127.0.0.1:18332
|
||||
|
||||
[signet]
|
||||
daemon=1
|
||||
bind=127.0.0.1:38333
|
||||
rpcbind=127.0.0.1:38332
|
||||
```
|
||||
|
||||
### Elements
|
||||
@@ -158,15 +183,39 @@ Configure your elements.conf like this:
|
||||
server=1
|
||||
daemon=1
|
||||
listen=1
|
||||
chain=liquidv1
|
||||
rpcuser=liquiduser
|
||||
rpcpassword=liquidpass
|
||||
validatepegin=1
|
||||
rpcuser=foo
|
||||
rpcpassword=bar
|
||||
mainchainrpchost=127.0.0.1
|
||||
mainchainrpcport=8332
|
||||
mainchainrpcuser=user
|
||||
mainchainrpcpassword=pass
|
||||
mainchainrpcuser=foo
|
||||
mainchainrpcpassword=bar
|
||||
txindex=1
|
||||
|
||||
[liquidv1]
|
||||
validatepegin=1
|
||||
mainchainrpcport=8332
|
||||
|
||||
[liquidtestnet]
|
||||
validatepegin=0
|
||||
anyonecanspendaremine=0
|
||||
initialfreecoins=2100000000000000
|
||||
con_dyna_deploy_start=0
|
||||
con_max_block_sig_size=150
|
||||
checkblockindex=0
|
||||
fallbackfee=0.00000100
|
||||
con_has_parent_chain=0
|
||||
parentgenesisblockhash=NULL
|
||||
pubkeyprefix=36
|
||||
scriptprefix=19
|
||||
blindedprefix=23
|
||||
bech32_hrp=tex
|
||||
blech32_hrp=tlq
|
||||
pchmessagestart=410edd62
|
||||
dynamic_epoch_length=1000
|
||||
signblockscript=51210217e403ddb181872c32a0cd468c710040b2f53d8cac69f18dad07985ee37e9a7151ae
|
||||
evbparams=dynafed:0:::
|
||||
addnode=liquid-testnet.blockstream.com:18892
|
||||
addnode=liquidtestnet.com:18891
|
||||
addnode=liquid.network:18444
|
||||
```
|
||||
|
||||
Start elementsd and wait for it to sync the Liquid blockchain.
|
||||
@@ -180,33 +229,42 @@ cd electrs
|
||||
git checkout new-index
|
||||
```
|
||||
|
||||
You'll need 3 instances, one for each network. Build one at a time:
|
||||
You'll need one instance per network. Build and run them one at a time:
|
||||
```
|
||||
./electrs-start-mainnet
|
||||
./electrs-start-testnet
|
||||
./electrs-start-signet
|
||||
./electrs-start-liquid
|
||||
./electrs-start-liquidtestnet
|
||||
```
|
||||
|
||||
### MariaDB
|
||||
|
||||
Import historical mempool fee database snapshot, or the blank mariadb structure if none:
|
||||
Import historical mempool fee database snapshot:
|
||||
```
|
||||
mysql -u root
|
||||
create database mempool;
|
||||
grant all on mempool.* to 'mempool'@'localhost' identified by 'mempool';
|
||||
create database tmempool;
|
||||
grant all on tmempool.* to 'tmempool'@'localhost' identified by 'tmempool';
|
||||
create database lmempool;
|
||||
grant all on lmempool.* to 'lmempool'@'localhost' identified by 'lmempool';
|
||||
create database mempool_testnet;
|
||||
grant all on mempool_testnet.* to 'mempool_testnet'@'localhost' identified by 'mempool_testnet';
|
||||
create database mempool_signet;
|
||||
grant all on mempool_signet.* to 'mempool_signet'@'localhost' identified by 'mempool_signet';
|
||||
create database mempool_liquid;
|
||||
grant all on mempool_liquid.* to 'mempool_liquid'@'localhost' identified by 'mempool_liquid';
|
||||
create database mempool_liquidtestnet;
|
||||
grant all on mempool_liquidtestnet.* to 'mempool_liquidtestnet'@'localhost' identified by 'mempool_liquidtestnet';
|
||||
```
|
||||
|
||||
Then import
|
||||
|
||||
### Bisq
|
||||
|
||||
Build bisq-statsnode normally and run using options like this:
|
||||
```
|
||||
mysql -u mempool -p mempool < /mempool/mempool/mariadb-structure.sql
|
||||
mysql -u tmempool -p tmempool < /mempool/mempool/mariadb-structure.sql
|
||||
mysql -u lmempool -p lmempool < /mempool/mempool/mariadb-structure.sql
|
||||
./bisq-statsnode --dumpBlockchainData=true --dumpStatistics=true
|
||||
```
|
||||
|
||||
If bisq is happy, it should dump JSON files for Bisq Markets and BSQ data into /bisq that the mempool backend will use.
|
||||
|
||||
### Mempool
|
||||
|
||||
After all 3 electrs instances are fully indexed, install your 3 mempool nodes:
|
||||
@@ -224,13 +282,15 @@ Finally, start your 3 mempool backends:
|
||||
|
||||
Get SSL certificate using certbot:
|
||||
```
|
||||
certbot --nginx -d mempool.space
|
||||
certbot --nginx -d mempool.ninja
|
||||
```
|
||||
|
||||
Install nginx.conf from this repo, edit as necessary:
|
||||
Make a symlink from /usr/local/etc/nginx/mempool to /mempool/mempool, and copy the nginx.conf and edit as necessary. You probably only need to edit the top-level nginx.conf file.
|
||||
```
|
||||
cp nginx.conf /usr/local/etc/nginx/nginx.conf
|
||||
vi /usr/local/etc/nginx/nginx.conf
|
||||
cd /usr/local/etc/nginx
|
||||
ln -s /mempool/mempool
|
||||
cp /mempool/mempool/nginx.conf .
|
||||
vi nginx.conf
|
||||
```
|
||||
|
||||
Restart nginx
|
||||
@@ -241,4 +301,3 @@ service nginx restart
|
||||
### Done
|
||||
|
||||
Your site should look like https://mempool.space/
|
||||
If it doesn't ask wiz on Keybase DM or Twitter for help.
|
||||
|
||||
30
production/electrs-start-liquidtestnet
Normal file
30
production/electrs-start-liquidtestnet
Normal file
@@ -0,0 +1,30 @@
|
||||
#!/usr/local/bin/zsh
|
||||
cd "$HOME/electrs"
|
||||
#source $HOME/.cargo/env
|
||||
#export PATH=$HOME/.cargo/bin:$PATH
|
||||
|
||||
until false
|
||||
do
|
||||
# patch code for FreeBSD
|
||||
if grep XBS5 "$HOME/.cargo/registry/src/github.com-1ecc6299db9ec823/sysconf-0.3.4/src/raw.rs" ; then
|
||||
grep -v XBS5 $HOME/.cargo/registry/src/github.com-1ecc6299db9ec823/sysconf-0.3.4/src/raw.rs > /tmp/foo && \
|
||||
mv /tmp/foo $HOME/.cargo/registry/src/github.com-1ecc6299db9ec823/sysconf-0.3.4/src/raw.rs
|
||||
fi
|
||||
|
||||
cargo run \
|
||||
--release \
|
||||
--features liquid \
|
||||
--bin electrs \
|
||||
-- \
|
||||
-vv \
|
||||
--asset-db-path "$HOME/asset_registry_testnet_db" \
|
||||
--address-search \
|
||||
--cors '*' \
|
||||
--db-dir /electrs \
|
||||
--network liquidtestnet \
|
||||
--daemon-dir $HOME \
|
||||
--http-addr '[::]:3004' \
|
||||
--cookie 'foo:bar' \
|
||||
--precache-scripts $HOME/electrs/contrib/popular-scripts.txt
|
||||
sleep 1
|
||||
done
|
||||
39
production/mempool-config.liquidtestnet.json
Normal file
39
production/mempool-config.liquidtestnet.json
Normal file
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"MEMPOOL": {
|
||||
"NETWORK": "liquid",
|
||||
"BACKEND": "esplora",
|
||||
"HTTP_PORT": 8994,
|
||||
"MINED_BLOCKS_CACHE": 144,
|
||||
"SPAWN_CLUSTER_PROCS": 0,
|
||||
"API_URL_PREFIX": "/api/v1/",
|
||||
"WEBSOCKET_REFRESH_RATE_MS": 2000
|
||||
},
|
||||
"SYSLOG" : {
|
||||
"MIN_PRIORITY": "debug"
|
||||
},
|
||||
"CORE_RPC": {
|
||||
"PORT": 7040,
|
||||
"USERNAME": "foo",
|
||||
"PASSWORD": "bar"
|
||||
},
|
||||
"SECOND_CORE_RPC": {
|
||||
"PORT": 8332,
|
||||
"USERNAME": "foo",
|
||||
"PASSWORD": "bar"
|
||||
},
|
||||
"ESPLORA": {
|
||||
"REST_API_URL": "http://127.0.0.1:4004"
|
||||
},
|
||||
"DATABASE": {
|
||||
"ENABLED": true,
|
||||
"HOST": "127.0.0.1",
|
||||
"PORT": 3306,
|
||||
"USERNAME": "mempool_liquidtestnet",
|
||||
"PASSWORD": "mempool_liquidtestnet",
|
||||
"DATABASE": "mempool_liquidtestnet"
|
||||
},
|
||||
"STATISTICS": {
|
||||
"ENABLED": true,
|
||||
"TX_PER_SECOND_SAMPLE_PERIOD": 150
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"TESTNET_ENABLED": true,
|
||||
"LIQUID_ENABLED": true,
|
||||
"LIQUID_TESTNET_ENABLED": true,
|
||||
"BISQ_ENABLED": true,
|
||||
"BISQ_SEPARATE_BACKEND": true,
|
||||
"SIGNET_ENABLED": true,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user