Compare commits
37 Commits
v2.2.2-rc2
...
v2.2.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
251a1af442 | ||
|
|
9bdf42530a | ||
|
|
8525fbb177 | ||
|
|
63a3568481 | ||
|
|
e4941740de | ||
|
|
25bd33f7da | ||
|
|
2d007b9100 | ||
|
|
b71330c606 | ||
|
|
844b640c8c | ||
|
|
1277e58e68 | ||
|
|
fde6fe324a | ||
|
|
4ed114a4d5 | ||
|
|
8c2dfea6a6 | ||
|
|
a0624df06b | ||
|
|
1eedcf900b | ||
|
|
0b077d6fda | ||
|
|
80047313e7 | ||
|
|
71229b94c8 | ||
|
|
c256daf8c8 | ||
|
|
ba0fb996d2 | ||
|
|
5977e96034 | ||
|
|
a151c5cddd | ||
|
|
0323fd966d | ||
|
|
beb834bc30 | ||
|
|
ad6503c7b3 | ||
|
|
f8c11c8b6b | ||
|
|
ba5421e77b | ||
|
|
20fa803cee | ||
|
|
393fa78a43 | ||
|
|
3f290dae06 | ||
|
|
24d18b9f2f | ||
|
|
79ef8ca371 | ||
|
|
ec12f21113 | ||
|
|
2e8ecc7277 | ||
|
|
fc28b06a0f | ||
|
|
8fdbfdc04c | ||
|
|
bdfcfc96a8 |
4
.vscode/settings.json
vendored
Normal file
4
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"editor.tabSize": 2,
|
||||||
|
"typescript.tsdk": "./backend/node_modules/typescript/lib"
|
||||||
|
}
|
||||||
4
backend/.vscode/settings.json
vendored
Normal file
4
backend/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"editor.tabSize": 2,
|
||||||
|
"typescript.tsdk": "../backend/node_modules/typescript/lib"
|
||||||
|
}
|
||||||
@@ -11,7 +11,8 @@
|
|||||||
"RECOMMENDED_FEE_PERCENTILE": 50,
|
"RECOMMENDED_FEE_PERCENTILE": 50,
|
||||||
"BLOCK_WEIGHT_UNITS": 4000000,
|
"BLOCK_WEIGHT_UNITS": 4000000,
|
||||||
"INITIAL_BLOCKS_AMOUNT": 8,
|
"INITIAL_BLOCKS_AMOUNT": 8,
|
||||||
"MEMPOOL_BLOCKS_AMOUNT": 8
|
"MEMPOOL_BLOCKS_AMOUNT": 8,
|
||||||
|
"PRICE_FEED_UPDATE_INTERVAL": 3600
|
||||||
},
|
},
|
||||||
"CORE_RPC": {
|
"CORE_RPC": {
|
||||||
"HOST": "127.0.0.1",
|
"HOST": "127.0.0.1",
|
||||||
|
|||||||
22
backend/package-lock.json
generated
22
backend/package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "mempool-backend",
|
"name": "mempool-backend",
|
||||||
"version": "2.2.2-dev",
|
"version": "2.2.2",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "mempool-backend",
|
"name": "mempool-backend",
|
||||||
"version": "2.2.2-dev",
|
"version": "2.2.2",
|
||||||
"license": "GNU Affero General Public License v3.0",
|
"license": "GNU Affero General Public License v3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mempool/bitcoin": "^3.0.3",
|
"@mempool/bitcoin": "^3.0.3",
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
"@types/locutus": "^0.0.6",
|
"@types/locutus": "^0.0.6",
|
||||||
"@types/ws": "^7.4.4",
|
"@types/ws": "^7.4.4",
|
||||||
"tslint": "^6.1.0",
|
"tslint": "^6.1.0",
|
||||||
"typescript": "^4.1.5"
|
"typescript": "4.4.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/code-frame": {
|
"node_modules/@babel/code-frame": {
|
||||||
@@ -1473,10 +1473,11 @@
|
|||||||
"integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g=="
|
"integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g=="
|
||||||
},
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "4.2.3",
|
"version": "4.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.2.tgz",
|
||||||
"integrity": "sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==",
|
"integrity": "sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
@@ -2770,9 +2771,9 @@
|
|||||||
"integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g=="
|
"integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g=="
|
||||||
},
|
},
|
||||||
"typescript": {
|
"typescript": {
|
||||||
"version": "4.2.3",
|
"version": "4.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.2.tgz",
|
||||||
"integrity": "sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==",
|
"integrity": "sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"unpipe": {
|
"unpipe": {
|
||||||
@@ -2820,7 +2821,8 @@
|
|||||||
"ws": {
|
"ws": {
|
||||||
"version": "7.4.6",
|
"version": "7.4.6",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
|
"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==",
|
||||||
|
"requires": {}
|
||||||
},
|
},
|
||||||
"yallist": {
|
"yallist": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "mempool-backend",
|
"name": "mempool-backend",
|
||||||
"version": "2.2.2-dev",
|
"version": "2.2.2",
|
||||||
"description": "Bitcoin mempool visualizer and blockchain explorer backend",
|
"description": "Bitcoin mempool visualizer and blockchain explorer backend",
|
||||||
"license": "GNU Affero General Public License v3.0",
|
"license": "GNU Affero General Public License v3.0",
|
||||||
"homepage": "https://mempool.space",
|
"homepage": "https://mempool.space",
|
||||||
@@ -45,6 +45,6 @@
|
|||||||
"@types/locutus": "^0.0.6",
|
"@types/locutus": "^0.0.6",
|
||||||
"@types/ws": "^7.4.4",
|
"@types/ws": "^7.4.4",
|
||||||
"tslint": "^6.1.0",
|
"tslint": "^6.1.0",
|
||||||
"typescript": "^4.1.5"
|
"typescript": "4.4.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ class BackendInfo {
|
|||||||
try {
|
try {
|
||||||
this.gitCommitHash = fs.readFileSync('../.git/refs/heads/master').toString().trim();
|
this.gitCommitHash = fs.readFileSync('../.git/refs/heads/master').toString().trim();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.err('Could not load git commit info: ' + e.message || e);
|
logger.err('Could not load git commit info: ' + (e instanceof Error ? e.message : e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ class BackendInfo {
|
|||||||
const packageJson = fs.readFileSync('package.json').toString();
|
const packageJson = fs.readFileSync('package.json').toString();
|
||||||
this.version = JSON.parse(packageJson).version;
|
this.version = JSON.parse(packageJson).version;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error(e);
|
throw new Error(e instanceof Error ? e.message : 'Error');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ class Bisq {
|
|||||||
this.buildIndex();
|
this.buildIndex();
|
||||||
this.calculateStats();
|
this.calculateStats();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.info('loadBisqDumpFile() error.' + e.message || e);
|
logger.info('loadBisqDumpFile() error.' + (e instanceof Error ? e.message : e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ class Bisq {
|
|||||||
logger.debug('Bisq market data updated in ' + time + ' ms');
|
logger.debug('Bisq market data updated in ' + time + ' ms');
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.err('loadBisqMarketDataDumpFile() error.' + e.message || e);
|
logger.err('loadBisqMarketDataDumpFile() error.' + (e instanceof Error ? e.message : e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -98,12 +98,15 @@ export namespace IBitcoinApi {
|
|||||||
|
|
||||||
export interface AddressInformation {
|
export interface AddressInformation {
|
||||||
isvalid: boolean; // (boolean) If the address is valid or not. If not, this is the only property returned.
|
isvalid: boolean; // (boolean) If the address is valid or not. If not, this is the only property returned.
|
||||||
|
isvalid_parent?: boolean; // (boolean) Elements only
|
||||||
address: string; // (string) The bitcoin address validated
|
address: string; // (string) The bitcoin address validated
|
||||||
scriptPubKey: string; // (string) The hex-encoded scriptPubKey generated by the address
|
scriptPubKey: string; // (string) The hex-encoded scriptPubKey generated by the address
|
||||||
isscript: boolean; // (boolean) If the key is a script
|
isscript: boolean; // (boolean) If the key is a script
|
||||||
iswitness: boolean; // (boolean) If the address is a witness
|
iswitness: boolean; // (boolean) If the address is a witness
|
||||||
witness_version?: boolean; // (numeric, optional) The version number of the witness program
|
witness_version?: boolean; // (numeric, optional) The version number of the witness program
|
||||||
witness_program: string; // (string, optional) The hex value of the witness program
|
witness_program: string; // (string, optional) The hex value of the witness program
|
||||||
|
confidential_key?: string; // (string) Elements only
|
||||||
|
unconfidential?: string; // (string) Elements only
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ChainTips {
|
export interface ChainTips {
|
||||||
|
|||||||
@@ -60,13 +60,12 @@ class BitcoinApi implements AbstractBitcoinApi {
|
|||||||
return this.bitcoindClient.getBlock(hash, 0);
|
return this.bitcoindClient.getBlock(hash, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$getBlockHash(height: number): Promise<string> {
|
$getBlockHash(height: number): Promise<string> {
|
||||||
return this.bitcoindClient.getBlockHash(height);
|
return this.bitcoindClient.getBlockHash(height);
|
||||||
}
|
}
|
||||||
|
|
||||||
$getBlockHeader(hash: string): Promise<string> {
|
$getBlockHeader(hash: string): Promise<string> {
|
||||||
return this.bitcoindClient.getBlockHeader(hash,false);
|
return this.bitcoindClient.getBlockHeader(hash, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
async $getBlock(hash: string): Promise<IEsploraApi.Block> {
|
async $getBlock(hash: string): Promise<IEsploraApi.Block> {
|
||||||
@@ -238,10 +237,6 @@ class BitcoinApi implements AbstractBitcoinApi {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected $validateAddress(address: string): Promise<IBitcoinApi.AddressInformation> {
|
|
||||||
return this.bitcoindClient.validateAddress(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
private $getMempoolEntry(txid: string): Promise<IBitcoinApi.MempoolEntry> {
|
private $getMempoolEntry(txid: string): Promise<IBitcoinApi.MempoolEntry> {
|
||||||
return this.bitcoindClient.getMempoolEntry(txid);
|
return this.bitcoindClient.getMempoolEntry(txid);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,10 @@ class BitcoinBaseApi {
|
|||||||
$getBlockchainInfo(): Promise<IBitcoinApi.BlockchainInfo> {
|
$getBlockchainInfo(): Promise<IBitcoinApi.BlockchainInfo> {
|
||||||
return this.bitcoindClient.getBlockchainInfo();
|
return this.bitcoindClient.getBlockchainInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$validateAddress(address: string): Promise<IBitcoinApi.AddressInformation> {
|
||||||
|
return this.bitcoindClient.validateAddress(address);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new BitcoinBaseApi();
|
export default new BitcoinBaseApi();
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import * as sha256 from 'crypto-js/sha256';
|
|||||||
import * as hexEnc from 'crypto-js/enc-hex';
|
import * as hexEnc from 'crypto-js/enc-hex';
|
||||||
import loadingIndicators from '../loading-indicators';
|
import loadingIndicators from '../loading-indicators';
|
||||||
import memoryCache from '../memory-cache';
|
import memoryCache from '../memory-cache';
|
||||||
|
import bitcoinBaseApi from './bitcoin-base.api';
|
||||||
|
|
||||||
class BitcoindElectrsApi extends BitcoinApi implements AbstractBitcoinApi {
|
class BitcoindElectrsApi extends BitcoinApi implements AbstractBitcoinApi {
|
||||||
private electrumClient: any;
|
private electrumClient: any;
|
||||||
@@ -44,7 +45,7 @@ class BitcoindElectrsApi extends BitcoinApi implements AbstractBitcoinApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async $getAddress(address: string): Promise<IEsploraApi.Address> {
|
async $getAddress(address: string): Promise<IEsploraApi.Address> {
|
||||||
const addressInfo = await this.$validateAddress(address);
|
const addressInfo = await bitcoinBaseApi.$validateAddress(address);
|
||||||
if (!addressInfo || !addressInfo.isvalid) {
|
if (!addressInfo || !addressInfo.isvalid) {
|
||||||
return ({
|
return ({
|
||||||
'address': address,
|
'address': address,
|
||||||
@@ -93,12 +94,12 @@ class BitcoindElectrsApi extends BitcoinApi implements AbstractBitcoinApi {
|
|||||||
if (e === 'failed to get confirmed status') {
|
if (e === 'failed to get confirmed status') {
|
||||||
e = 'The number of transactions on this address exceeds the Electrum server limit';
|
e = 'The number of transactions on this address exceeds the Electrum server limit';
|
||||||
}
|
}
|
||||||
throw new Error(e);
|
throw new Error(typeof e === 'string' ? e : 'Error');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async $getAddressTransactions(address: string, lastSeenTxId: string): Promise<IEsploraApi.Transaction[]> {
|
async $getAddressTransactions(address: string, lastSeenTxId: string): Promise<IEsploraApi.Transaction[]> {
|
||||||
const addressInfo = await this.$validateAddress(address);
|
const addressInfo = await bitcoinBaseApi.$validateAddress(address);
|
||||||
if (!addressInfo || !addressInfo.isvalid) {
|
if (!addressInfo || !addressInfo.isvalid) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@@ -131,7 +132,7 @@ class BitcoindElectrsApi extends BitcoinApi implements AbstractBitcoinApi {
|
|||||||
if (e === 'failed to get confirmed status') {
|
if (e === 'failed to get confirmed status') {
|
||||||
e = 'The number of transactions on this address exceeds the Electrum server limit';
|
e = 'The number of transactions on this address exceeds the Electrum server limit';
|
||||||
}
|
}
|
||||||
throw new Error(e);
|
throw new Error(typeof e === 'string' ? e : 'Error');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ class Blocks {
|
|||||||
const tx = await transactionUtils.$getTransactionExtended(txIds[i]);
|
const tx = await transactionUtils.$getTransactionExtended(txIds[i]);
|
||||||
transactions.push(tx);
|
transactions.push(tx);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.debug('Error fetching block tx: ' + e.message || e);
|
logger.debug('Error fetching block tx: ' + (e instanceof Error ? e.message : e));
|
||||||
if (i === 0) {
|
if (i === 0) {
|
||||||
throw new Error('Failed to fetch Coinbase transaction: ' + txIds[i]);
|
throw new Error('Failed to fetch Coinbase transaction: ' + txIds[i]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class DiskCache {
|
|||||||
logger.debug('Mempool and blocks data saved to disk cache');
|
logger.debug('Mempool and blocks data saved to disk cache');
|
||||||
this.isWritingCache = false;
|
this.isWritingCache = false;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.warn('Error writing to cache file: ' + e.message || e);
|
logger.warn('Error writing to cache file: ' + (e instanceof Error ? e.message : e));
|
||||||
this.isWritingCache = false;
|
this.isWritingCache = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import logger from '../logger';
|
import logger from '../logger';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { IConversionRates } from '../mempool.interfaces';
|
import { IConversionRates } from '../mempool.interfaces';
|
||||||
|
import config from '../config';
|
||||||
|
|
||||||
class FiatConversion {
|
class FiatConversion {
|
||||||
private conversionRates: IConversionRates = {
|
private conversionRates: IConversionRates = {
|
||||||
@@ -16,7 +17,7 @@ class FiatConversion {
|
|||||||
|
|
||||||
public startService() {
|
public startService() {
|
||||||
logger.info('Starting currency rates service');
|
logger.info('Starting currency rates service');
|
||||||
setInterval(this.updateCurrency.bind(this), 1000 * 60);
|
setInterval(this.updateCurrency.bind(this), 1000 * config.MEMPOOL.PRICE_FEED_UPDATE_INTERVAL);
|
||||||
this.updateCurrency();
|
this.updateCurrency();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,7 +36,7 @@ class FiatConversion {
|
|||||||
this.ratesChangedCallback(this.conversionRates);
|
this.ratesChangedCallback(this.conversionRates);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.err('Error updating fiat conversion rates: ' + e);
|
logger.err('Error updating fiat conversion rates: ' + (e instanceof Error ? e.message : e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ class Mempool {
|
|||||||
}
|
}
|
||||||
newTransactions.push(transaction);
|
newTransactions.push(transaction);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.debug('Error finding transaction in mempool: ' + e.message || e);
|
logger.debug('Error finding transaction in mempool: ' + (e instanceof Error ? e.message : e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -255,7 +255,7 @@ class Statistics {
|
|||||||
connection.release();
|
connection.release();
|
||||||
return result.insertId;
|
return result.insertId;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.err('$create() error' + e.message || e);
|
logger.err('$create() error' + (e instanceof Error ? e.message : e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -313,7 +313,7 @@ class Statistics {
|
|||||||
return this.mapStatisticToOptimizedStatistic([rows[0]])[0];
|
return this.mapStatisticToOptimizedStatistic([rows[0]])[0];
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.err('$list2H() error' + e.message || e);
|
logger.err('$list2H() error' + (e instanceof Error ? e.message : e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,7 +325,7 @@ class Statistics {
|
|||||||
connection.release();
|
connection.release();
|
||||||
return this.mapStatisticToOptimizedStatistic(rows);
|
return this.mapStatisticToOptimizedStatistic(rows);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.err('$list2H() error' + e.message || e);
|
logger.err('$list2H() error' + (e instanceof Error ? e.message : e));
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -338,7 +338,7 @@ class Statistics {
|
|||||||
connection.release();
|
connection.release();
|
||||||
return this.mapStatisticToOptimizedStatistic(rows);
|
return this.mapStatisticToOptimizedStatistic(rows);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.err('$list24h() error' + e.message || e);
|
logger.err('$list24h() error' + (e instanceof Error ? e.message : e));
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -351,7 +351,7 @@ class Statistics {
|
|||||||
connection.release();
|
connection.release();
|
||||||
return this.mapStatisticToOptimizedStatistic(rows);
|
return this.mapStatisticToOptimizedStatistic(rows);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.err('$list1W() error' + e);
|
logger.err('$list1W() error' + (e instanceof Error ? e.message : e));
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -364,7 +364,7 @@ class Statistics {
|
|||||||
connection.release();
|
connection.release();
|
||||||
return this.mapStatisticToOptimizedStatistic(rows);
|
return this.mapStatisticToOptimizedStatistic(rows);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.err('$list1M() error' + e);
|
logger.err('$list1M() error' + (e instanceof Error ? e.message : e));
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -377,7 +377,7 @@ class Statistics {
|
|||||||
connection.release();
|
connection.release();
|
||||||
return this.mapStatisticToOptimizedStatistic(rows);
|
return this.mapStatisticToOptimizedStatistic(rows);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.err('$list3M() error' + e);
|
logger.err('$list3M() error' + (e instanceof Error ? e.message : e));
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -390,7 +390,7 @@ class Statistics {
|
|||||||
connection.release();
|
connection.release();
|
||||||
return this.mapStatisticToOptimizedStatistic(rows);
|
return this.mapStatisticToOptimizedStatistic(rows);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.err('$list6M() error' + e);
|
logger.err('$list6M() error' + (e instanceof Error ? e.message : e));
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -403,7 +403,7 @@ class Statistics {
|
|||||||
connection.release();
|
connection.release();
|
||||||
return this.mapStatisticToOptimizedStatistic(rows);
|
return this.mapStatisticToOptimizedStatistic(rows);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.err('$list6M() error' + e);
|
logger.err('$list6M() error' + (e instanceof Error ? e.message : e));
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ class WebsocketHandler {
|
|||||||
const fullTx = await transactionUtils.$getTransactionExtended(tx.txid, true);
|
const fullTx = await transactionUtils.$getTransactionExtended(tx.txid, true);
|
||||||
response['tx'] = fullTx;
|
response['tx'] = fullTx;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.debug('Error finding transaction: ' + e.message || e);
|
logger.debug('Error finding transaction: ' + (e instanceof Error ? e.message : e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -69,7 +69,7 @@ class WebsocketHandler {
|
|||||||
const fullTx = await transactionUtils.$getTransactionExtended(client['track-tx'], true);
|
const fullTx = await transactionUtils.$getTransactionExtended(client['track-tx'], true);
|
||||||
response['tx'] = fullTx;
|
response['tx'] = fullTx;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.debug('Error finding transaction. ' + e.message || e);
|
logger.debug('Error finding transaction. ' + (e instanceof Error ? e.message : e));
|
||||||
client['track-mempool-tx'] = parsedMessage['track-tx'];
|
client['track-mempool-tx'] = parsedMessage['track-tx'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,9 +80,13 @@ class WebsocketHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (parsedMessage && parsedMessage['track-address']) {
|
if (parsedMessage && parsedMessage['track-address']) {
|
||||||
if (/^([a-km-zA-HJ-NP-Z1-9]{26,35}|[a-km-zA-HJ-NP-Z1-9]{80}|[a-z]{2,5}1[ac-hj-np-z02-9]{8,87})$/
|
if (/^([a-km-zA-HJ-NP-Z1-9]{26,35}|[a-km-zA-HJ-NP-Z1-9]{80}|[a-z]{2,5}1[ac-hj-np-z02-9]{8,100}|[A-Z]{2,5}1[AC-HJ-NP-Z02-9]{8,100})$/
|
||||||
.test(parsedMessage['track-address'])) {
|
.test(parsedMessage['track-address'])) {
|
||||||
client['track-address'] = parsedMessage['track-address'];
|
let matchedAddress = parsedMessage['track-address'];
|
||||||
|
if (/^[A-Z]{2,5}1[AC-HJ-NP-Z02-9]{8,100}$/.test(parsedMessage['track-address'])) {
|
||||||
|
matchedAddress = matchedAddress.toLowerCase();
|
||||||
|
}
|
||||||
|
client['track-address'] = matchedAddress;
|
||||||
} else {
|
} else {
|
||||||
client['track-address'] = null;
|
client['track-address'] = null;
|
||||||
}
|
}
|
||||||
@@ -124,7 +128,7 @@ class WebsocketHandler {
|
|||||||
client.send(JSON.stringify(response));
|
client.send(JSON.stringify(response));
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.debug('Error parsing websocket message: ' + e.message || e);
|
logger.debug('Error parsing websocket message: ' + (e instanceof Error ? e.message : e));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -252,7 +256,7 @@ class WebsocketHandler {
|
|||||||
const fullTx = await transactionUtils.$getTransactionExtended(tx.txid, true);
|
const fullTx = await transactionUtils.$getTransactionExtended(tx.txid, true);
|
||||||
response['tx'] = fullTx;
|
response['tx'] = fullTx;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.debug('Error finding transaction in mempool: ' + e.message || e);
|
logger.debug('Error finding transaction in mempool: ' + (e instanceof Error ? e.message : e));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
response['tx'] = tx;
|
response['tx'] = tx;
|
||||||
@@ -272,7 +276,7 @@ class WebsocketHandler {
|
|||||||
const fullTx = await transactionUtils.$getTransactionExtended(tx.txid, true);
|
const fullTx = await transactionUtils.$getTransactionExtended(tx.txid, true);
|
||||||
foundTransactions.push(fullTx);
|
foundTransactions.push(fullTx);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.debug('Error finding transaction in mempool: ' + e.message || e);
|
logger.debug('Error finding transaction in mempool: ' + (e instanceof Error ? e.message : e));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
foundTransactions.push(tx);
|
foundTransactions.push(tx);
|
||||||
@@ -286,7 +290,7 @@ class WebsocketHandler {
|
|||||||
const fullTx = await transactionUtils.$getTransactionExtended(tx.txid, true);
|
const fullTx = await transactionUtils.$getTransactionExtended(tx.txid, true);
|
||||||
foundTransactions.push(fullTx);
|
foundTransactions.push(fullTx);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.debug('Error finding transaction in mempool: ' + e.message || e);
|
logger.debug('Error finding transaction in mempool: ' + (e instanceof Error ? e.message : e));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
foundTransactions.push(tx);
|
foundTransactions.push(tx);
|
||||||
@@ -337,7 +341,7 @@ class WebsocketHandler {
|
|||||||
const fullTx = await transactionUtils.$getTransactionExtended(rbfTransaction, true);
|
const fullTx = await transactionUtils.$getTransactionExtended(rbfTransaction, true);
|
||||||
response['rbfTransaction'] = fullTx;
|
response['rbfTransaction'] = fullTx;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.debug('Error finding transaction in mempool: ' + e.message || e);
|
logger.debug('Error finding transaction in mempool: ' + (e instanceof Error ? e.message : e));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
response['rbfTransaction'] = rbfTx;
|
response['rbfTransaction'] = rbfTx;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ interface IConfig {
|
|||||||
BLOCK_WEIGHT_UNITS: number;
|
BLOCK_WEIGHT_UNITS: number;
|
||||||
INITIAL_BLOCKS_AMOUNT: number;
|
INITIAL_BLOCKS_AMOUNT: number;
|
||||||
MEMPOOL_BLOCKS_AMOUNT: number;
|
MEMPOOL_BLOCKS_AMOUNT: number;
|
||||||
|
PRICE_FEED_UPDATE_INTERVAL: number;
|
||||||
};
|
};
|
||||||
ESPLORA: {
|
ESPLORA: {
|
||||||
REST_API_URL: string;
|
REST_API_URL: string;
|
||||||
@@ -75,6 +76,7 @@ const defaults: IConfig = {
|
|||||||
'BLOCK_WEIGHT_UNITS': 4000000,
|
'BLOCK_WEIGHT_UNITS': 4000000,
|
||||||
'INITIAL_BLOCKS_AMOUNT': 8,
|
'INITIAL_BLOCKS_AMOUNT': 8,
|
||||||
'MEMPOOL_BLOCKS_AMOUNT': 8,
|
'MEMPOOL_BLOCKS_AMOUNT': 8,
|
||||||
|
'PRICE_FEED_UPDATE_INTERVAL': 3600,
|
||||||
},
|
},
|
||||||
'ESPLORA': {
|
'ESPLORA': {
|
||||||
'REST_API_URL': 'http://127.0.0.1:3000',
|
'REST_API_URL': 'http://127.0.0.1:3000',
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export async function checkDbConnection() {
|
|||||||
logger.info('Database connection established.');
|
logger.info('Database connection established.');
|
||||||
connection.release();
|
connection.release();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.err('Could not connect to database: ' + e.message || e);
|
logger.err('Could not connect to database: ' + (e instanceof Error ? e.message : e));
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ class Server {
|
|||||||
try {
|
try {
|
||||||
await memPool.$updateMemPoolInfo();
|
await memPool.$updateMemPoolInfo();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const msg = `updateMempoolInfo: ${(e.message || e)}`;
|
const msg = `updateMempoolInfo: ${(e instanceof Error ? e.message : e)}`;
|
||||||
if (config.CORE_RPC_MINFEE.ENABLED) {
|
if (config.CORE_RPC_MINFEE.ENABLED) {
|
||||||
logger.warn(msg);
|
logger.warn(msg);
|
||||||
} else {
|
} else {
|
||||||
@@ -123,7 +123,7 @@ class Server {
|
|||||||
setTimeout(this.runMainUpdateLoop.bind(this), config.MEMPOOL.POLL_RATE_MS);
|
setTimeout(this.runMainUpdateLoop.bind(this), config.MEMPOOL.POLL_RATE_MS);
|
||||||
this.currentBackendRetryInterval = 5;
|
this.currentBackendRetryInterval = 5;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const loggerMsg = `runMainLoop error: ${(e.message || e)}. Retrying in ${this.currentBackendRetryInterval} sec.`;
|
const loggerMsg = `runMainLoop error: ${(e instanceof Error ? e.message : e)}. Retrying in ${this.currentBackendRetryInterval} sec.`;
|
||||||
if (this.currentBackendRetryInterval > 5) {
|
if (this.currentBackendRetryInterval > 5) {
|
||||||
logger.warn(loggerMsg);
|
logger.warn(loggerMsg);
|
||||||
mempool.setOutOfSync();
|
mempool.setOutOfSync();
|
||||||
@@ -158,6 +158,7 @@ class Server {
|
|||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'fees/mempool-blocks', routes.getMempoolBlocks)
|
.get(config.MEMPOOL.API_URL_PREFIX + 'fees/mempool-blocks', routes.getMempoolBlocks)
|
||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'backend-info', routes.getBackendInfo)
|
.get(config.MEMPOOL.API_URL_PREFIX + 'backend-info', routes.getBackendInfo)
|
||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'init-data', routes.getInitData)
|
.get(config.MEMPOOL.API_URL_PREFIX + 'init-data', routes.getInitData)
|
||||||
|
.get(config.MEMPOOL.API_URL_PREFIX + 'validate-address/:address', routes.validateAddress)
|
||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'donations', async (req, res) => {
|
.get(config.MEMPOOL.API_URL_PREFIX + 'donations', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get('https://mempool.space/api/v1/donations', { responseType: 'stream', timeout: 10000 });
|
const response = await axios.get('https://mempool.space/api/v1/donations', { responseType: 'stream', timeout: 10000 });
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import transactionUtils from './api/transaction-utils';
|
|||||||
import blocks from './api/blocks';
|
import blocks from './api/blocks';
|
||||||
import loadingIndicators from './api/loading-indicators';
|
import loadingIndicators from './api/loading-indicators';
|
||||||
import { Common } from './api/common';
|
import { Common } from './api/common';
|
||||||
|
import bitcoinBaseApi from './api/bitcoin/bitcoin-base.api';
|
||||||
|
|
||||||
class Routes {
|
class Routes {
|
||||||
constructor() {}
|
constructor() {}
|
||||||
@@ -55,7 +56,7 @@ class Routes {
|
|||||||
const result = websocketHandler.getInitData();
|
const result = websocketHandler.getInitData();
|
||||||
res.json(result);
|
res.json(result);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
res.status(500).send(e.message);
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,7 +75,7 @@ class Routes {
|
|||||||
const result = mempoolBlocks.getMempoolBlocks();
|
const result = mempoolBlocks.getMempoolBlocks();
|
||||||
res.json(result);
|
res.json(result);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
res.status(500).send(e.message);
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -477,10 +478,10 @@ class Routes {
|
|||||||
res.json(transaction);
|
res.json(transaction);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
let statusCode = 500;
|
let statusCode = 500;
|
||||||
if (e.message && e.message.indexOf('No such mempool or blockchain transaction') > -1) {
|
if (e instanceof Error && e instanceof Error && e.message && e.message.indexOf('No such mempool or blockchain transaction') > -1) {
|
||||||
statusCode = 404;
|
statusCode = 404;
|
||||||
}
|
}
|
||||||
res.status(statusCode).send(e.message || e);
|
res.status(statusCode).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -491,10 +492,10 @@ class Routes {
|
|||||||
res.send(transaction.hex);
|
res.send(transaction.hex);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
let statusCode = 500;
|
let statusCode = 500;
|
||||||
if (e.message && e.message.indexOf('No such mempool or blockchain transaction') > -1) {
|
if (e instanceof Error && e.message && e.message.indexOf('No such mempool or blockchain transaction') > -1) {
|
||||||
statusCode = 404;
|
statusCode = 404;
|
||||||
}
|
}
|
||||||
res.status(statusCode).send(e.message || e);
|
res.status(statusCode).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -504,10 +505,10 @@ class Routes {
|
|||||||
res.json(transaction.status);
|
res.json(transaction.status);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
let statusCode = 500;
|
let statusCode = 500;
|
||||||
if (e.message && e.message.indexOf('No such mempool or blockchain transaction') > -1) {
|
if (e instanceof Error && e.message && e.message.indexOf('No such mempool or blockchain transaction') > -1) {
|
||||||
statusCode = 404;
|
statusCode = 404;
|
||||||
}
|
}
|
||||||
res.status(statusCode).send(e.message || e);
|
res.status(statusCode).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -516,7 +517,7 @@ class Routes {
|
|||||||
const result = await bitcoinApi.$getBlock(req.params.hash);
|
const result = await bitcoinApi.$getBlock(req.params.hash);
|
||||||
res.json(result);
|
res.json(result);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
res.status(500).send(e.message || e);
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -526,7 +527,7 @@ class Routes {
|
|||||||
res.setHeader('content-type', 'text/plain');
|
res.setHeader('content-type', 'text/plain');
|
||||||
res.send(blockHeader);
|
res.send(blockHeader);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
res.status(500).send(e.message || e);
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -563,7 +564,7 @@ class Routes {
|
|||||||
res.json(returnBlocks);
|
res.json(returnBlocks);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
loadingIndicators.setProgress('blocks', 100);
|
loadingIndicators.setProgress('blocks', 100);
|
||||||
res.status(500).send(e.message || e);
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -582,13 +583,13 @@ class Routes {
|
|||||||
transactions.push(transaction);
|
transactions.push(transaction);
|
||||||
loadingIndicators.setProgress('blocktxs-' + req.params.hash, (i + 1) / endIndex * 100);
|
loadingIndicators.setProgress('blocktxs-' + req.params.hash, (i + 1) / endIndex * 100);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.debug('getBlockTransactions error: ' + e.message || e);
|
logger.debug('getBlockTransactions error: ' + (e instanceof Error ? e.message : e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
res.json(transactions);
|
res.json(transactions);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
loadingIndicators.setProgress('blocktxs-' + req.params.hash, 100);
|
loadingIndicators.setProgress('blocktxs-' + req.params.hash, 100);
|
||||||
res.status(500).send(e.message || e);
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -597,7 +598,7 @@ class Routes {
|
|||||||
const blockHash = await bitcoinApi.$getBlockHash(parseInt(req.params.height, 10));
|
const blockHash = await bitcoinApi.$getBlockHash(parseInt(req.params.height, 10));
|
||||||
res.send(blockHash);
|
res.send(blockHash);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
res.status(500).send(e.message || e);
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -611,10 +612,10 @@ class Routes {
|
|||||||
const addressData = await bitcoinApi.$getAddress(req.params.address);
|
const addressData = await bitcoinApi.$getAddress(req.params.address);
|
||||||
res.json(addressData);
|
res.json(addressData);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.message && e.message.indexOf('exceeds') > 0) {
|
if (e instanceof Error && e.message && e.message.indexOf('exceeds') > 0) {
|
||||||
return res.status(413).send(e.message);
|
return res.status(413).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
res.status(500).send(e.message || e);
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -628,10 +629,10 @@ class Routes {
|
|||||||
const transactions = await bitcoinApi.$getAddressTransactions(req.params.address, req.params.txId);
|
const transactions = await bitcoinApi.$getAddressTransactions(req.params.address, req.params.txId);
|
||||||
res.json(transactions);
|
res.json(transactions);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.message && e.message.indexOf('exceeds') > 0) {
|
if (e instanceof Error && e.message && e.message.indexOf('exceeds') > 0) {
|
||||||
return res.status(413).send(e.message);
|
return res.status(413).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
res.status(500).send(e.message || e);
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -644,7 +645,7 @@ class Routes {
|
|||||||
const blockHash = await bitcoinApi.$getAddressPrefix(req.params.prefix);
|
const blockHash = await bitcoinApi.$getAddressPrefix(req.params.prefix);
|
||||||
res.send(blockHash);
|
res.send(blockHash);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
res.status(500).send(e.message || e);
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -665,7 +666,7 @@ class Routes {
|
|||||||
const rawMempool = await bitcoinApi.$getRawMempool();
|
const rawMempool = await bitcoinApi.$getRawMempool();
|
||||||
res.send(rawMempool);
|
res.send(rawMempool);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
res.status(500).send(e.message || e);
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -674,7 +675,7 @@ class Routes {
|
|||||||
const result = await bitcoinApi.$getBlockHeightTip();
|
const result = await bitcoinApi.$getBlockHeightTip();
|
||||||
res.json(result);
|
res.json(result);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
res.status(500).send(e.message || e);
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -683,7 +684,16 @@ class Routes {
|
|||||||
const result = await bitcoinApi.$getTxIdsForBlock(req.params.hash);
|
const result = await bitcoinApi.$getTxIdsForBlock(req.params.hash);
|
||||||
res.json(result);
|
res.json(result);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
res.status(500).send(e.message || e);
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async validateAddress(req: Request, res: Response) {
|
||||||
|
try {
|
||||||
|
const result = await bitcoinBaseApi.$validateAddress(req.params.address);
|
||||||
|
res.json(result);
|
||||||
|
} catch (e) {
|
||||||
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -727,7 +737,7 @@ class Routes {
|
|||||||
const timeAvg = timeAvgMins * 60;
|
const timeAvg = timeAvgMins * 60;
|
||||||
const remainingTime = remainingBlocks * timeAvg;
|
const remainingTime = remainingBlocks * timeAvg;
|
||||||
const estimatedRetargetDate = remainingTime + now;
|
const estimatedRetargetDate = remainingTime + now;
|
||||||
|
|
||||||
const result = {
|
const result = {
|
||||||
progressPercent,
|
progressPercent,
|
||||||
difficultyChange,
|
difficultyChange,
|
||||||
@@ -737,11 +747,11 @@ class Routes {
|
|||||||
previousRetarget,
|
previousRetarget,
|
||||||
nextRetargetHeight,
|
nextRetargetHeight,
|
||||||
timeAvg,
|
timeAvg,
|
||||||
}
|
};
|
||||||
res.json(result);
|
res.json(result);
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
res.status(500).send(e.message || e);
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
18
docker/scripts/get_image_digest.sh
Executable file
18
docker/scripts/get_image_digest.sh
Executable file
@@ -0,0 +1,18 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
VERSION=$1
|
||||||
|
IMAGE=""
|
||||||
|
|
||||||
|
if [ -z "${VERSION}" ]; then
|
||||||
|
echo "no version provided (i.e, v2.2.0), using latest tag"
|
||||||
|
VERSION="latest"
|
||||||
|
fi
|
||||||
|
|
||||||
|
for package in frontend backend; do
|
||||||
|
PACKAGE=mempool/"$package"
|
||||||
|
IMAGE="$PACKAGE":"$VERSION"
|
||||||
|
HASH=`docker pull $IMAGE > /dev/null && docker inspect $IMAGE | sed -n '/RepoDigests/{n;p;}' | grep -o '[0-9a-f]\{64\}'`
|
||||||
|
if [ -n "${HASH}" ]; then
|
||||||
|
echo "$IMAGE"@sha256:"$HASH"
|
||||||
|
fi
|
||||||
|
done
|
||||||
@@ -9,6 +9,11 @@ describe('Mainnet', () => {
|
|||||||
cy.intercept('/api/tx/*/outspends').as('tx-outspends');
|
cy.intercept('/api/tx/*/outspends').as('tx-outspends');
|
||||||
cy.intercept('/resources/pools.json').as('pools');
|
cy.intercept('/resources/pools.json').as('pools');
|
||||||
|
|
||||||
|
// Search Auto Complete
|
||||||
|
cy.intercept('/api/address-prefix/1wiz').as('search-1wiz');
|
||||||
|
cy.intercept('/api/address-prefix/1wizS').as('search-1wizS');
|
||||||
|
cy.intercept('/api/address-prefix/1wizSA').as('search-1wizSA');
|
||||||
|
|
||||||
Cypress.Commands.add('waitForBlockData', () => {
|
Cypress.Commands.add('waitForBlockData', () => {
|
||||||
cy.wait('@tx-outspends');
|
cy.wait('@tx-outspends');
|
||||||
cy.wait('@pools');
|
cy.wait('@pools');
|
||||||
@@ -56,6 +61,46 @@ describe('Mainnet', () => {
|
|||||||
cy.waitForSkeletonGone();
|
cy.waitForSkeletonGone();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('search', () => {
|
||||||
|
it('allows searching for partial Bitcoin addresses', () => {
|
||||||
|
cy.visit('/');
|
||||||
|
cy.get('.search-box-container > .form-control').type('1wiz').then(() => {
|
||||||
|
cy.wait('@search-1wiz');
|
||||||
|
cy.get('ngb-typeahead-window button.dropdown-item').should('have.length', 10);
|
||||||
|
});
|
||||||
|
|
||||||
|
cy.get('.search-box-container > .form-control').type('S').then(() => {
|
||||||
|
cy.wait('@search-1wizS');
|
||||||
|
cy.get('ngb-typeahead-window button.dropdown-item').should('have.length', 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
cy.get('.search-box-container > .form-control').type('A').then(() => {
|
||||||
|
cy.wait('@search-1wizSA');
|
||||||
|
cy.get('ngb-typeahead-window button.dropdown-item').should('have.length', 1)
|
||||||
|
});
|
||||||
|
|
||||||
|
cy.get('ngb-typeahead-window button.dropdown-item.active').click().then(() => {
|
||||||
|
cy.url().should('include', '/address/1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC');
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('.text-center').should('not.have.text', 'Invalid Bitcoin address');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
['BC1PQYQSZQ', 'bc1PqYqSzQ'].forEach((searchTerm) => {
|
||||||
|
it(`allows searching for partial case insensitive bc1 addresses: ${searchTerm}`, () => {
|
||||||
|
cy.visit('/');
|
||||||
|
cy.get('.search-box-container > .form-control').type(searchTerm).then(() => {
|
||||||
|
cy.get('ngb-typeahead-window button.dropdown-item').should('have.length', 1);
|
||||||
|
cy.get('ngb-typeahead-window button.dropdown-item.active').click().then(() => {
|
||||||
|
cy.url().should('include', '/address/bc1pqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs3wf0qm');
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('.text-center').should('not.have.text', 'Invalid Bitcoin address');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('blocks navigation', () => {
|
describe('blocks navigation', () => {
|
||||||
|
|
||||||
describe('keyboard events', () => {
|
describe('keyboard events', () => {
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ const codes = {
|
|||||||
Cypress.Commands.add('waitForSkeletonGone', () => {
|
Cypress.Commands.add('waitForSkeletonGone', () => {
|
||||||
cy.waitUntil(() => {
|
cy.waitUntil(() => {
|
||||||
return Cypress.$('.skeleton-loader').length === 0;
|
return Cypress.$('.skeleton-loader').length === 0;
|
||||||
}, { verbose: true, description: "waitForSkeletonGone", errorMsg: "skeleton loaders never went away", timeout: 7000, interval: 50});
|
}, { verbose: true, description: "waitForSkeletonGone", errorMsg: "skeleton loaders never went away", timeout: 15000, interval: 50});
|
||||||
});
|
});
|
||||||
|
|
||||||
Cypress.Commands.add(
|
Cypress.Commands.add(
|
||||||
|
|||||||
200
frontend/package-lock.json
generated
200
frontend/package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "mempool-frontend",
|
"name": "mempool-frontend",
|
||||||
"version": "2.2.2-dev",
|
"version": "2.2.2",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "mempool-frontend",
|
"name": "mempool-frontend",
|
||||||
"version": "2.2.2-dev",
|
"version": "2.2.2",
|
||||||
"license": "GNU Affero General Public License v3.0",
|
"license": "GNU Affero General Public License v3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "~11.2.8",
|
"@angular/animations": "~11.2.8",
|
||||||
@@ -68,7 +68,7 @@
|
|||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@cypress/schematic": "^1.3.0",
|
"@cypress/schematic": "^1.3.0",
|
||||||
"cypress": "^7.7.0",
|
"cypress": "^8.3.1",
|
||||||
"cypress-fail-on-console-error": "^2.1.0",
|
"cypress-fail-on-console-error": "^2.1.0",
|
||||||
"cypress-wait-until": "^1.7.1",
|
"cypress-wait-until": "^1.7.1",
|
||||||
"mock-socket": "^9.0.3",
|
"mock-socket": "^9.0.3",
|
||||||
@@ -1815,9 +1815,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@cypress/request": {
|
"node_modules/@cypress/request": {
|
||||||
"version": "2.88.5",
|
"version": "2.88.6",
|
||||||
"resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.5.tgz",
|
"resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.6.tgz",
|
||||||
"integrity": "sha512-TzEC1XMi1hJkywWpRfD2clreTa/Z+lOrXDCxxBTBPEcY5azdPi56A6Xw+O4tWJnaJH3iIE7G5aDXZC6JgRZLcA==",
|
"integrity": "sha512-z0UxBE/+qaESAHY9p9sM2h8Y4XqtsbDCt0/DPOrqA/RZgKi4PkxdpXyK4wCCnSk1xHqWHZZAE+gV6aDAR6+caQ==",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"aws-sign2": "~0.7.0",
|
"aws-sign2": "~0.7.0",
|
||||||
@@ -1833,13 +1833,12 @@
|
|||||||
"isstream": "~0.1.2",
|
"isstream": "~0.1.2",
|
||||||
"json-stringify-safe": "~5.0.1",
|
"json-stringify-safe": "~5.0.1",
|
||||||
"mime-types": "~2.1.19",
|
"mime-types": "~2.1.19",
|
||||||
"oauth-sign": "~0.9.0",
|
|
||||||
"performance-now": "^2.1.0",
|
"performance-now": "^2.1.0",
|
||||||
"qs": "~6.5.2",
|
"qs": "~6.5.2",
|
||||||
"safe-buffer": "^5.1.2",
|
"safe-buffer": "^5.1.2",
|
||||||
"tough-cookie": "~2.5.0",
|
"tough-cookie": "~2.5.0",
|
||||||
"tunnel-agent": "^0.6.0",
|
"tunnel-agent": "^0.6.0",
|
||||||
"uuid": "^3.3.2"
|
"uuid": "^8.3.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
@@ -1867,15 +1866,6 @@
|
|||||||
"node": ">=0.8"
|
"node": ">=0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@cypress/request/node_modules/uuid": {
|
|
||||||
"version": "3.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
|
|
||||||
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
|
|
||||||
"optional": true,
|
|
||||||
"bin": {
|
|
||||||
"uuid": "bin/uuid"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@cypress/schematic": {
|
"node_modules/@cypress/schematic": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@cypress/schematic/-/schematic-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@cypress/schematic/-/schematic-1.3.0.tgz",
|
||||||
@@ -2422,19 +2412,18 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@npmcli/git": {
|
"node_modules/@npmcli/git": {
|
||||||
"version": "2.0.6",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@npmcli/git/-/git-2.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/@npmcli/git/-/git-2.1.0.tgz",
|
||||||
"integrity": "sha512-a1MnTfeRPBaKbFY07fd+6HugY1WAkKJzdiJvlRub/9o5xz2F/JtPacZZapx5zRJUQFIzSL677vmTSxEcDMrDbg==",
|
"integrity": "sha512-/hBFX/QG1b+N7PZBFs0bi+evgRZcK9nWBxQKZkGoXUT5hJSwl5c4d7y8/hm+NQZRPhQ67RzFaj5UM9YeyKoryw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@npmcli/promise-spawn": "^1.1.0",
|
"@npmcli/promise-spawn": "^1.3.2",
|
||||||
"lru-cache": "^6.0.0",
|
"lru-cache": "^6.0.0",
|
||||||
"mkdirp": "^1.0.3",
|
"mkdirp": "^1.0.4",
|
||||||
"npm-pick-manifest": "^6.0.0",
|
"npm-pick-manifest": "^6.1.1",
|
||||||
"promise-inflight": "^1.0.1",
|
"promise-inflight": "^1.0.1",
|
||||||
"promise-retry": "^2.0.1",
|
"promise-retry": "^2.0.1",
|
||||||
"semver": "^7.3.2",
|
"semver": "^7.3.5",
|
||||||
"unique-filename": "^1.1.1",
|
|
||||||
"which": "^2.0.2"
|
"which": "^2.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2444,6 +2433,18 @@
|
|||||||
"integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==",
|
"integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@npmcli/git/node_modules/hosted-git-info": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"lru-cache": "^6.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@npmcli/git/node_modules/mkdirp": {
|
"node_modules/@npmcli/git/node_modules/mkdirp": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||||
@@ -2456,6 +2457,32 @@
|
|||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@npmcli/git/node_modules/npm-package-arg": {
|
||||||
|
"version": "8.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-8.1.5.tgz",
|
||||||
|
"integrity": "sha512-LhgZrg0n0VgvzVdSm1oiZworPbTxYHUJCgtsJW8mGvlDpxTM1vSJc3m5QZeUkhAHIzbz3VCHd/R4osi1L1Tg/Q==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"hosted-git-info": "^4.0.1",
|
||||||
|
"semver": "^7.3.4",
|
||||||
|
"validate-npm-package-name": "^3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@npmcli/git/node_modules/npm-pick-manifest": {
|
||||||
|
"version": "6.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-6.1.1.tgz",
|
||||||
|
"integrity": "sha512-dBsdBtORT84S8V8UTad1WlUyKIY9iMsAmqxHbLdeEeBNMLQDlDWWra3wYUx9EBEIiG/YwAy0XyNHDd2goAsfuA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"npm-install-checks": "^4.0.0",
|
||||||
|
"npm-normalize-package-bin": "^1.0.1",
|
||||||
|
"npm-package-arg": "^8.1.2",
|
||||||
|
"semver": "^7.3.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@npmcli/git/node_modules/promise-retry": {
|
"node_modules/@npmcli/git/node_modules/promise-retry": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz",
|
||||||
@@ -6069,12 +6096,13 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/cypress": {
|
"node_modules/cypress": {
|
||||||
"version": "7.7.0",
|
"version": "8.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/cypress/-/cypress-7.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/cypress/-/cypress-8.3.1.tgz",
|
||||||
"integrity": "sha512-uYBYXNoI5ym0UxROwhQXWTi8JbUEjpC6l/bzoGZNxoKGsLrC1SDPgIDJMgLX/MeEdPL0UInXLDUWN/rSyZUCjQ==",
|
"integrity": "sha512-1v6pfx+/5cXhaT5T6QKOvnkawmEHWHLiVzm3MYMoQN1fkX2Ma1C32STd3jBStE9qT5qPSTILjGzypVRxCBi40g==",
|
||||||
|
"hasInstallScript": true,
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cypress/request": "^2.88.5",
|
"@cypress/request": "^2.88.6",
|
||||||
"@cypress/xvfb": "^1.2.4",
|
"@cypress/xvfb": "^1.2.4",
|
||||||
"@types/node": "^14.14.31",
|
"@types/node": "^14.14.31",
|
||||||
"@types/sinonjs__fake-timers": "^6.0.2",
|
"@types/sinonjs__fake-timers": "^6.0.2",
|
||||||
@@ -12222,7 +12250,7 @@
|
|||||||
"version": "0.9.0",
|
"version": "0.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
|
||||||
"integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
|
"integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "*"
|
"node": "*"
|
||||||
}
|
}
|
||||||
@@ -12873,9 +12901,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/path-parse": {
|
"node_modules/path-parse": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||||
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw=="
|
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
|
||||||
},
|
},
|
||||||
"node_modules/path-platform": {
|
"node_modules/path-platform": {
|
||||||
"version": "0.11.15",
|
"version": "0.11.15",
|
||||||
@@ -16486,9 +16514,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tar": {
|
"node_modules/tar": {
|
||||||
"version": "6.1.6",
|
"version": "6.1.11",
|
||||||
"resolved": "https://registry.npmjs.org/tar/-/tar-6.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz",
|
||||||
"integrity": "sha512-oaWyu5dQbHaYcyZCTfyPpC+VmI62/OM2RTUYavTk1MDr1cwW5Boi3baeYQKiZbY2uSQJGr+iMOzb/JFxLrft+g==",
|
"integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chownr": "^2.0.0",
|
"chownr": "^2.0.0",
|
||||||
@@ -17498,9 +17526,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/url-parse": {
|
"node_modules/url-parse": {
|
||||||
"version": "1.5.1",
|
"version": "1.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.3.tgz",
|
||||||
"integrity": "sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==",
|
"integrity": "sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"querystringify": "^2.1.1",
|
"querystringify": "^2.1.1",
|
||||||
@@ -17553,7 +17581,7 @@
|
|||||||
"version": "8.3.2",
|
"version": "8.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"uuid": "dist/bin/uuid"
|
"uuid": "dist/bin/uuid"
|
||||||
}
|
}
|
||||||
@@ -21510,9 +21538,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@cypress/request": {
|
"@cypress/request": {
|
||||||
"version": "2.88.5",
|
"version": "2.88.6",
|
||||||
"resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.5.tgz",
|
"resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.6.tgz",
|
||||||
"integrity": "sha512-TzEC1XMi1hJkywWpRfD2clreTa/Z+lOrXDCxxBTBPEcY5azdPi56A6Xw+O4tWJnaJH3iIE7G5aDXZC6JgRZLcA==",
|
"integrity": "sha512-z0UxBE/+qaESAHY9p9sM2h8Y4XqtsbDCt0/DPOrqA/RZgKi4PkxdpXyK4wCCnSk1xHqWHZZAE+gV6aDAR6+caQ==",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"aws-sign2": "~0.7.0",
|
"aws-sign2": "~0.7.0",
|
||||||
@@ -21528,13 +21556,12 @@
|
|||||||
"isstream": "~0.1.2",
|
"isstream": "~0.1.2",
|
||||||
"json-stringify-safe": "~5.0.1",
|
"json-stringify-safe": "~5.0.1",
|
||||||
"mime-types": "~2.1.19",
|
"mime-types": "~2.1.19",
|
||||||
"oauth-sign": "~0.9.0",
|
|
||||||
"performance-now": "^2.1.0",
|
"performance-now": "^2.1.0",
|
||||||
"qs": "~6.5.2",
|
"qs": "~6.5.2",
|
||||||
"safe-buffer": "^5.1.2",
|
"safe-buffer": "^5.1.2",
|
||||||
"tough-cookie": "~2.5.0",
|
"tough-cookie": "~2.5.0",
|
||||||
"tunnel-agent": "^0.6.0",
|
"tunnel-agent": "^0.6.0",
|
||||||
"uuid": "^3.3.2"
|
"uuid": "^8.3.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"qs": {
|
"qs": {
|
||||||
@@ -21552,12 +21579,6 @@
|
|||||||
"psl": "^1.1.28",
|
"psl": "^1.1.28",
|
||||||
"punycode": "^2.1.1"
|
"punycode": "^2.1.1"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"uuid": {
|
|
||||||
"version": "3.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
|
|
||||||
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
|
|
||||||
"optional": true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -22024,19 +22045,18 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@npmcli/git": {
|
"@npmcli/git": {
|
||||||
"version": "2.0.6",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@npmcli/git/-/git-2.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/@npmcli/git/-/git-2.1.0.tgz",
|
||||||
"integrity": "sha512-a1MnTfeRPBaKbFY07fd+6HugY1WAkKJzdiJvlRub/9o5xz2F/JtPacZZapx5zRJUQFIzSL677vmTSxEcDMrDbg==",
|
"integrity": "sha512-/hBFX/QG1b+N7PZBFs0bi+evgRZcK9nWBxQKZkGoXUT5hJSwl5c4d7y8/hm+NQZRPhQ67RzFaj5UM9YeyKoryw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@npmcli/promise-spawn": "^1.1.0",
|
"@npmcli/promise-spawn": "^1.3.2",
|
||||||
"lru-cache": "^6.0.0",
|
"lru-cache": "^6.0.0",
|
||||||
"mkdirp": "^1.0.3",
|
"mkdirp": "^1.0.4",
|
||||||
"npm-pick-manifest": "^6.0.0",
|
"npm-pick-manifest": "^6.1.1",
|
||||||
"promise-inflight": "^1.0.1",
|
"promise-inflight": "^1.0.1",
|
||||||
"promise-retry": "^2.0.1",
|
"promise-retry": "^2.0.1",
|
||||||
"semver": "^7.3.2",
|
"semver": "^7.3.5",
|
||||||
"unique-filename": "^1.1.1",
|
|
||||||
"which": "^2.0.2"
|
"which": "^2.0.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -22046,12 +22066,44 @@
|
|||||||
"integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==",
|
"integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"hosted-git-info": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"lru-cache": "^6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"mkdirp": {
|
"mkdirp": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||||
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
|
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"npm-package-arg": {
|
||||||
|
"version": "8.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-8.1.5.tgz",
|
||||||
|
"integrity": "sha512-LhgZrg0n0VgvzVdSm1oiZworPbTxYHUJCgtsJW8mGvlDpxTM1vSJc3m5QZeUkhAHIzbz3VCHd/R4osi1L1Tg/Q==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"hosted-git-info": "^4.0.1",
|
||||||
|
"semver": "^7.3.4",
|
||||||
|
"validate-npm-package-name": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"npm-pick-manifest": {
|
||||||
|
"version": "6.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-6.1.1.tgz",
|
||||||
|
"integrity": "sha512-dBsdBtORT84S8V8UTad1WlUyKIY9iMsAmqxHbLdeEeBNMLQDlDWWra3wYUx9EBEIiG/YwAy0XyNHDd2goAsfuA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"npm-install-checks": "^4.0.0",
|
||||||
|
"npm-normalize-package-bin": "^1.0.1",
|
||||||
|
"npm-package-arg": "^8.1.2",
|
||||||
|
"semver": "^7.3.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"promise-retry": {
|
"promise-retry": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz",
|
||||||
@@ -25229,12 +25281,12 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"cypress": {
|
"cypress": {
|
||||||
"version": "7.7.0",
|
"version": "8.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/cypress/-/cypress-7.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/cypress/-/cypress-8.3.1.tgz",
|
||||||
"integrity": "sha512-uYBYXNoI5ym0UxROwhQXWTi8JbUEjpC6l/bzoGZNxoKGsLrC1SDPgIDJMgLX/MeEdPL0UInXLDUWN/rSyZUCjQ==",
|
"integrity": "sha512-1v6pfx+/5cXhaT5T6QKOvnkawmEHWHLiVzm3MYMoQN1fkX2Ma1C32STd3jBStE9qT5qPSTILjGzypVRxCBi40g==",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@cypress/request": "^2.88.5",
|
"@cypress/request": "^2.88.6",
|
||||||
"@cypress/xvfb": "^1.2.4",
|
"@cypress/xvfb": "^1.2.4",
|
||||||
"@types/node": "^14.14.31",
|
"@types/node": "^14.14.31",
|
||||||
"@types/sinonjs__fake-timers": "^6.0.2",
|
"@types/sinonjs__fake-timers": "^6.0.2",
|
||||||
@@ -30373,7 +30425,7 @@
|
|||||||
"version": "0.9.0",
|
"version": "0.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
|
||||||
"integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
|
"integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
|
||||||
"devOptional": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"object-assign": {
|
"object-assign": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
@@ -30900,9 +30952,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"path-parse": {
|
"path-parse": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||||
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw=="
|
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
|
||||||
},
|
},
|
||||||
"path-platform": {
|
"path-platform": {
|
||||||
"version": "0.11.15",
|
"version": "0.11.15",
|
||||||
@@ -33913,9 +33965,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"tar": {
|
"tar": {
|
||||||
"version": "6.1.6",
|
"version": "6.1.11",
|
||||||
"resolved": "https://registry.npmjs.org/tar/-/tar-6.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz",
|
||||||
"integrity": "sha512-oaWyu5dQbHaYcyZCTfyPpC+VmI62/OM2RTUYavTk1MDr1cwW5Boi3baeYQKiZbY2uSQJGr+iMOzb/JFxLrft+g==",
|
"integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"chownr": "^2.0.0",
|
"chownr": "^2.0.0",
|
||||||
@@ -34757,9 +34809,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"url-parse": {
|
"url-parse": {
|
||||||
"version": "1.5.1",
|
"version": "1.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.3.tgz",
|
||||||
"integrity": "sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==",
|
"integrity": "sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"querystringify": "^2.1.1",
|
"querystringify": "^2.1.1",
|
||||||
@@ -34803,7 +34855,7 @@
|
|||||||
"version": "8.3.2",
|
"version": "8.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||||
"dev": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
"validate-npm-package-name": {
|
"validate-npm-package-name": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "mempool-frontend",
|
"name": "mempool-frontend",
|
||||||
"version": "2.2.2-dev",
|
"version": "2.2.2",
|
||||||
"description": "Bitcoin mempool visualizer and blockchain explorer backend",
|
"description": "Bitcoin mempool visualizer and blockchain explorer backend",
|
||||||
"license": "GNU Affero General Public License v3.0",
|
"license": "GNU Affero General Public License v3.0",
|
||||||
"homepage": "https://mempool.space",
|
"homepage": "https://mempool.space",
|
||||||
@@ -112,7 +112,7 @@
|
|||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@cypress/schematic": "^1.3.0",
|
"@cypress/schematic": "^1.3.0",
|
||||||
"cypress": "^7.7.0",
|
"cypress": "^8.3.1",
|
||||||
"cypress-fail-on-console-error": "^2.1.0",
|
"cypress-fail-on-console-error": "^2.1.0",
|
||||||
"cypress-wait-until": "^1.7.1",
|
"cypress-wait-until": "^1.7.1",
|
||||||
"mock-socket": "^9.0.3",
|
"mock-socket": "^9.0.3",
|
||||||
|
|||||||
@@ -18,6 +18,10 @@
|
|||||||
<div class="col-md">
|
<div class="col-md">
|
||||||
<table class="table table-borderless table-striped">
|
<table class="table table-borderless table-striped">
|
||||||
<tbody>
|
<tbody>
|
||||||
|
<tr *ngIf="addressInfo && addressInfo.unconfidential">
|
||||||
|
<td i18n="address.unconfidential">Unconfidential</td>
|
||||||
|
<td><a [routerLink]="['/address/' | relativeUrl, addressInfo.unconfidential]">{{ addressInfo.unconfidential }}</a> <app-clipboard [text]="addressInfo.unconfidential"></app-clipboard></td>
|
||||||
|
</tr>
|
||||||
<ng-template [ngIf]="!address.electrum">
|
<ng-template [ngIf]="!address.electrum">
|
||||||
<tr>
|
<tr>
|
||||||
<td i18n="address.total-received">Total received</td>
|
<td i18n="address.total-received">Total received</td>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { AudioService } from 'src/app/services/audio.service';
|
|||||||
import { ApiService } from 'src/app/services/api.service';
|
import { ApiService } from 'src/app/services/api.service';
|
||||||
import { of, merge, Subscription, Observable } from 'rxjs';
|
import { of, merge, Subscription, Observable } from 'rxjs';
|
||||||
import { SeoService } from 'src/app/services/seo.service';
|
import { SeoService } from 'src/app/services/seo.service';
|
||||||
|
import { AddressInformation } from 'src/app/interfaces/node-api.interface';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-address',
|
selector: 'app-address',
|
||||||
@@ -27,6 +28,7 @@ export class AddressComponent implements OnInit, OnDestroy {
|
|||||||
error: any;
|
error: any;
|
||||||
mainSubscription: Subscription;
|
mainSubscription: Subscription;
|
||||||
addressLoadingStatus$: Observable<number>;
|
addressLoadingStatus$: Observable<number>;
|
||||||
|
addressInfo: null | AddressInformation = null;
|
||||||
|
|
||||||
totalConfirmedTxCount = 0;
|
totalConfirmedTxCount = 0;
|
||||||
loadedConfirmedTxCount = 0;
|
loadedConfirmedTxCount = 0;
|
||||||
@@ -67,8 +69,12 @@ export class AddressComponent implements OnInit, OnDestroy {
|
|||||||
this.address = null;
|
this.address = null;
|
||||||
this.isLoadingTransactions = true;
|
this.isLoadingTransactions = true;
|
||||||
this.transactions = null;
|
this.transactions = null;
|
||||||
|
this.addressInfo = null;
|
||||||
document.body.scrollTo(0, 0);
|
document.body.scrollTo(0, 0);
|
||||||
this.addressString = params.get('id') || '';
|
this.addressString = params.get('id') || '';
|
||||||
|
if (/^[A-Z]{2,5}1[AC-HJ-NP-Z02-9]{8,100}$/.test(this.addressString)) {
|
||||||
|
this.addressString = this.addressString.toLowerCase();
|
||||||
|
}
|
||||||
this.seoService.setTitle($localize`:@@address.component.browser-title:Address: ${this.addressString}:INTERPOLATION:`);
|
this.seoService.setTitle($localize`:@@address.component.browser-title:Address: ${this.addressString}:INTERPOLATION:`);
|
||||||
|
|
||||||
return merge(
|
return merge(
|
||||||
@@ -92,10 +98,20 @@ export class AddressComponent implements OnInit, OnDestroy {
|
|||||||
)
|
)
|
||||||
.pipe(
|
.pipe(
|
||||||
filter((address) => !!address),
|
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)) {
|
||||||
|
this.apiService.validateAddress$(address.address)
|
||||||
|
.subscribe((addressInfo) => {
|
||||||
|
this.addressInfo = addressInfo;
|
||||||
|
this.websocketService.startTrackAddress(addressInfo.unconfidential);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.websocketService.startTrackAddress(address.address);
|
||||||
|
}
|
||||||
|
}),
|
||||||
switchMap((address) => {
|
switchMap((address) => {
|
||||||
this.address = address;
|
this.address = address;
|
||||||
this.updateChainStats();
|
this.updateChainStats();
|
||||||
this.websocketService.startTrackAddress(address.address);
|
|
||||||
this.isLoadingAddress = false;
|
this.isLoadingAddress = false;
|
||||||
this.isLoadingTransactions = true;
|
this.isLoadingTransactions = true;
|
||||||
return this.electrsApiService.getAddressTransactions$(address.address);
|
return this.electrsApiService.getAddressTransactions$(address.address);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Component, OnInit, Input, ChangeDetectionStrategy } from '@angular/core';
|
import { Component, OnInit, OnDestroy, Input, ChangeDetectionStrategy } from '@angular/core';
|
||||||
import { StateService } from '../../services/state.service';
|
import { StateService } from '../../services/state.service';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable, Subscription } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-amount',
|
selector: 'app-amount',
|
||||||
@@ -8,11 +8,13 @@ import { Observable } from 'rxjs';
|
|||||||
styleUrls: ['./amount.component.scss'],
|
styleUrls: ['./amount.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class AmountComponent implements OnInit {
|
export class AmountComponent implements OnInit, OnDestroy {
|
||||||
conversions$: Observable<any>;
|
conversions$: Observable<any>;
|
||||||
viewFiat$: Observable<boolean>;
|
viewFiat$: Observable<boolean>;
|
||||||
network = '';
|
network = '';
|
||||||
|
|
||||||
|
stateSubscription: Subscription;
|
||||||
|
|
||||||
@Input() satoshis: number;
|
@Input() satoshis: number;
|
||||||
@Input() digitsInfo = '1.8-8';
|
@Input() digitsInfo = '1.8-8';
|
||||||
@Input() noFiat = false;
|
@Input() noFiat = false;
|
||||||
@@ -24,7 +26,13 @@ export class AmountComponent implements OnInit {
|
|||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.viewFiat$ = this.stateService.viewFiat$.asObservable();
|
this.viewFiat$ = this.stateService.viewFiat$.asObservable();
|
||||||
this.conversions$ = this.stateService.conversions$.asObservable();
|
this.conversions$ = this.stateService.conversions$.asObservable();
|
||||||
this.stateService.networkChanged$.subscribe((network) => this.network = network);
|
this.stateSubscription = this.stateService.networkChanged$.subscribe((network) => this.network = network);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
if (this.stateSubscription) {
|
||||||
|
this.stateSubscription.unsubscribe();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,20 +1,20 @@
|
|||||||
<div class="code">
|
<div class="code">
|
||||||
<ul ngbNav #navCodeTemplate="ngbNav" class="nav-tabs code-tab">
|
<ul ngbNav #navCodeTemplate="ngbNav" class="nav-tabs code-tab">
|
||||||
<li ngbNavItem *ngIf="code.codeSample.curl">
|
<li ngbNavItem *ngIf="code.codeTemplate.curl && method !== 'websocket'">
|
||||||
<a ngbNavLink>cURL</a>
|
<a ngbNavLink>cURL</a>
|
||||||
<ng-template ngbNavContent>
|
<ng-template ngbNavContent>
|
||||||
<div class="subtitle"><ng-container i18n="API Docs code example">Code Example</ng-container> <app-clipboard [text]="wrapCurl(code.codeSample.curl)"></app-clipboard></div>
|
<div class="subtitle"><ng-container i18n="API Docs code example">Code Example</ng-container> <app-clipboard [text]="wrapCurlTemplate(code)"></app-clipboard></div>
|
||||||
<pre><code [innerText]="wrapCurl(code.codeSample.curl)"></code></pre>
|
<pre><code [innerText]="wrapCurlTemplate(code)"></code></pre>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</li>
|
</li>
|
||||||
<li ngbNavItem>
|
<li ngbNavItem>
|
||||||
<a ngbNavLink>CommonJS</a>
|
<a ngbNavLink>CommonJS</a>
|
||||||
<ng-template ngbNavContent>
|
<ng-template ngbNavContent>
|
||||||
<div class="subtitle"><ng-container i18n="API Docs code example">Code Example</ng-container> <app-clipboard [text]="wrapCommonJS(code.codeSample.commonJS)"></app-clipboard></div>
|
<div class="subtitle"><ng-container i18n="API Docs code example">Code Example</ng-container> <app-clipboard [text]="wrapCommonJS(code)"></app-clipboard></div>
|
||||||
<div class="links">
|
<div class="links">
|
||||||
<a [href]="npmGithubLink()" target="_blank">github repository</a>
|
<a [href]="npmGithubLink()" target="_blank">github repository</a>
|
||||||
</div>
|
</div>
|
||||||
<pre><code [innerText]="wrapCommonJS(code.codeSample.commonJS)"></code></pre>
|
<pre><code [innerText]="wrapCommonJS(code)"></code></pre>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</li>
|
</li>
|
||||||
<li ngbNavItem>
|
<li ngbNavItem>
|
||||||
@@ -26,14 +26,14 @@
|
|||||||
<a [href]="npmModuleLink()" target="_blank">npm package</a>
|
<a [href]="npmModuleLink()" target="_blank">npm package</a>
|
||||||
</div>
|
</div>
|
||||||
<pre><code [innerText]="wrapImportTemplate()"></code></pre>
|
<pre><code [innerText]="wrapImportTemplate()"></code></pre>
|
||||||
<div class="subtitle"><ng-container i18n="API Docs code example">Code Example</ng-container> <app-clipboard [text]="wrapESmodule(code.codeSample.esModule)"></app-clipboard></div>
|
<div class="subtitle"><ng-container i18n="API Docs code example">Code Example</ng-container> <app-clipboard [text]="wrapEsModule(code)"></app-clipboard></div>
|
||||||
<pre><code [innerText]="wrapESmodule(code.codeSample.esModule)"></code></pre>
|
<pre><code [innerText]="wrapEsModule(code)"></code></pre>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div [ngbNavOutlet]="navCodeTemplate"></div>
|
<div [ngbNavOutlet]="navCodeTemplate"></div>
|
||||||
<div *ngIf="code.responseSample" class="response">
|
<div *ngIf="code.codeTemplate && wrapResponse(code) !== ''" class="response">
|
||||||
<div class="subtitle"><ng-container i18n="API Docs API response">Response</ng-container> <app-clipboard [text]="code.responseSample"></app-clipboard></div>
|
<div class="subtitle"><ng-container i18n="API Docs API response">Response</ng-container> <app-clipboard [text]="wrapResponse(code)"></app-clipboard></div>
|
||||||
<pre><code [innerText]="code.responseSample"></code></pre>
|
<pre><code [innerText]="wrapResponse(code)"></code></pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,32 +1,32 @@
|
|||||||
import { Component, Input } from '@angular/core';
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
|
import { Env, StateService } from 'src/app/services/state.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-code-template',
|
selector: 'app-code-template',
|
||||||
templateUrl: './code-template.component.html',
|
templateUrl: './code-template.component.html',
|
||||||
styleUrls: ['./code-template.component.scss']
|
styleUrls: ['./code-template.component.scss']
|
||||||
})
|
})
|
||||||
export class CodeTemplateComponent {
|
export class CodeTemplateComponent implements OnInit {
|
||||||
@Input() network: string;
|
@Input() network: string;
|
||||||
@Input() layer: string;
|
@Input() code: any;
|
||||||
@Input() code: {
|
@Input() hostname: string;
|
||||||
codeSample: {
|
@Input() method: 'get' | 'post' | 'websocket' = 'get';
|
||||||
esModule: string;
|
env: Env;
|
||||||
commonJS: string;
|
|
||||||
curl: string;
|
|
||||||
},
|
|
||||||
responseSample: string;
|
|
||||||
};
|
|
||||||
hostname = document.location.hostname;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
private stateService: StateService,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.env = this.stateService.env;
|
||||||
|
}
|
||||||
|
|
||||||
npmGithubLink(){
|
npmGithubLink(){
|
||||||
let npmLink = `https://github.com/mempool/mempool.js`;
|
let npmLink = `https://github.com/mempool/mempool.js`;
|
||||||
if (this.layer === 'bisq') {
|
if (this.network === 'bisq') {
|
||||||
npmLink = `https://github.com/mempool/mempool.js/tree/main/npm-bisq-js`;
|
npmLink = `https://github.com/mempool/mempool.js/tree/main/npm-bisq-js`;
|
||||||
}
|
}
|
||||||
if (this.layer === 'liquid') {
|
if (this.network === 'liquid') {
|
||||||
npmLink = `https://github.com/mempool/mempool.js/tree/main/npm-liquid-js`;
|
npmLink = `https://github.com/mempool/mempool.js/tree/main/npm-liquid-js`;
|
||||||
}
|
}
|
||||||
return npmLink;
|
return npmLink;
|
||||||
@@ -34,67 +34,153 @@ export class CodeTemplateComponent {
|
|||||||
|
|
||||||
npmModuleLink() {
|
npmModuleLink() {
|
||||||
let npmLink = `https://www.npmjs.org/package/@mempool/mempool.js`;
|
let npmLink = `https://www.npmjs.org/package/@mempool/mempool.js`;
|
||||||
if (this.layer === 'bisq') {
|
if (this.network === 'bisq') {
|
||||||
npmLink = `https://www.npmjs.org/package/@mempool/bisq.js`;
|
npmLink = `https://www.npmjs.org/package/@mempool/bisq.js`;
|
||||||
}
|
}
|
||||||
if (this.layer === 'liquid') {
|
if (this.network === 'liquid') {
|
||||||
npmLink = `https://www.npmjs.org/package/@mempool/liquid.js`;
|
npmLink = `https://www.npmjs.org/package/@mempool/liquid.js`;
|
||||||
}
|
}
|
||||||
return npmLink;
|
return npmLink;
|
||||||
}
|
}
|
||||||
|
|
||||||
normalizeCodeHostname(code: string) {
|
normalizeHostsESModule(codeText: string) {
|
||||||
let codeText: string;
|
if (this.env.BASE_MODULE === 'mempool') {
|
||||||
if (this.network === 'bisq' || this.network === 'liquid'){
|
if (['liquid', 'bisq'].includes(this.network)) {
|
||||||
codeText = code.replace('%{1}', this.network);
|
codeText = codeText.replace('%{0}', this.network);
|
||||||
}else{
|
} else {
|
||||||
codeText = code.replace('%{1}', 'bitcoin');
|
codeText = codeText.replace('%{0}', 'bitcoin');
|
||||||
|
}
|
||||||
|
if(['', 'main', 'liquid', 'bisq'].includes(this.network)) {
|
||||||
|
codeText = codeText.replace('mempoolJS();', `mempoolJS({
|
||||||
|
hostname: '${document.location.hostname}'
|
||||||
|
});`);
|
||||||
|
} else {
|
||||||
|
codeText = codeText.replace('mempoolJS();', `mempoolJS({
|
||||||
|
hostname: '${document.location.hostname}',
|
||||||
|
network: '${this.network}'
|
||||||
|
});`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.env.BASE_MODULE === 'bisq') {
|
||||||
|
codeText = codeText.replace('} = mempoolJS();', ` = bisqJS();`);
|
||||||
|
codeText = codeText.replace('{ %{0}: ', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.env.BASE_MODULE === 'liquid') {
|
||||||
|
codeText = codeText.replace('} = mempoolJS();', ` = liquidJS();`);
|
||||||
|
codeText = codeText.replace('{ %{0}: ', '');
|
||||||
}
|
}
|
||||||
return codeText;
|
return codeText;
|
||||||
}
|
}
|
||||||
|
|
||||||
wrapESmodule(code: string) {
|
normalizeHostsCommonJS(codeText: string) {
|
||||||
let codeText = this.normalizeCodeHostname(code);
|
if (this.env.BASE_MODULE === 'mempool') {
|
||||||
|
if (['liquid', 'bisq'].includes(this.network)) {
|
||||||
if (this.network && this.network !== 'mainnet') {
|
codeText = codeText.replace('%{0}', this.network);
|
||||||
codeText = codeText.replace('mempoolJS();', `mempoolJS({
|
} else {
|
||||||
hostname: '${this.hostname}/${this.network}'
|
codeText = codeText.replace('%{0}', 'bitcoin');
|
||||||
});` );
|
}
|
||||||
|
if(['', 'main', 'liquid', 'bisq'].includes(this.network)) {
|
||||||
|
codeText = codeText.replace('mempoolJS();', `mempoolJS({
|
||||||
|
hostname: '${document.location.hostname}'
|
||||||
|
});`);
|
||||||
|
} else {
|
||||||
|
codeText = codeText.replace('mempoolJS();', `mempoolJS({
|
||||||
|
hostname: '${document.location.hostname}',
|
||||||
|
network: '${this.network}'
|
||||||
|
});`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let importText = `import mempoolJS from "@mempool/mempool.js";`;
|
if (this.env.BASE_MODULE === 'bisq') {
|
||||||
if (this.layer === 'bisq') {
|
codeText = codeText.replace('} = mempoolJS();', ` = bisqJS();`);
|
||||||
importText = `import bisqJS from "@mempool/bisq.js";`;
|
codeText = codeText.replace('{ %{0}: ', '');
|
||||||
}
|
|
||||||
if (this.layer === 'liquid') {
|
|
||||||
importText = `import liquidJS from "@mempool/liquid.js";`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${importText}
|
if (this.env.BASE_MODULE === 'liquid') {
|
||||||
|
codeText = codeText.replace('} = mempoolJS();', ` = liquidJS();`);
|
||||||
|
codeText = codeText.replace('{ %{0}: ', '');
|
||||||
|
}
|
||||||
|
return codeText;
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapEsModule(code: any) {
|
||||||
|
let codeText: string;
|
||||||
|
if (code.codeTemplate) {
|
||||||
|
codeText = this.normalizeHostsESModule(code.codeTemplate.esModule);
|
||||||
|
|
||||||
|
if(this.network === '' || this.network === 'main') {
|
||||||
|
codeText = this.replaceJSPlaceholder(codeText, code.codeSampleMainnet.esModule);
|
||||||
|
}
|
||||||
|
if (this.network === 'testnet') {
|
||||||
|
codeText = this.replaceJSPlaceholder(codeText, code.codeSampleTestnet.esModule);
|
||||||
|
}
|
||||||
|
if (this.network === 'signet') {
|
||||||
|
codeText = this.replaceJSPlaceholder(codeText, code.codeSampleSignet.esModule);
|
||||||
|
}
|
||||||
|
if (this.network === 'liquid') {
|
||||||
|
codeText = this.replaceJSPlaceholder(codeText, code.codeSampleLiquid.esModule);
|
||||||
|
}
|
||||||
|
if (this.network === 'bisq') {
|
||||||
|
codeText = this.replaceJSPlaceholder(codeText, code.codeSampleBisq.esModule);
|
||||||
|
}
|
||||||
|
|
||||||
|
let importText = `import mempoolJS from "@mempool/mempool.js";`;
|
||||||
|
if (this.env.BASE_MODULE === 'bisq') {
|
||||||
|
importText = `import bisqJS from "@mempool/bisq.js";`;
|
||||||
|
}
|
||||||
|
if (this.env.BASE_MODULE === 'liquid') {
|
||||||
|
importText = `import liquidJS from "@mempool/liquid.js";`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${importText}
|
||||||
|
|
||||||
const init = async () => {
|
const init = async () => {
|
||||||
${codeText}
|
${codeText}
|
||||||
};
|
};
|
||||||
init();`;
|
init();`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wrapCommonJS(code: string) {
|
wrapCommonJS(code: any) {
|
||||||
let codeText = this.normalizeCodeHostname(code);
|
let codeText: string;
|
||||||
|
if (code.codeTemplate) {
|
||||||
|
codeText = this.normalizeHostsCommonJS(code.codeTemplate.commonJS);
|
||||||
|
|
||||||
if (this.network && this.network !== 'mainnet') {
|
if(this.network === '' || this.network === 'main') {
|
||||||
codeText = codeText.replace('mempoolJS();', `mempoolJS({
|
codeText = this.replaceJSPlaceholder(codeText, code.codeSampleMainnet.esModule);
|
||||||
hostname: '${this.hostname}/${this.network}'
|
}
|
||||||
});` );
|
if (this.network === 'testnet') {
|
||||||
}
|
codeText = this.replaceJSPlaceholder(codeText, code.codeSampleTestnet.esModule);
|
||||||
|
}
|
||||||
|
if (this.network === 'signet') {
|
||||||
|
codeText = this.replaceJSPlaceholder(codeText, code.codeSampleSignet.esModule);
|
||||||
|
}
|
||||||
|
if (this.network === 'liquid') {
|
||||||
|
codeText = this.replaceJSPlaceholder(codeText, code.codeSampleLiquid.esModule);
|
||||||
|
}
|
||||||
|
if (this.network === 'bisq') {
|
||||||
|
codeText = this.replaceJSPlaceholder(codeText, code.codeSampleBisq.esModule);
|
||||||
|
}
|
||||||
|
|
||||||
let importText = `<script src="https://mempool.space/mempool.js"></script>`;
|
let importText = `<script src="https://mempool.space/mempool.js"></script>`;
|
||||||
if (this.layer === 'bisq') {
|
if (this.env.BASE_MODULE === 'bisq') {
|
||||||
importText = `<script src="https://bisq.markets/bisq.js"></script>`;
|
importText = `<script src="https://bisq.markets/bisq.js"></script>`;
|
||||||
}
|
}
|
||||||
if (this.layer === 'liquid') {
|
if (this.env.BASE_MODULE === 'liquid') {
|
||||||
importText = `<script src="https://liquid.network/liquid.js"></script>`;
|
importText = `<script src="https://liquid.network/liquid.js"></script>`;
|
||||||
}
|
}
|
||||||
return `<!DOCTYPE html>
|
|
||||||
|
let resultHtml = '<pre id="result"></pre>';
|
||||||
|
if (this.method === 'websocket') {
|
||||||
|
resultHtml = `<pre id="result-blocks"></pre>
|
||||||
|
<pre id="result-mempool-info"></pre>
|
||||||
|
<pre id="result-transactions"></pre>
|
||||||
|
<pre id="result-mempool-blocks"></pre>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
${importText}
|
${importText}
|
||||||
@@ -105,14 +191,11 @@ init();`;
|
|||||||
init();
|
init();
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body></body>
|
<body>
|
||||||
|
${resultHtml}
|
||||||
|
</body>
|
||||||
</html>`;
|
</html>`;
|
||||||
}
|
|
||||||
wrapCurl(code: string) {
|
|
||||||
if (this.network && this.network !== 'mainnet') {
|
|
||||||
return code.replace('mempool.space/', `mempool.space/${this.network}/`);
|
|
||||||
}
|
}
|
||||||
return code;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wrapImportTemplate() {
|
wrapImportTemplate() {
|
||||||
@@ -123,7 +206,7 @@ npm install @mempool/mempool.js --save
|
|||||||
# yarn
|
# yarn
|
||||||
yarn add @mempool/mempool.js`;
|
yarn add @mempool/mempool.js`;
|
||||||
|
|
||||||
if (this.layer === 'bisq') {
|
if (this.env.BASE_MODULE === 'bisq') {
|
||||||
importTemplate = `# npm
|
importTemplate = `# npm
|
||||||
npm install @mempool/bisq.js --save
|
npm install @mempool/bisq.js --save
|
||||||
|
|
||||||
@@ -131,7 +214,7 @@ npm install @mempool/bisq.js --save
|
|||||||
yarn add @mempool/bisq.js`;
|
yarn add @mempool/bisq.js`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.layer === 'liquid') {
|
if (this.env.BASE_MODULE === 'liquid') {
|
||||||
importTemplate = `# npm
|
importTemplate = `# npm
|
||||||
npm install @mempool/liquid.js --save
|
npm install @mempool/liquid.js --save
|
||||||
|
|
||||||
@@ -142,4 +225,78 @@ yarn add @mempool/liquid.js`;
|
|||||||
return importTemplate;
|
return importTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wrapCurlTemplate(code: any) {
|
||||||
|
if (code.codeTemplate) {
|
||||||
|
if (this.network === 'testnet') {
|
||||||
|
return this.replaceCurlPlaceholder(code.codeTemplate.curl, code.codeSampleTestnet);
|
||||||
|
}
|
||||||
|
if (this.network === 'signet') {
|
||||||
|
return this.replaceCurlPlaceholder(code.codeTemplate.curl, code.codeSampleSignet);
|
||||||
|
}
|
||||||
|
if (this.network === 'liquid') {
|
||||||
|
return this.replaceCurlPlaceholder(code.codeTemplate.curl, code.codeSampleLiquid);
|
||||||
|
}
|
||||||
|
if (this.network === 'bisq') {
|
||||||
|
return this.replaceCurlPlaceholder(code.codeTemplate.curl, code.codeSampleBisq);
|
||||||
|
}
|
||||||
|
if (this.network === '' || this.network === 'main') {
|
||||||
|
return this.replaceCurlPlaceholder(code.codeTemplate.curl, code.codeSampleMainnet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapResponse(code: any) {
|
||||||
|
if (this.method === 'websocket') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
if (this.network === 'testnet') {
|
||||||
|
return code.codeSampleTestnet.response;
|
||||||
|
}
|
||||||
|
if (this.network === 'signet') {
|
||||||
|
return code.codeSampleSignet.response;
|
||||||
|
}
|
||||||
|
if (this.network === 'liquid') {
|
||||||
|
return code.codeSampleLiquid.response;
|
||||||
|
}
|
||||||
|
if (this.network === 'bisq') {
|
||||||
|
return code.codeSampleBisq.response;
|
||||||
|
}
|
||||||
|
return code.codeSampleMainnet.response;
|
||||||
|
}
|
||||||
|
|
||||||
|
replaceJSPlaceholder(text: string, code: any) {
|
||||||
|
for (let index = 0; index < code.length; index++) {
|
||||||
|
const textReplace = code[index];
|
||||||
|
const indexNumber = index + 1;
|
||||||
|
text = text.replace('%{' + indexNumber + '}', textReplace);
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
replaceCurlPlaceholder(curlText: any, code: any) {
|
||||||
|
let text = curlText;
|
||||||
|
for (let index = 0; index < code.curl.length; index++) {
|
||||||
|
const textReplace = code.curl[index];
|
||||||
|
const indexNumber = index + 1;
|
||||||
|
text = text.replace('%{' + indexNumber + '}', textReplace);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.env.BASE_MODULE === 'mempool') {
|
||||||
|
if (this.network === 'main' || this.network === '') {
|
||||||
|
if (this.method === 'post') {
|
||||||
|
return `curl POST -sSLd "${text}"`;
|
||||||
|
}
|
||||||
|
return `curl -sSL "${this.hostname}${text}"`;
|
||||||
|
}
|
||||||
|
if (this.method === 'post') {
|
||||||
|
text = text.replace('/api', `/${this.network}/api`);
|
||||||
|
return `curl POST -sSLd "${text}"`;
|
||||||
|
}
|
||||||
|
return `curl -sSL "${this.hostname}/${this.network}${text}"`;
|
||||||
|
}
|
||||||
|
if (this.env.BASE_MODULE !== 'mempool') {
|
||||||
|
return `curl -sSL "${this.hostname}${text}"`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,8 +65,20 @@ export class BlockComponent implements OnInit, OnDestroy {
|
|||||||
map((indicators) => indicators['blocktxs-' + this.blockHash] !== undefined ? indicators['blocktxs-' + this.blockHash] : 0)
|
map((indicators) => indicators['blocktxs-' + this.blockHash] !== undefined ? indicators['blocktxs-' + this.blockHash] : 0)
|
||||||
);
|
);
|
||||||
|
|
||||||
this.subscription = this.route.paramMap
|
this.blocksSubscription = this.stateService.blocks$
|
||||||
.pipe(
|
.subscribe(([block]) => {
|
||||||
|
this.latestBlock = block;
|
||||||
|
this.latestBlocks.unshift(block);
|
||||||
|
this.latestBlocks = this.latestBlocks.slice(0, this.stateService.env.KEEP_BLOCKS_AMOUNT);
|
||||||
|
this.setNextAndPreviousBlockLink();
|
||||||
|
|
||||||
|
if (block.id === this.blockHash) {
|
||||||
|
this.block = block;
|
||||||
|
this.fees = block.reward / 100000000 - this.blockSubsidy;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.subscription = this.route.paramMap.pipe(
|
||||||
switchMap((params: ParamMap) => {
|
switchMap((params: ParamMap) => {
|
||||||
const blockHash: string = params.get('id') || '';
|
const blockHash: string = params.get('id') || '';
|
||||||
this.block = undefined;
|
this.block = undefined;
|
||||||
@@ -94,7 +106,12 @@ export class BlockComponent implements OnInit, OnDestroy {
|
|||||||
} else {
|
} else {
|
||||||
this.isLoadingBlock = true;
|
this.isLoadingBlock = true;
|
||||||
|
|
||||||
|
let blockInCache: Block;
|
||||||
if (isBlockHeight) {
|
if (isBlockHeight) {
|
||||||
|
blockInCache = this.latestBlocks.find((block) => block.height === parseInt(blockHash, 10));
|
||||||
|
if (blockInCache) {
|
||||||
|
return of(blockInCache);
|
||||||
|
}
|
||||||
return this.electrsApiService.getBlockHashFromHeight$(parseInt(blockHash, 10))
|
return this.electrsApiService.getBlockHashFromHeight$(parseInt(blockHash, 10))
|
||||||
.pipe(
|
.pipe(
|
||||||
switchMap((hash) => {
|
switchMap((hash) => {
|
||||||
@@ -107,12 +124,11 @@ export class BlockComponent implements OnInit, OnDestroy {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.stateService.blocks$.subscribe(([block]) => {
|
blockInCache = this.latestBlocks.find((block) => block.id === this.blockHash);
|
||||||
if (block.id === blockHash) {
|
if (blockInCache) {
|
||||||
this.block = block;
|
return of(blockInCache);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
return this.electrsApiService.getBlock$(blockHash);
|
return this.electrsApiService.getBlock$(blockHash);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
@@ -159,14 +175,6 @@ export class BlockComponent implements OnInit, OnDestroy {
|
|||||||
this.isLoadingBlock = false;
|
this.isLoadingBlock = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.blocksSubscription = this.stateService.blocks$
|
|
||||||
.subscribe(([block]) => {
|
|
||||||
this.latestBlock = block;
|
|
||||||
this.latestBlocks.unshift(block);
|
|
||||||
this.latestBlocks = this.latestBlocks.slice(0, this.stateService.env.KEEP_BLOCKS_AMOUNT);
|
|
||||||
this.setNextAndPreviousBlockLink();
|
|
||||||
});
|
|
||||||
|
|
||||||
this.networkChangedSubscription = this.stateService.networkChanged$
|
this.networkChangedSubscription = this.stateService.networkChanged$
|
||||||
.subscribe((network) => this.network = network);
|
.subscribe((network) => this.network = network);
|
||||||
|
|
||||||
@@ -269,12 +277,14 @@ export class BlockComponent implements OnInit, OnDestroy {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const block = this.latestBlocks.find((b) => b.height === this.nextBlockHeight - 2);
|
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/', block ? block.id : this.block.previousblockhash], { state: { data: { block, blockHeight: this.nextBlockHeight - 2 } } });
|
this.router.navigate([(this.network && this.stateService.env.BASE_MODULE === 'mempool' ? '/' + this.network : '') + '/block/',
|
||||||
|
block ? block.id : this.block.previousblockhash], { state: { data: { block, blockHeight: this.nextBlockHeight - 2 } } });
|
||||||
}
|
}
|
||||||
|
|
||||||
navigateToNextBlock() {
|
navigateToNextBlock() {
|
||||||
const block = this.latestBlocks.find((b) => b.height === this.nextBlockHeight);
|
const block = this.latestBlocks.find((b) => b.height === this.nextBlockHeight);
|
||||||
this.router.navigate([(this.network && this.stateService.env.BASE_MODULE === 'mempool' ? '/' + this.network : '') + '/block/', block ? block.id : this.nextBlockHeight], { state: { data: { block, blockHeight: this.nextBlockHeight } } });
|
this.router.navigate([(this.network && this.stateService.env.BASE_MODULE === 'mempool' ? '/' + this.network : '') + '/block/',
|
||||||
|
block ? block.id : this.nextBlockHeight], { state: { data: { block, blockHeight: this.nextBlockHeight } } });
|
||||||
}
|
}
|
||||||
|
|
||||||
setNextAndPreviousBlockLink(){
|
setNextAndPreviousBlockLink(){
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export class SearchFormComponent implements OnInit {
|
|||||||
searchForm: FormGroup;
|
searchForm: FormGroup;
|
||||||
@Output() searchTriggered = new EventEmitter();
|
@Output() searchTriggered = new EventEmitter();
|
||||||
|
|
||||||
regexAddress = /^([a-km-zA-HJ-NP-Z1-9]{26,35}|[a-km-zA-HJ-NP-Z1-9]{80}|[bB]?[a-z]{2,5}1[ac-hj-np-z02-9]{8,87})$/;
|
regexAddress = /^([a-km-zA-HJ-NP-Z1-9]{26,35}|[a-km-zA-HJ-NP-Z1-9]{80}|[a-z]{2,5}1[ac-hj-np-z02-9]{8,100}|[A-Z]{2,5}1[AC-HJ-NP-Z02-9]{8,100})$/;
|
||||||
regexBlockhash = /^[0]{8}[a-fA-F0-9]{56}$/;
|
regexBlockhash = /^[0]{8}[a-fA-F0-9]{56}$/;
|
||||||
regexTransaction = /^[a-fA-F0-9]{64}$/;
|
regexTransaction = /^[a-fA-F0-9]{64}$/;
|
||||||
regexBlockheight = /^[0-9]+$/;
|
regexBlockheight = /^[0-9]+$/;
|
||||||
|
|||||||
@@ -34,3 +34,16 @@ export interface DifficultyAdjustment {
|
|||||||
remainingBlocks: number;
|
remainingBlocks: number;
|
||||||
remainingTime: number;
|
remainingTime: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AddressInformation {
|
||||||
|
isvalid: boolean; // (boolean) If the address is valid or not. If not, this is the only property returned.
|
||||||
|
isvalid_parent?: boolean; // (boolean) Elements only
|
||||||
|
address: string; // (string) The bitcoin address validated
|
||||||
|
scriptPubKey: string; // (string) The hex-encoded scriptPubKey generated by the address
|
||||||
|
isscript: boolean; // (boolean) If the key is a script
|
||||||
|
iswitness: boolean; // (boolean) If the address is a witness
|
||||||
|
witness_version?: boolean; // (numeric, optional) The version number of the witness program
|
||||||
|
witness_program: string; // (string, optional) The hex value of the witness program
|
||||||
|
confidential_key?: string; // (string) Elements only
|
||||||
|
unconfidential?: string; // (string) Elements only
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { HttpClient, HttpParams } from '@angular/common/http';
|
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||||
import { CpfpInfo, OptimizedMempoolStats, DifficultyAdjustment } from '../interfaces/node-api.interface';
|
import { CpfpInfo, OptimizedMempoolStats, DifficultyAdjustment, AddressInformation } from '../interfaces/node-api.interface';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { StateService } from './state.service';
|
import { StateService } from './state.service';
|
||||||
import { WebsocketResponse } from '../interfaces/websocket.interface';
|
import { WebsocketResponse } from '../interfaces/websocket.interface';
|
||||||
@@ -96,4 +96,8 @@ export class ApiService {
|
|||||||
getDifficultyAdjustment$(): Observable<DifficultyAdjustment> {
|
getDifficultyAdjustment$(): Observable<DifficultyAdjustment> {
|
||||||
return this.httpClient.get<DifficultyAdjustment>(this.apiBaseUrl + this.apiBasePath + '/api/v1/difficulty-adjustment');
|
return this.httpClient.get<DifficultyAdjustment>(this.apiBaseUrl + this.apiBasePath + '/api/v1/difficulty-adjustment');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validateAddress$(address: string): Observable<AddressInformation> {
|
||||||
|
return this.httpClient.get<AddressInformation>(this.apiBaseUrl + this.apiBasePath + '/api/v1/validate-address/' + address);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
"arrow-parens": false,
|
"arrow-parens": false,
|
||||||
"arrow-return-shorthand": true,
|
"arrow-return-shorthand": true,
|
||||||
"curly": true,
|
"curly": true,
|
||||||
|
"no-bitwise": false,
|
||||||
"deprecation": {
|
"deprecation": {
|
||||||
"severity": "warning"
|
"severity": "warning"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
HOSTNAME=$(hostname)
|
HOSTNAME=$(hostname)
|
||||||
|
|
||||||
echo restarting mempool backends | wall
|
echo restarting mempool backends | wall
|
||||||
echo "${HOSTNAME} restarted mempool backends" | /usr/local/bin/keybase chat send --nonblock --channel general mempool.devops
|
echo "${HOSTNAME} restarted mempool backends" | /usr/local/bin/keybase chat send --nonblock --channel general mempool.ops
|
||||||
ps uaxw|grep 'dist/index'|grep -v grep|grep -v services|awk '{print $2}'|xargs -n 1 kill
|
ps uaxw|grep 'dist/index'|grep -v grep|grep -v services|awk '{print $2}'|xargs -n 1 kill
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
|
|||||||
@@ -70,6 +70,6 @@ for target in mainnet liquid bisq;do build_frontend "${target}";done
|
|||||||
for target in mainnet liquid bisq;do ship_frontend "${target}";done
|
for target in mainnet liquid bisq;do ship_frontend "${target}";done
|
||||||
|
|
||||||
echo "${HOSTNAME} updated to \`${REF}\` @ \`${HASH}\`" | /usr/local/bin/keybase chat send --nonblock --channel general mempool.dev
|
echo "${HOSTNAME} updated to \`${REF}\` @ \`${HASH}\`" | /usr/local/bin/keybase chat send --nonblock --channel general mempool.dev
|
||||||
echo "${HOSTNAME} updated to \`${REF}\` @ \`${HASH}\`" | /usr/local/bin/keybase chat send --nonblock --channel general mempool.devops
|
echo "${HOSTNAME} updated to \`${REF}\` @ \`${HASH}\`" | /usr/local/bin/keybase chat send --nonblock --channel general mempool.ops
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
local7.>=notice |/usr/local/bin/sudo -u mempool /usr/local/bin/mempool-logger mempool.devops general
|
local7.>=notice |/usr/local/bin/sudo -u mempool /usr/local/bin/mempool-logger mempool.ops alerts
|
||||||
local7.info |/usr/local/bin/sudo -u mempool /usr/local/bin/mempool-logger mempool.devops node100
|
local7.info |/usr/local/bin/sudo -u mempool /usr/local/bin/mempool-logger mempool.ops node100
|
||||||
local7.info /var/log/mempool
|
local7.info /var/log/mempool
|
||||||
local7.* /var/log/mempool.debug
|
local7.* /var/log/mempool.debug
|
||||||
|
|||||||
Reference in New Issue
Block a user