Compare commits
92 Commits
v2.3.0-dev
...
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 | ||
|
|
5d360d4156 | ||
|
|
91e30fbc3c | ||
|
|
5b22e2a000 | ||
|
|
533653e54a | ||
|
|
3dc0dc13ad | ||
|
|
e332789afc | ||
|
|
e4a9fd06b4 | ||
|
|
5845f2380e | ||
|
|
c29311d831 | ||
|
|
252db109bc | ||
|
|
b1c9334119 | ||
|
|
ab04247726 | ||
|
|
e94a85b989 | ||
|
|
a4569788f8 | ||
|
|
b455814e90 | ||
|
|
7afd0f3fe7 | ||
|
|
a2a85469cf | ||
|
|
94488a6029 | ||
|
|
8e4829146a | ||
|
|
08f185525c | ||
|
|
d6b00fe39e | ||
|
|
cec3baeaa4 | ||
|
|
6e59733cac | ||
|
|
c5b705ede7 | ||
|
|
2819e24efe | ||
|
|
5f9bc4497a | ||
|
|
086b14e816 | ||
|
|
958bfe6d25 | ||
|
|
e01ab449cf | ||
|
|
9a18019d9d | ||
|
|
5d8c970351 | ||
|
|
89fede9e48 | ||
|
|
f8a54784d0 | ||
|
|
010381aac4 | ||
|
|
1a8fd23b05 | ||
|
|
3ae46e6ba1 | ||
|
|
40f1949654 | ||
|
|
2281116504 |
@@ -1,7 +1,14 @@
|
||||
---
|
||||
name: 🐛 Bug Report
|
||||
about: Report bugs or other issues to us on GitHub
|
||||
---
|
||||
|
||||
<!--
|
||||
SUPPORT REQUESTS: This is for reporting bugs in Mempool.
|
||||
If you have a support request, please join our Keybase group:
|
||||
SUPPORT REQUESTS:
|
||||
This is for reporting bugs in Mempool, not for support requests.
|
||||
If you have a support request, please join our Keybase or Matrix:
|
||||
https://keybase.io/team/mempool
|
||||
https://matrix.to/#/#mempool:bitcoin.kyoto
|
||||
-->
|
||||
|
||||
### Description
|
||||
@@ -14,11 +21,11 @@
|
||||
|
||||
### Steps to reproduce
|
||||
|
||||
<!--if you can reliably reproduce the bug, list the steps here -->
|
||||
<!-- if you can reliably reproduce the bug, list the steps here -->
|
||||
|
||||
### Expected behaviour
|
||||
|
||||
<!--description of the expected behavior -->
|
||||
<!-- description of the expected behavior -->
|
||||
|
||||
### Actual behaviour
|
||||
|
||||
@@ -26,7 +33,7 @@
|
||||
|
||||
### Screenshots
|
||||
|
||||
<!--Screenshots if gui related, drag and drop to add to the issue -->
|
||||
<!-- Screenshots if gui related, drag and drop to add to the issue -->
|
||||
|
||||
#### Device or machine
|
||||
|
||||
28
.github/ISSUE_TEMPLATE/30-feature-request.md
vendored
Normal file
28
.github/ISSUE_TEMPLATE/30-feature-request.md
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
name: ✨ Feature Request
|
||||
about: Request a feature or suggest other enhancements 💡
|
||||
---
|
||||
|
||||
<!--
|
||||
SUPPORT REQUESTS:
|
||||
This is for requesting features in Mempool, not for support requests.
|
||||
If you have a support request, please join our Keybase or Matrix:
|
||||
https://keybase.io/team/mempool
|
||||
https://matrix.to/#/#mempool:bitcoin.kyoto
|
||||
-->
|
||||
|
||||
### Description
|
||||
|
||||
<!-- brief description of the feature request -->
|
||||
|
||||
### Problem to be solved
|
||||
|
||||
<!-- description of the the problem you're having -->
|
||||
|
||||
### Proposed solution
|
||||
|
||||
<!-- explain how you think we should solve the problem -->
|
||||
|
||||
#### Additional info
|
||||
|
||||
<!-- Additional information useful for implementing (e.g. docs, links, etc.) -->
|
||||
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: 💬 Need help? Chat with us on Matrix
|
||||
url: https://matrix.to/#/#mempool:bitcoin.kyoto
|
||||
about: For support requests or general questions
|
||||
- name: 💬 Need help? Chat with us on Keybase
|
||||
url: https://keybase.io/team/mempool
|
||||
about: For support requests or general questions
|
||||
@@ -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:
|
||||
|
||||
|
||||
7
backend/.gitignore
vendored
7
backend/.gitignore
vendored
@@ -1,7 +1,10 @@
|
||||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# production config
|
||||
mempool-config.json
|
||||
# production config and external assets
|
||||
*.json
|
||||
!mempool-config.sample.json
|
||||
|
||||
icons.json
|
||||
|
||||
# compiled output
|
||||
/dist
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
"INITIAL_BLOCKS_AMOUNT": 8,
|
||||
"MEMPOOL_BLOCKS_AMOUNT": 8,
|
||||
"PRICE_FEED_UPDATE_INTERVAL": 3600,
|
||||
"USE_SECOND_NODE_FOR_MINFEE": false
|
||||
"USE_SECOND_NODE_FOR_MINFEE": false,
|
||||
"EXTERNAL_ASSETS": []
|
||||
},
|
||||
"CORE_RPC": {
|
||||
"HOST": "127.0.0.1",
|
||||
|
||||
@@ -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();
|
||||
|
||||
39
backend/src/api/liquid/icons.ts
Normal file
39
backend/src/api/liquid/icons.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import * as fs from 'fs';
|
||||
import config from '../../config';
|
||||
import logger from '../../logger';
|
||||
|
||||
class Icons {
|
||||
private static FILE_NAME = './icons.json';
|
||||
private iconIds: string[] = [];
|
||||
private icons: { [assetId: string]: string; } = {};
|
||||
|
||||
constructor() {}
|
||||
|
||||
public loadIcons() {
|
||||
if (!fs.existsSync(Icons.FILE_NAME)) {
|
||||
logger.warn(`${Icons.FILE_NAME} does not exist. No Liquid icons loaded.`);
|
||||
return;
|
||||
}
|
||||
const cacheData = fs.readFileSync(Icons.FILE_NAME, 'utf8');
|
||||
this.icons = JSON.parse(cacheData);
|
||||
|
||||
for (const i in this.icons) {
|
||||
this.iconIds.push(i);
|
||||
}
|
||||
logger.debug(`Liquid icons has been loaded.`);
|
||||
}
|
||||
|
||||
public getIconByAssetId(assetId: string): Buffer | undefined {
|
||||
const icon = this.icons[assetId];
|
||||
if (icon) {
|
||||
return Buffer.from(icon, 'base64');
|
||||
}
|
||||
}
|
||||
|
||||
public getAllIconIds() {
|
||||
return this.iconIds;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default new Icons();
|
||||
@@ -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;
|
||||
@@ -394,7 +395,7 @@ class Statistics {
|
||||
public async $list24H(): Promise<OptimizedStatistic[]> {
|
||||
try {
|
||||
const connection = await DB.pool.getConnection();
|
||||
const query = this.getQueryForDaysAvg(120, '1 DAY'); // 2m interval
|
||||
const query = `SELECT *, UNIX_TIMESTAMP(added) as added FROM statistics ORDER BY id DESC LIMIT 1440`;
|
||||
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout });
|
||||
connection.release();
|
||||
return this.mapStatisticToOptimizedStatistic(rows);
|
||||
@@ -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;
|
||||
@@ -16,6 +16,7 @@ interface IConfig {
|
||||
MEMPOOL_BLOCKS_AMOUNT: number;
|
||||
PRICE_FEED_UPDATE_INTERVAL: number;
|
||||
USE_SECOND_NODE_FOR_MINFEE: boolean;
|
||||
EXTERNAL_ASSETS: string[];
|
||||
};
|
||||
ESPLORA: {
|
||||
REST_API_URL: string;
|
||||
@@ -78,6 +79,7 @@ const defaults: IConfig = {
|
||||
'MEMPOOL_BLOCKS_AMOUNT': 8,
|
||||
'PRICE_FEED_UPDATE_INTERVAL': 3600,
|
||||
'USE_SECOND_NODE_FOR_MINFEE': false,
|
||||
'EXTERNAL_ASSETS': [],
|
||||
},
|
||||
'ESPLORA': {
|
||||
'REST_API_URL': 'http://127.0.0.1:3000',
|
||||
|
||||
@@ -22,6 +22,9 @@ import loadingIndicators from './api/loading-indicators';
|
||||
import mempool from './api/mempool';
|
||||
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;
|
||||
@@ -78,6 +81,7 @@ class Server {
|
||||
|
||||
this.setUpWebsocketHandling();
|
||||
|
||||
await syncAssets.syncAssets();
|
||||
diskCache.loadMempoolCache();
|
||||
|
||||
if (config.DATABASE.ENABLED) {
|
||||
@@ -93,6 +97,10 @@ class Server {
|
||||
statistics.startStatistics();
|
||||
}
|
||||
|
||||
if (Common.isLiquid()) {
|
||||
icons.loadIcons();
|
||||
}
|
||||
|
||||
fiatConversion.startService();
|
||||
|
||||
this.setUpHttpApiRoutes();
|
||||
@@ -149,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();
|
||||
@@ -276,7 +284,14 @@ class Server {
|
||||
;
|
||||
}
|
||||
|
||||
if (config.MEMPOOL.NETWORK === 'liquid' && config.DATABASE.ENABLED) {
|
||||
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 (Common.isLiquid() && config.DATABASE.ENABLED) {
|
||||
this.app
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'liquid/pegs/month', routes.$getElementsPegsByMonth)
|
||||
;
|
||||
|
||||
@@ -19,6 +19,7 @@ import loadingIndicators from './api/loading-indicators';
|
||||
import { Common } from './api/common';
|
||||
import bitcoinClient from './api/bitcoin/bitcoin-client';
|
||||
import elementsParser from './api/liquid/elements-parser';
|
||||
import icons from './api/liquid/icons';
|
||||
|
||||
class Routes {
|
||||
constructor() {}
|
||||
@@ -807,6 +808,26 @@ class Routes {
|
||||
: (e.message || 'Error'));
|
||||
}
|
||||
}
|
||||
|
||||
public getLiquidIcon(req: Request, res: Response) {
|
||||
const result = icons.getIconByAssetId(req.params.assetId);
|
||||
if (result) {
|
||||
res.setHeader('content-type', 'image/png');
|
||||
res.setHeader('content-length', result.length);
|
||||
res.send(result);
|
||||
} else {
|
||||
res.status(404).send('Asset icon not found');
|
||||
}
|
||||
}
|
||||
|
||||
public getAllLiquidIcon(req: Request, res: Response) {
|
||||
const result = icons.getAllIconIds();
|
||||
if (result) {
|
||||
res.json(result);
|
||||
} else {
|
||||
res.status(404).send('Asset icons not found');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new Routes();
|
||||
|
||||
32
backend/src/sync-assets.ts
Normal file
32
backend/src/sync-assets.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import axios from 'axios';
|
||||
import * as fs from 'fs';
|
||||
const fsPromises = fs.promises;
|
||||
import config from './config';
|
||||
import logger from './logger';
|
||||
|
||||
const PATH = './';
|
||||
|
||||
class SyncAssets {
|
||||
constructor() { }
|
||||
|
||||
public async syncAssets() {
|
||||
for (const url of config.MEMPOOL.EXTERNAL_ASSETS) {
|
||||
await this.downloadFile(url);
|
||||
}
|
||||
}
|
||||
|
||||
private async downloadFile(url: string) {
|
||||
const fileName = url.split('/').slice(-1)[0];
|
||||
logger.info(`Downloading external asset: ${fileName}...`);
|
||||
try {
|
||||
const response = await axios.get(url, {
|
||||
responseType: 'stream', timeout: 30000
|
||||
});
|
||||
await fsPromises.writeFile(PATH + fileName, response.data);
|
||||
} catch (e: any) {
|
||||
throw new Error(`Failed to download external asset. ` + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new SyncAssets();
|
||||
@@ -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
|
||||
|
||||
@@ -59,9 +59,8 @@ describe('Bisq', () => {
|
||||
cy.visit(`${basePath}`);
|
||||
cy.waitForSkeletonGone();
|
||||
cy.get('li:nth-of-type(5) > a').click().then(() => {
|
||||
cy.get('.card').should('have.length.at.least', 1);
|
||||
cy.get('.card').first().click();
|
||||
cy.get('.card-body');
|
||||
cy.get('.section-header').should('have.length.at.least', 1);
|
||||
cy.get('.endpoint-container').should('have.length.at.least', 1);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
96
frontend/package-lock.json
generated
96
frontend/package-lock.json
generated
@@ -26,7 +26,7 @@
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.35",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.15.3",
|
||||
"@juggle/resize-observer": "^3.3.1",
|
||||
"@mempool/mempool.js": "^2.2.4",
|
||||
"@mempool/mempool.js": "2.3.0-dev1",
|
||||
"@ng-bootstrap/ng-bootstrap": "^10.0.0",
|
||||
"@nguniversal/express-engine": "11.2.1",
|
||||
"@types/qrcode": "1.4.1",
|
||||
@@ -3230,12 +3230,40 @@
|
||||
"integrity": "sha512-zMM9Ds+SawiUkakS7y94Ymqx+S0ORzpG3frZirN3l+UlXUmSUR7hF4wxCVqW+ei94JzV5kt0uXBcoOEAuiydrw=="
|
||||
},
|
||||
"node_modules/@mempool/mempool.js": {
|
||||
"version": "2.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@mempool/mempool.js/-/mempool.js-2.2.4.tgz",
|
||||
"integrity": "sha512-G9Ga2jHLfAuU/qXikRBtTecYr7BhLJH1WbIahefnGpgP48DCQaj1jizvuRZHhoElUvUT5flRt/O9kLjlbToqhw==",
|
||||
"version": "2.3.0-dev1",
|
||||
"resolved": "https://registry.npmjs.org/@mempool/mempool.js/-/mempool.js-2.3.0-dev1.tgz",
|
||||
"integrity": "sha512-+UYGuG8qqdgrtC4J94hCs7+Dry8OprIixEarIC6rww1Nb5POz8n3NTDH8to1r0XLjPr+Du6/OX8fEN1QW94rNA==",
|
||||
"dependencies": {
|
||||
"axios": "^0.21.1",
|
||||
"ws": "^7.4.3"
|
||||
"axios": "0.24.0",
|
||||
"ws": "8.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@mempool/mempool.js/node_modules/axios": {
|
||||
"version": "0.24.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz",
|
||||
"integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.14.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@mempool/mempool.js/node_modules/ws": {
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.3.0.tgz",
|
||||
"integrity": "sha512-Gs5EZtpqZzLvmIM59w4igITU57lrtYVFneaa434VROv4thzJyV6UjIL3D42lslWlI+D4KzLYnxSwtfuiO79sNw==",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": "^5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@ng-bootstrap/ng-bootstrap": {
|
||||
@@ -4541,6 +4569,7 @@
|
||||
"version": "0.21.4",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
|
||||
"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.14.0"
|
||||
}
|
||||
@@ -7264,7 +7293,7 @@
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.3.1"
|
||||
}
|
||||
@@ -9122,7 +9151,7 @@
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
|
||||
"integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
@@ -9131,7 +9160,7 @@
|
||||
"version": "5.1.5",
|
||||
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
|
||||
"integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ajv": "^6.12.3",
|
||||
"har-schema": "^2.0.0"
|
||||
@@ -9416,7 +9445,7 @@
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
|
||||
"integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"assert-plus": "^1.0.0",
|
||||
"jsprim": "^1.2.2",
|
||||
@@ -10385,7 +10414,7 @@
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
|
||||
"integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
|
||||
"devOptional": true
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
@@ -10453,7 +10482,7 @@
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
|
||||
"integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"assert-plus": "1.0.0",
|
||||
"extsprintf": "1.3.0",
|
||||
@@ -17962,6 +17991,7 @@
|
||||
"version": "7.4.6",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
|
||||
"integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==",
|
||||
"devOptional": true,
|
||||
"engines": {
|
||||
"node": ">=8.3.0"
|
||||
}
|
||||
@@ -20409,12 +20439,28 @@
|
||||
"integrity": "sha512-zMM9Ds+SawiUkakS7y94Ymqx+S0ORzpG3frZirN3l+UlXUmSUR7hF4wxCVqW+ei94JzV5kt0uXBcoOEAuiydrw=="
|
||||
},
|
||||
"@mempool/mempool.js": {
|
||||
"version": "2.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@mempool/mempool.js/-/mempool.js-2.2.4.tgz",
|
||||
"integrity": "sha512-G9Ga2jHLfAuU/qXikRBtTecYr7BhLJH1WbIahefnGpgP48DCQaj1jizvuRZHhoElUvUT5flRt/O9kLjlbToqhw==",
|
||||
"version": "2.3.0-dev1",
|
||||
"resolved": "https://registry.npmjs.org/@mempool/mempool.js/-/mempool.js-2.3.0-dev1.tgz",
|
||||
"integrity": "sha512-+UYGuG8qqdgrtC4J94hCs7+Dry8OprIixEarIC6rww1Nb5POz8n3NTDH8to1r0XLjPr+Du6/OX8fEN1QW94rNA==",
|
||||
"requires": {
|
||||
"axios": "^0.21.1",
|
||||
"ws": "^7.4.3"
|
||||
"axios": "0.24.0",
|
||||
"ws": "8.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": {
|
||||
"version": "0.24.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz",
|
||||
"integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==",
|
||||
"requires": {
|
||||
"follow-redirects": "^1.14.4"
|
||||
}
|
||||
},
|
||||
"ws": {
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.3.0.tgz",
|
||||
"integrity": "sha512-Gs5EZtpqZzLvmIM59w4igITU57lrtYVFneaa434VROv4thzJyV6UjIL3D42lslWlI+D4KzLYnxSwtfuiO79sNw==",
|
||||
"requires": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@ng-bootstrap/ng-bootstrap": {
|
||||
@@ -21532,6 +21578,7 @@
|
||||
"version": "0.21.4",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
|
||||
"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
|
||||
"devOptional": true,
|
||||
"requires": {
|
||||
"follow-redirects": "^1.14.0"
|
||||
}
|
||||
@@ -23795,7 +23842,7 @@
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
|
||||
"devOptional": true
|
||||
"dev": true
|
||||
},
|
||||
"diffie-hellman": {
|
||||
"version": "5.0.3",
|
||||
@@ -25290,13 +25337,13 @@
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
|
||||
"integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
|
||||
"devOptional": true
|
||||
"dev": true
|
||||
},
|
||||
"har-validator": {
|
||||
"version": "5.1.5",
|
||||
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
|
||||
"integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ajv": "^6.12.3",
|
||||
"har-schema": "^2.0.0"
|
||||
@@ -25544,7 +25591,7 @@
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
|
||||
"integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"assert-plus": "^1.0.0",
|
||||
"jsprim": "^1.2.2",
|
||||
@@ -26286,7 +26333,7 @@
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
|
||||
"integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
|
||||
"devOptional": true
|
||||
"dev": true
|
||||
},
|
||||
"json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
@@ -26339,7 +26386,7 @@
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
|
||||
"integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"assert-plus": "1.0.0",
|
||||
"extsprintf": "1.3.0",
|
||||
@@ -32158,7 +32205,8 @@
|
||||
"ws": {
|
||||
"version": "7.4.6",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
|
||||
"integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A=="
|
||||
"integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==",
|
||||
"devOptional": true
|
||||
},
|
||||
"xhr2": {
|
||||
"version": "0.2.0",
|
||||
|
||||
@@ -34,7 +34,10 @@
|
||||
"sync-assets": "node sync-assets.js && rsync -av ./dist/mempool/browser/en-US/resources ./dist/mempool/browser/resources",
|
||||
"sync-assets-dev": "node sync-assets.js dev",
|
||||
"generate-config": "node generate-config.js",
|
||||
"build-mempool.js": "tsc | browserify -p tinyify ./node_modules/@mempool/mempool.js/lib/index.js --standalone mempoolJS > ./dist/mempool/browser/en-US/mempool.js",
|
||||
"build-mempool.js": "npm run build-mempool-js && npm run build-mempool-liquid-js && npm run build-mempool-bisq-js",
|
||||
"build-mempool-js": "browserify -p tinyify ./node_modules/@mempool/mempool.js/lib/index.js --standalone mempoolJS > ./dist/mempool/browser/en-US/mempool.js",
|
||||
"build-mempool-bisq-js": "browserify -p tinyify ./node_modules/@mempool/mempool.js/lib/index-bisq.js --standalone bisqJS > ./dist/mempool/browser/en-US/bisq.js",
|
||||
"build-mempool-liquid-js": "browserify -p tinyify ./node_modules/@mempool/mempool.js/lib/index-liquid.js --standalone liquidJS > ./dist/mempool/browser/en-US/liquid.js",
|
||||
"test": "ng test",
|
||||
"lint": "ng lint",
|
||||
"e2e": "npm run generate-config && ng e2e",
|
||||
@@ -70,7 +73,7 @@
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.35",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.15.3",
|
||||
"@juggle/resize-observer": "^3.3.1",
|
||||
"@mempool/mempool.js": "^2.2.4",
|
||||
"@mempool/mempool.js": "2.3.0-dev1",
|
||||
"@ng-bootstrap/ng-bootstrap": "^10.0.0",
|
||||
"@nguniversal/express-engine": "11.2.1",
|
||||
"@types/qrcode": "1.4.1",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -48,9 +48,10 @@ import { FeesBoxComponent } from './components/fees-box/fees-box.component';
|
||||
import { DashboardComponent } from './dashboard/dashboard.component';
|
||||
import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome';
|
||||
import { faFilter, faAngleDown, faAngleUp, faAngleRight, faAngleLeft, faBolt, faChartArea, faCogs, faCubes, faDatabase, faExchangeAlt, faInfoCircle,
|
||||
faLink, faList, faSearch, faCaretUp, faCaretDown, faTachometerAlt, faThList, faTint, faTv, faAngleDoubleDown, faSortUp, faAngleDoubleUp, faChevronDown, faFileAlt, faRedoAlt, faArrowAltCircleRight, faExternalLinkAlt, faBook } from '@fortawesome/free-solid-svg-icons';
|
||||
faLink, faList, faSearch, faCaretUp, faCaretDown, faTachometerAlt, faThList, faTint, faTv, faAngleDoubleDown, faSortUp, faAngleDoubleUp, faChevronDown, faFileAlt, faRedoAlt, faArrowAltCircleRight, faExternalLinkAlt, faBook, faListUl } from '@fortawesome/free-solid-svg-icons';
|
||||
import { ApiDocsComponent } from './components/docs/api-docs.component';
|
||||
import { DocsComponent } from './components/docs/docs.component';
|
||||
import { ApiDocsNavComponent } from './components/docs/api-docs-nav.component';
|
||||
import { CodeTemplateComponent } from './components/docs/code-template.component';
|
||||
import { TermsOfServiceComponent } from './components/terms-of-service/terms-of-service.component';
|
||||
import { PrivacyPolicyComponent } from './components/privacy-policy/privacy-policy.component';
|
||||
@@ -59,6 +60,7 @@ import { StorageService } from './services/storage.service';
|
||||
import { HttpCacheInterceptor } from './services/http-cache.interceptor';
|
||||
import { SponsorComponent } from './components/sponsor/sponsor.component';
|
||||
import { PushTransactionComponent } from './components/push-transaction/push-transaction.component';
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@@ -102,6 +104,7 @@ import { PushTransactionComponent } from './components/push-transaction/push-tra
|
||||
SponsorComponent,
|
||||
PushTransactionComponent,
|
||||
DocsComponent,
|
||||
ApiDocsNavComponent,
|
||||
],
|
||||
imports: [
|
||||
BrowserModule.withServerTransition({ appId: 'serverApp' }),
|
||||
@@ -111,6 +114,7 @@ import { PushTransactionComponent } from './components/push-transaction/push-tra
|
||||
BrowserAnimationsModule,
|
||||
InfiniteScrollModule,
|
||||
NgbTypeaheadModule,
|
||||
NgbModule,
|
||||
FontAwesomeModule,
|
||||
SharedModule,
|
||||
NgxEchartsModule.forRoot({
|
||||
@@ -161,5 +165,6 @@ export class AppModule {
|
||||
library.addIcons(faAngleRight);
|
||||
library.addIcons(faAngleLeft);
|
||||
library.addIcons(faBook);
|
||||
library.addIcons(faListUl);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -23,12 +23,12 @@
|
||||
<a target="_blank" href="https://twitter.com/mempool">
|
||||
<svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="twitter" class="svg-inline--fa fa-twitter fa-w-16 fa-4x" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z"></path></svg>
|
||||
</a>
|
||||
<a target="_blank" href="https://t.me/mempoolspace">
|
||||
<svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="telegram-plane" class="svg-inline--fa fa-telegram-plane fa-w-14 fa-4x" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M446.7 98.6l-67.6 318.8c-5.1 22.5-18.4 28.1-37.3 17.5l-103-75.9-49.7 47.8c-5.5 5.5-10.1 10.1-20.7 10.1l7.4-104.9 190.9-172.5c8.3-7.4-1.8-11.5-12.9-4.1L117.8 284 16.2 252.2c-22.1-6.9-22.5-22.1 4.6-32.7L418.2 66.4c18.4-6.9 34.5 4.1 28.5 32.2z"></path></svg>
|
||||
</a>
|
||||
<a target="_blank" href="https://keybase.io/team/mempool">
|
||||
<svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="keybase" class="svg-inline--fa fa-keybase fa-w-14 fa-4x" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M286.17 419a18 18 0 1 0 18 18 18 18 0 0 0-18-18zm111.92-147.6c-9.5-14.62-39.37-52.45-87.26-73.71q-9.1-4.06-18.38-7.27a78.43 78.43 0 0 0-47.88-104.13c-12.41-4.1-23.33-6-32.41-5.77-.6-2-1.89-11 9.4-35L198.66 32l-5.48 7.56c-8.69 12.06-16.92 23.55-24.34 34.89a51 51 0 0 0-8.29-1.25c-41.53-2.45-39-2.33-41.06-2.33-50.61 0-50.75 52.12-50.75 45.88l-2.36 36.68c-1.61 27 19.75 50.21 47.63 51.85l8.93.54a214 214 0 0 0-46.29 35.54C14 304.66 14 374 14 429.77v33.64l23.32-29.8a148.6 148.6 0 0 0 14.56 37.56c5.78 10.13 14.87 9.45 19.64 7.33 4.21-1.87 10-6.92 3.75-20.11a178.29 178.29 0 0 1-15.76-53.13l46.82-59.83-24.66 74.11c58.23-42.4 157.38-61.76 236.25-38.59 34.2 10.05 67.45.69 84.74-23.84.72-1 1.2-2.16 1.85-3.22a156.09 156.09 0 0 1 2.8 28.43c0 23.3-3.69 52.93-14.88 81.64-2.52 6.46 1.76 14.5 8.6 15.74 7.42 1.57 15.33-3.1 18.37-11.15C429 443 434 414 434 382.32c0-38.58-13-77.46-35.91-110.92zM142.37 128.58l-15.7-.93-1.39 21.79 13.13.78a93 93 0 0 0 .32 19.57l-22.38-1.34a12.28 12.28 0 0 1-11.76-12.79L107 119c1-12.17 13.87-11.27 13.26-11.32l29.11 1.73a144.35 144.35 0 0 0-7 19.17zm148.42 172.18a10.51 10.51 0 0 1-14.35-1.39l-9.68-11.49-34.42 27a8.09 8.09 0 0 1-11.13-1.08l-15.78-18.64a7.38 7.38 0 0 1 1.34-10.34l34.57-27.18-14.14-16.74-17.09 13.45a7.75 7.75 0 0 1-10.59-1s-3.72-4.42-3.8-4.53a7.38 7.38 0 0 1 1.37-10.34L214 225.19s-18.51-22-18.6-22.14a9.56 9.56 0 0 1 1.74-13.42 10.38 10.38 0 0 1 14.3 1.37l81.09 96.32a9.58 9.58 0 0 1-1.74 13.44zM187.44 419a18 18 0 1 0 18 18 18 18 0 0 0-18-18z"></path></svg>
|
||||
</a>
|
||||
<a target="_blank" href="https://matrix.to/#/#mempool:bitcoin.kyoto">
|
||||
<svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="matrix" class="svg-inline--fa fa-matrix fa-w-16 fa-4x" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1536 1792"><path fill="currentColor" d="M40.467 163.152v1465.696H145.92V1664H0V128h145.92v35.152zm450.757 464.64v74.14h2.069c19.79-28.356 43.717-50.215 71.483-65.575 27.765-15.656 59.963-23.336 96-23.336 34.56 0 66.165 6.795 94.818 20.086 28.652 13.293 50.216 37.22 65.28 70.893 16.246-23.926 38.4-45.194 66.166-63.507 27.766-18.314 60.848-27.472 98.954-27.472 28.948 0 55.828 3.545 80.64 10.635 24.812 7.088 45.785 18.314 63.508 33.968 17.722 15.656 31.31 35.742 41.354 60.85 9.747 25.107 14.768 55.236 14.768 90.683v366.573h-150.35V865.28c0-18.314-.59-35.741-2.068-51.987-1.476-16.247-5.316-30.426-11.52-42.24-6.499-12.112-15.656-21.563-28.062-28.653-12.405-7.088-29.242-10.634-50.214-10.634-21.268 0-38.4 4.135-51.397 12.112-12.997 8.27-23.336 18.608-30.72 31.901-7.386 12.997-12.407 27.765-14.77 44.602-2.363 16.542-3.84 33.379-3.84 50.216v305.133H692.971v-307.2c0-16.247-.294-32.197-1.18-48.149-.591-15.95-3.84-30.424-9.157-44.011-5.317-13.293-14.178-24.223-26.585-32.197-12.406-7.976-30.425-12.112-54.646-12.112-7.088 0-16.542 1.478-28.062 4.726-11.52 3.25-23.04 9.157-33.968 18.02-10.93 8.86-20.383 21.563-28.063 38.103-7.68 16.543-11.52 38.4-11.52 65.28v317.834H349.44V627.792zm1004.309 1001.056V163.152H1390.08V128H1536v1536h-145.92v-35.152z"/></svg>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="enterprise-sponsor">
|
||||
@@ -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>
|
||||
|
||||
@@ -178,7 +178,7 @@
|
||||
</div>
|
||||
|
||||
<div class="maintainers" *ngIf="contributors.core.length">
|
||||
<h3 i18n="about.project_staff">Project Staff</h3>
|
||||
<h3 i18n="about.project_members">Project Members</h3>
|
||||
<div class="wrapper">
|
||||
<ng-template ngFor let-contributor [ngForOf]="contributors.core">
|
||||
<a [href]="'https://github.com/' + contributor.name" target="_blank" [title]="contributor.name">
|
||||
|
||||
@@ -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 } } });
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<div class="blocks-container blockchain-blocks-container" *ngIf="(loadingBlocks$ | async) === false; else loadingBlocksTemplate">
|
||||
<div *ngFor="let block of blocks; let i = index; trackBy: trackByBlocksFn" >
|
||||
<div class="text-center bitcoin-block mined-block blockchain-blocks-{{ i }}" id="bitcoin-block-{{ block.height }}" [ngStyle]="blockStyles[i]" [class.blink-bg]="(specialBlocks[block.height] !== undefined)">
|
||||
<a [routerLink]="['/block/' | relativeUrl, block.id]" [state]="{ data: { block: block } }" class="blockLink"> </a>
|
||||
<a draggable="false" [routerLink]="['/block/' | relativeUrl, block.id]" [state]="{ data: { block: block } }"
|
||||
class="blockLink" [ngClass]="{'disabled': (this.stateService.blockScrolling$ | async)}"> </a>
|
||||
<div class="block-height">
|
||||
<a [routerLink]="['/block/' | relativeUrl, block.id]" [state]="{ data: { block: block } }">{{ block.height }}</a>
|
||||
</div>
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.blockLink.disabled {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.mined-block {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
|
||||
@@ -36,18 +36,19 @@ export class BlockchainBlocksComponent implements OnInit, OnDestroy {
|
||||
'': ['#9339f4', '#105fb0'],
|
||||
bisq: ['#9339f4', '#105fb0'],
|
||||
liquid: ['#116761', '#183550'],
|
||||
'liquidtestnet': ['#494a4a', '#272e46'],
|
||||
testnet: ['#1d486f', '#183550'],
|
||||
signet: ['#6f1d5d', '#471850'],
|
||||
};
|
||||
|
||||
constructor(
|
||||
private stateService: StateService,
|
||||
public stateService: StateService,
|
||||
private router: Router,
|
||||
private cd: ChangeDetectorRef,
|
||||
) { }
|
||||
|
||||
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)));
|
||||
|
||||
@@ -18,6 +18,11 @@
|
||||
.blockchain-wrapper {
|
||||
overflow: hidden;
|
||||
height: 250px;
|
||||
|
||||
-webkit-user-select: none; /* Safari */
|
||||
-moz-user-select: none; /* Firefox */
|
||||
-ms-user-select: none; /* IE10+/Edge */
|
||||
user-select: none; /* Standard */
|
||||
}
|
||||
|
||||
.position-container {
|
||||
@@ -26,7 +31,7 @@
|
||||
top: 75px;
|
||||
}
|
||||
|
||||
.position-container.liquid {
|
||||
.position-container.liquid, .position-container.liquidtestnet {
|
||||
left: 420px;
|
||||
}
|
||||
|
||||
@@ -34,7 +39,7 @@
|
||||
.position-container {
|
||||
left: 95%;
|
||||
}
|
||||
.position-container.liquid {
|
||||
.position-container.liquid, .position-container.liquidtestnet {
|
||||
left: 50%;
|
||||
}
|
||||
.position-container.loading {
|
||||
|
||||
76
frontend/src/app/components/docs/api-docs-nav.component.html
Normal file
76
frontend/src/app/components/docs/api-docs-nav.component.html
Normal file
@@ -0,0 +1,76 @@
|
||||
<ng-template [ngIf]="network.val !== 'bisq' && network.val !== 'liquid'">
|
||||
<p>General</p>
|
||||
<a [routerLink]="['./']" fragment="get-difficulty-adjustment" (click)="collapseItem.toggle()">GET Difficulty Adjustment</a>
|
||||
</ng-template>
|
||||
|
||||
<ng-template [ngIf]="network.val === 'bisq'">
|
||||
<p>Markets</p>
|
||||
<a [routerLink]="['./']" fragment="get-market-currencies" (click)="collapseItem.toggle()">GET Market Currencies</a>
|
||||
<a [routerLink]="['./']" fragment="get-market-depth" (click)="collapseItem.toggle()">GET Market Depth</a>
|
||||
<a [routerLink]="['./']" fragment="get-market-hloc" (click)="collapseItem.toggle()">GET Market HLOC</a>
|
||||
<a [routerLink]="['./']" fragment="get-markets" (click)="collapseItem.toggle()">GET Markets</a>
|
||||
<a [routerLink]="['./']" fragment="get-market-offers" (click)="collapseItem.toggle()">GET Market Offers</a>
|
||||
<a [routerLink]="['./']" fragment="get-market-ticker" (click)="collapseItem.toggle()">GET Market Ticker</a>
|
||||
<a [routerLink]="['./']" fragment="get-market-trades" (click)="collapseItem.toggle()">GET Market Trades</a>
|
||||
<a [routerLink]="['./']" fragment="get-market-volumes" (click)="collapseItem.toggle()">GET Market Volumes</a>
|
||||
</ng-template>
|
||||
|
||||
<ng-template [ngIf]="network.val === 'bisq'">
|
||||
<p>General</p>
|
||||
<a [routerLink]="['./']" fragment="get-stats" (click)="collapseItem.toggle()">GET Stats</a>
|
||||
</ng-template>
|
||||
|
||||
<p>Addresses</p>
|
||||
<a [routerLink]="['./']" fragment="get-address" (click)="collapseItem.toggle()">GET Address</a>
|
||||
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-address-transactions" (click)="collapseItem.toggle()">GET Address Transactions</a>
|
||||
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-address-transactions-chain" (click)="collapseItem.toggle()">GET Address Transactions Chain</a>
|
||||
<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' || 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>
|
||||
<a [routerLink]="['./']" fragment="get-asset-transactions" (click)="collapseItem.toggle()">GET Asset Transactions</a>
|
||||
<a [routerLink]="['./']" fragment="get-asset-supply" (click)="collapseItem.toggle()">GET Asset Supply</a>
|
||||
<a [routerLink]="['./']" fragment="get-asset-icon" (click)="collapseItem.toggle()">GET Asset Icon</a>
|
||||
</ng-template>
|
||||
|
||||
<p>Blocks</p>
|
||||
<a [routerLink]="['./']" fragment="get-block" (click)="collapseItem.toggle()">GET Block</a>
|
||||
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-block-header" (click)="collapseItem.toggle()">GET Block Header</a>
|
||||
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-block-height" (click)="collapseItem.toggle()">GET Block Height</a>
|
||||
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-block-raw" (click)="collapseItem.toggle()">GET Block Raw</a>
|
||||
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-block-status" (click)="collapseItem.toggle()">GET Block Status</a>
|
||||
<a [routerLink]="['./']" fragment="get-block-tip-height" (click)="collapseItem.toggle()">GET Block Tip Height</a>
|
||||
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-block-tip-hash" (click)="collapseItem.toggle()">GET Block Tip Hash</a>
|
||||
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-block-transaction-id" (click)="collapseItem.toggle()">GET Block Transaction ID</a>
|
||||
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-block-transaction-ids" (click)="collapseItem.toggle()">GET Block Transaction IDs</a>
|
||||
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-block-transactions" (click)="collapseItem.toggle()">GET Block Transactions</a>
|
||||
<a [routerLink]="['./']" fragment="get-blocks" (click)="collapseItem.toggle()">GET Blocks</a>
|
||||
|
||||
<ng-template [ngIf]="network.val !== 'bisq'">
|
||||
<p>Fees</p>
|
||||
<a [routerLink]="['./']" fragment="get-mempool-blocks-fees" (click)="collapseItem.toggle()">GET Mempool Blocks Fees</a>
|
||||
<a [routerLink]="['./']" fragment="get-recommended-fees" (click)="collapseItem.toggle()">GET Recommended Fees</a>
|
||||
</ng-template>
|
||||
|
||||
<ng-template [ngIf]="network.val !== 'bisq'">
|
||||
<p>Mempool</p>
|
||||
<a [routerLink]="['./']" fragment="get-mempool" (click)="collapseItem.toggle()">GET Mempool</a>
|
||||
<a [routerLink]="['./']" fragment="get-mempool-transaction-ids" (click)="collapseItem.toggle()">GET Mempool Transaction IDs</a>
|
||||
<a [routerLink]="['./']" fragment="get-mempool-recent" (click)="collapseItem.toggle()">GET Mempool Recent</a>
|
||||
</ng-template>
|
||||
|
||||
<p>Transactions</p>
|
||||
<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' && 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>
|
||||
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-transaction-raw" (click)="collapseItem.toggle()">GET Transaction Raw</a>
|
||||
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-transaction-status" (click)="collapseItem.toggle()">GET Transaction Status</a>
|
||||
<a *ngIf="network.val === 'bisq'" [routerLink]="['./']" fragment="get-transactions" (click)="collapseItem.toggle()">GET Transactions</a>
|
||||
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="post-transaction" (click)="collapseItem.toggle()">POST Transaction</a>
|
||||
17
frontend/src/app/components/docs/api-docs-nav.component.scss
Normal file
17
frontend/src/app/components/docs/api-docs-nav.component.scss
Normal file
@@ -0,0 +1,17 @@
|
||||
p {
|
||||
color: #4a68b9;
|
||||
font-weight: 700;
|
||||
margin: 10px 0;
|
||||
margin: 15px 0 10px 0;
|
||||
}
|
||||
|
||||
p:first-child {
|
||||
margin-top: 0
|
||||
}
|
||||
|
||||
a {
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
margin: 5px 0;
|
||||
}
|
||||
18
frontend/src/app/components/docs/api-docs-nav.component.ts
Normal file
18
frontend/src/app/components/docs/api-docs-nav.component.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Component, OnInit, Input } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-api-docs-nav',
|
||||
templateUrl: './api-docs-nav.component.html',
|
||||
styleUrls: ['./api-docs-nav.component.scss']
|
||||
})
|
||||
export class ApiDocsNavComponent implements OnInit {
|
||||
|
||||
@Input() network: any;
|
||||
@Input() collapseItem: any = { toggle: () => {} };
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -22,6 +22,10 @@ li.nav-item {
|
||||
}
|
||||
}
|
||||
|
||||
.no-bottom-space {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.nav-tabs .nav-link.active {
|
||||
border-bottom: 1px solid #fff;
|
||||
@media (min-width: 676px){
|
||||
@@ -72,10 +76,131 @@ li.nav-item {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
#restAPI .api-category {
|
||||
margin: 30px 0;
|
||||
#doc-nav-desktop {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.api-category h4 {
|
||||
margin-bottom: 15px;
|
||||
#doc-nav-desktop.relative {
|
||||
float: left;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#doc-nav-desktop.fixed {
|
||||
float: unset;
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
overflow-y: auto;
|
||||
height: calc(100vh - 50px);
|
||||
scrollbar-color: #2d3348 #11131f;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
::-webkit-scrollbar {
|
||||
width: 3px;
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
background: #11131f;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: #2d3348;
|
||||
border-radius: 5px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.doc-content {
|
||||
width: calc(100% - 330px);
|
||||
float: right;
|
||||
}
|
||||
|
||||
.endpoint-container:before {
|
||||
display: block;
|
||||
content: " ";
|
||||
height: 1px;
|
||||
margin-top: -1px;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.endpoint-container .section-header {
|
||||
display: block;
|
||||
background-color: #2d3348;
|
||||
color: #1bd8f4;
|
||||
padding: 1rem 1.3rem 1rem 1.3rem;
|
||||
font-weight: bold;
|
||||
border-radius: 0.25rem;
|
||||
margin: 20px 0 20px 0;
|
||||
font-size: 24px;
|
||||
position: relative;
|
||||
}
|
||||
.endpoint-container .section-header:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.endpoint-container .section-header span {
|
||||
color: #fff;
|
||||
background-color: #653b9c;
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
font-weight: 400;
|
||||
padding: 8px 10px;
|
||||
letter-spacing: 1px;
|
||||
border-radius: 0.25rem;
|
||||
font-family: monospace;
|
||||
float: right;
|
||||
}
|
||||
|
||||
#doc-nav-mobile {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
width: calc(100% - 60px);
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
#doc-nav-mobile > div {
|
||||
background-color: #2d3348;
|
||||
z-index: 100;
|
||||
border-radius: 0 0 0.5rem 0.5rem;
|
||||
height: 55vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#doc-nav-mobile button {
|
||||
width: 100%;
|
||||
background-color: #105fb0;
|
||||
color: #fff;
|
||||
border-color: #105fb0;
|
||||
border-radius: 0.5rem 0.5rem 0 0;
|
||||
}
|
||||
|
||||
@media (max-width: 992px) {
|
||||
|
||||
.hide-on-mobile {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.doc-content {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.endpoint-container .section-header {
|
||||
margin: 40px 0 70px 0;
|
||||
}
|
||||
|
||||
.endpoint-container .section-header span {
|
||||
float: none;
|
||||
position: absolute;
|
||||
top: unset;
|
||||
left: 0;
|
||||
bottom: -50px;
|
||||
}
|
||||
|
||||
.endpoint-container:before {
|
||||
height: 30px;
|
||||
margin-top: -12px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
.hide-on-desktop {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, OnInit, Input } from '@angular/core';
|
||||
import { Component, OnInit, Input, ViewChild, ElementRef } from '@angular/core';
|
||||
import { Env, StateService } from 'src/app/services/state.service';
|
||||
import { Observable, merge, of } from 'rxjs';
|
||||
import { SeoService } from 'src/app/services/seo.service';
|
||||
@@ -17,12 +17,26 @@ export class ApiDocsComponent implements OnInit {
|
||||
code: any;
|
||||
baseNetworkUrl = '';
|
||||
@Input() restTabActivated: Boolean;
|
||||
@ViewChild( "mobileFixedApiNav", { static: false } ) mobileFixedApiNav: ElementRef;
|
||||
desktopDocsNavPosition = "relative";
|
||||
showFloatingDocsNav = false;
|
||||
mobileMenuOpen = true;
|
||||
|
||||
constructor(
|
||||
private stateService: StateService,
|
||||
private seoService: SeoService,
|
||||
) { }
|
||||
|
||||
ngAfterViewInit() {
|
||||
const that = this;
|
||||
setTimeout( () => {
|
||||
window.addEventListener('scroll', function() {
|
||||
that.desktopDocsNavPosition = ( window.pageYOffset > 182 ) ? "fixed" : "relative";
|
||||
that.showFloatingDocsNav = ( window.pageYOffset > ( that.mobileFixedApiNav.nativeElement.offsetHeight + 188 ) ) ? true : false;
|
||||
});
|
||||
}, 1 );
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.env = this.stateService.env;
|
||||
this.seoService.setTitle($localize`:@@e351b40b3869a5c7d19c3d4918cb1ac7aaab95c4:API`);
|
||||
@@ -628,24 +642,6 @@ export class ApiDocsComponent implements OnInit {
|
||||
console.log(asset);
|
||||
`,
|
||||
},
|
||||
codeSampleMainnet: {
|
||||
esModule: [],
|
||||
commonJS: [],
|
||||
curl: [],
|
||||
response: ''
|
||||
},
|
||||
codeSampleTestnet: {
|
||||
esModule: [],
|
||||
commonJS: [],
|
||||
curl: [],
|
||||
response: ''
|
||||
},
|
||||
codeSampleSignet: {
|
||||
esModule: [],
|
||||
commonJS: [],
|
||||
curl: [],
|
||||
response: ''
|
||||
},
|
||||
codeSampleLiquid: {
|
||||
esModule: [`6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d`],
|
||||
commonJS: [`6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d`],
|
||||
@@ -679,6 +675,47 @@ export class ApiDocsComponent implements OnInit {
|
||||
response: ''
|
||||
},
|
||||
},
|
||||
assetIcons: {
|
||||
codeTemplate: {
|
||||
curl: `/api/v1/assets/icons`,
|
||||
commonJS: `
|
||||
const { %{0}: { assets } } = mempoolJS();
|
||||
|
||||
const assetsIcons = await assets.getAssetsIcons();
|
||||
|
||||
document.getElementById("result").textContent = JSON.stringify(assetsIcons, undefined, 2);
|
||||
`,
|
||||
esModule: `
|
||||
const { %{0}: { assets } } = mempoolJS();
|
||||
|
||||
const assetsIcons = await assets.getAssetsIcons();
|
||||
console.log(assetsIcons);
|
||||
`,
|
||||
},
|
||||
codeSampleLiquid: {
|
||||
esModule: [`6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d`],
|
||||
commonJS: [`6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d`],
|
||||
curl: [`6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d`],
|
||||
response: `[
|
||||
"6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d",
|
||||
"ce091c998b83c78bb71a632313ba3760f1763d9cfcffae02258ffa9865a37bd2"
|
||||
...
|
||||
]`,
|
||||
},
|
||||
},
|
||||
assetIcon: {
|
||||
noWrap: true,
|
||||
codeTemplate: {
|
||||
curl: `/api/v1/asset/%{1}/icon`,
|
||||
commonJS: `<img src="https://liquid.place/api/v1/asset/%{1}/icon">`,
|
||||
},
|
||||
codeSampleLiquid: {
|
||||
esModule: [`6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d`],
|
||||
commonJS: [`6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d`],
|
||||
curl: [`6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d`],
|
||||
response: `PNG`,
|
||||
},
|
||||
},
|
||||
assetTransactions: {
|
||||
codeTemplate: {
|
||||
curl: `/api/asset/%{1}/txs`,
|
||||
@@ -3626,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;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3642,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,13 +157,17 @@ 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') {
|
||||
codeText = this.replaceJSPlaceholder(codeText, code.codeSampleBisq.esModule);
|
||||
}
|
||||
|
||||
if (code.noWrap) {
|
||||
return codeText;
|
||||
}
|
||||
|
||||
let importText = `<script src="https://mempool.space/mempool.js"></script>`;
|
||||
if (this.env.BASE_MODULE === 'bisq') {
|
||||
importText = `<script src="https://bisq.markets/bisq.js"></script>`;
|
||||
@@ -233,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') {
|
||||
@@ -255,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') {
|
||||
|
||||
@@ -27,5 +27,13 @@
|
||||
|
||||
<div id="main-tab-content" [ngbNavOutlet]="nav" class="mt-2"></div>
|
||||
|
||||
<br>
|
||||
|
||||
<div id="footer" class="text-center">
|
||||
<a [routerLink]="['/terms-of-service']" i18n="shared.terms-of-service|Terms of Service">Terms of Service</a>
|
||||
|
|
||||
<a [routerLink]="['/privacy-policy']" i18n="shared.privacy-policy|Privacy Policy">Privacy Policy</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
#main-tab-content {
|
||||
text-align: left;
|
||||
padding-top: 10px;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
#footer {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
<div class="flashing">
|
||||
<ng-template ngFor let-projectedBlock [ngForOf]="mempoolBlocks$ | async" let-i="index" [ngForTrackBy]="trackByFn">
|
||||
<div class="bitcoin-block text-center mempool-block" id="mempool-block-{{ i }}" [ngStyle]="mempoolBlockStyles[i]" [class.blink-bg]="projectedBlock.blink">
|
||||
<a [routerLink]="['/mempool-block/' | relativeUrl, i]" class="blockLink"> </a>
|
||||
<a draggable="false" [routerLink]="['/mempool-block/' | relativeUrl, i]"
|
||||
class="blockLink" [ngClass]="{'disabled': (this.stateService.blockScrolling$ | async)}"> </a>
|
||||
<div class="block-body">
|
||||
<div class="fees">
|
||||
~{{ projectedBlock.medianFee | number:feeRounding }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span>
|
||||
@@ -18,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>
|
||||
|
||||
@@ -117,6 +117,10 @@
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.blockLink.disabled {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.blockLink:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -8,8 +8,11 @@
|
||||
|
||||
<div *ngIf="countdown > 0" class="warning-label">{{ eventName }} in {{ countdown | number }} block{{ countdown === 1 ? '' : 's' }}!</div>
|
||||
|
||||
<div id="blockchain-container" dir="ltr">
|
||||
<app-blockchain></app-blockchain>
|
||||
<div id="blockchain-container" dir="ltr" #blockchainContainer
|
||||
(mousedown)="onMouseDown($event)"
|
||||
(dragstart)="onDragStart($event)"
|
||||
>
|
||||
<app-blockchain></app-blockchain>
|
||||
</div>
|
||||
|
||||
<router-outlet></router-outlet>
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component, ElementRef, HostListener, OnInit, ViewChild } from '@angular/core';
|
||||
import { WebsocketService } from 'src/app/services/websocket.service';
|
||||
import { StateService } from 'src/app/services/state.service';
|
||||
import { specialBlocks } from 'src/app/app.constants';
|
||||
import { takeLast } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'app-start',
|
||||
@@ -16,6 +15,9 @@ export class StartComponent implements OnInit {
|
||||
countdown = 0;
|
||||
specialEvent = false;
|
||||
eventName = '';
|
||||
mouseDragStartX: number;
|
||||
blockchainScrollLeftInit: number;
|
||||
@ViewChild('blockchainContainer') blockchainContainer: ElementRef;
|
||||
|
||||
constructor(
|
||||
private websocketService: WebsocketService,
|
||||
@@ -50,4 +52,27 @@ export class StartComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
onMouseDown(event: MouseEvent) {
|
||||
this.mouseDragStartX = event.clientX;
|
||||
this.blockchainScrollLeftInit = this.blockchainContainer.nativeElement.scrollLeft;
|
||||
}
|
||||
onDragStart(event: MouseEvent) { // Ignore Firefox annoying default drag behavior
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
// We're catching the whole page event here because we still want to scroll blocks
|
||||
// even if the mouse leave the blockchain blocks container. Same idea for mouseup below.
|
||||
@HostListener('document:mousemove', ['$event'])
|
||||
onMouseMove(event: MouseEvent): void {
|
||||
if (this.mouseDragStartX != null) {
|
||||
this.stateService.setBlockScrollingInProgress(true);
|
||||
this.blockchainContainer.nativeElement.scrollLeft =
|
||||
this.blockchainScrollLeftInit + this.mouseDragStartX - event.clientX
|
||||
}
|
||||
}
|
||||
@HostListener('document:mouseup', [])
|
||||
onMouseUp() {
|
||||
this.mouseDragStartX = null;
|
||||
this.stateService.setBlockScrollingInProgress(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
@@ -89,6 +91,8 @@ export class StateService {
|
||||
markBlock$ = new ReplaySubject<MarkBlockState>();
|
||||
keyNavigation$ = new Subject<KeyboardEvent>();
|
||||
|
||||
blockScrolling$: Subject<boolean> = new Subject<boolean>();
|
||||
|
||||
constructor(
|
||||
@Inject(PLATFORM_ID) private platformId: any,
|
||||
private router: Router,
|
||||
@@ -114,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);
|
||||
}
|
||||
@@ -123,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') {
|
||||
@@ -134,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';
|
||||
@@ -142,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':
|
||||
@@ -153,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('');
|
||||
}
|
||||
@@ -176,4 +196,12 @@ export class StateService {
|
||||
if (!prop) { return false; }
|
||||
return document[prop];
|
||||
}
|
||||
|
||||
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
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user