Compare commits
24 Commits
orangesurf
...
mononaut/s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
283fd1c58e | ||
|
|
3c84505579 | ||
|
|
81315af206 | ||
|
|
6fa747b303 | ||
|
|
c66f028f12 | ||
|
|
ed28a24c8a | ||
|
|
ddcf745722 | ||
|
|
774c0b4f83 | ||
|
|
734d5f2461 | ||
|
|
24a76cafa4 | ||
|
|
348a12c4a1 | ||
|
|
67750bd166 | ||
|
|
e5ea7afbe2 | ||
|
|
4b56144e6e | ||
|
|
332099cc01 | ||
|
|
0a933d022c | ||
|
|
47044db043 | ||
|
|
01df22ef86 | ||
|
|
ad360db71f | ||
|
|
a2bc6f5bba | ||
|
|
9e5b7436d4 | ||
|
|
5a3ee725b8 | ||
|
|
7a8ae7c9a6 | ||
|
|
74b420c258 |
@@ -3,6 +3,10 @@ import logger from '../../logger';
|
||||
import bitcoinClient from './bitcoin-client';
|
||||
import config from '../../config';
|
||||
|
||||
const BLOCKHASH_REGEX = /^[a-f0-9]{64}$/i;
|
||||
const TXID_REGEX = /^[a-f0-9]{64}$/i;
|
||||
const RAW_TX_REGEX = /^[a-f0-9]{2,}$/i;
|
||||
|
||||
/**
|
||||
* Define a set of routes used by the accelerator server
|
||||
* Those routes are not designed to be public
|
||||
@@ -10,7 +14,7 @@ import config from '../../config';
|
||||
class BitcoinBackendRoutes {
|
||||
private static tag = 'BitcoinBackendRoutes';
|
||||
|
||||
public initRoutes(app: Application) {
|
||||
public initRoutes(app: Application): void {
|
||||
app
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'internal/bitcoin-core/' + 'get-mempool-entry', this.disableCache, this.$getMempoolEntry)
|
||||
.post(config.MEMPOOL.API_URL_PREFIX + 'internal/bitcoin-core/' + 'decode-raw-transaction', this.disableCache, this.$decodeRawTransaction)
|
||||
@@ -26,10 +30,10 @@ class BitcoinBackendRoutes {
|
||||
|
||||
/**
|
||||
* Disable caching for bitcoin core routes
|
||||
*
|
||||
* @param req
|
||||
* @param res
|
||||
* @param next
|
||||
*
|
||||
* @param req
|
||||
* @param res
|
||||
* @param next
|
||||
*/
|
||||
private disableCache(req: Request, res: Response, next: NextFunction): void {
|
||||
res.setHeader('Pragma', 'no-cache');
|
||||
@@ -40,16 +44,16 @@ class BitcoinBackendRoutes {
|
||||
|
||||
/**
|
||||
* Exeption handler to return proper details to the accelerator server
|
||||
*
|
||||
* @param e
|
||||
* @param fnName
|
||||
* @param res
|
||||
*
|
||||
* @param e
|
||||
* @param fnName
|
||||
* @param res
|
||||
*/
|
||||
private static handleException(e: any, fnName: string, res: Response): void {
|
||||
if (typeof(e.code) === 'number') {
|
||||
res.status(400).send(JSON.stringify(e, ['code', 'message']));
|
||||
} else {
|
||||
const err = `exception in ${fnName}. ${e}. Details: ${JSON.stringify(e, ['code', 'message'])}`;
|
||||
res.status(400).send(JSON.stringify(e, ['code']));
|
||||
} else {
|
||||
const err = `unknown exception in ${fnName}`;
|
||||
logger.err(err, BitcoinBackendRoutes.tag);
|
||||
res.status(500).send(err);
|
||||
}
|
||||
@@ -58,13 +62,13 @@ class BitcoinBackendRoutes {
|
||||
private async $getMempoolEntry(req: Request, res: Response): Promise<void> {
|
||||
const txid = req.query.txid;
|
||||
try {
|
||||
if (typeof(txid) !== 'string' || txid.length !== 64) {
|
||||
res.status(400).send(`invalid param txid ${txid}. must be a string of 64 char`);
|
||||
if (typeof(txid) !== 'string' || txid.length !== 64 || !TXID_REGEX.test(txid)) {
|
||||
res.status(400).send(`invalid param txid. must be 64 hexadecimal characters`);
|
||||
return;
|
||||
}
|
||||
const mempoolEntry = await bitcoinClient.getMempoolEntry(txid);
|
||||
if (!mempoolEntry) {
|
||||
res.status(404).send(`no mempool entry found for txid ${txid}`);
|
||||
res.status(404).send();
|
||||
return;
|
||||
}
|
||||
res.status(200).send(mempoolEntry);
|
||||
@@ -76,13 +80,13 @@ class BitcoinBackendRoutes {
|
||||
private async $decodeRawTransaction(req: Request, res: Response): Promise<void> {
|
||||
const rawTx = req.body.rawTx;
|
||||
try {
|
||||
if (typeof(rawTx) !== 'string') {
|
||||
res.status(400).send(`invalid param rawTx ${rawTx}. must be a string`);
|
||||
if (typeof(rawTx) !== 'string' || !RAW_TX_REGEX.test(rawTx)) {
|
||||
res.status(400).send(`invalid param rawTx. must be a string of hexadecimal characters`);
|
||||
return;
|
||||
}
|
||||
const decodedTx = await bitcoinClient.decodeRawTransaction(rawTx);
|
||||
if (!decodedTx) {
|
||||
res.status(400).send(`unable to decode rawTx ${rawTx}`);
|
||||
res.status(400).send(`unable to decode rawTx`);
|
||||
return;
|
||||
}
|
||||
res.status(200).send(decodedTx);
|
||||
@@ -95,23 +99,23 @@ class BitcoinBackendRoutes {
|
||||
const txid = req.query.txid;
|
||||
const verbose = req.query.verbose;
|
||||
try {
|
||||
if (typeof(txid) !== 'string' || txid.length !== 64) {
|
||||
res.status(400).send(`invalid param txid ${txid}. must be a string of 64 char`);
|
||||
if (typeof(txid) !== 'string' || txid.length !== 64 || !TXID_REGEX.test(txid)) {
|
||||
res.status(400).send(`invalid param txid. must be 64 hexadecimal characters`);
|
||||
return;
|
||||
}
|
||||
if (typeof(verbose) !== 'string') {
|
||||
res.status(400).send(`invalid param verbose ${verbose}. must be a string representing an integer`);
|
||||
res.status(400).send(`invalid param verbose. must be a string representing an integer`);
|
||||
return;
|
||||
}
|
||||
const verboseNumber = parseInt(verbose, 10);
|
||||
if (typeof(verboseNumber) !== 'number') {
|
||||
res.status(400).send(`invalid param verbose ${verbose}. must be a valid integer`);
|
||||
res.status(400).send(`invalid param verbose. must be a valid integer`);
|
||||
return;
|
||||
}
|
||||
|
||||
const decodedTx = await bitcoinClient.getRawTransaction(txid, verboseNumber);
|
||||
if (!decodedTx) {
|
||||
res.status(400).send(`unable to get raw transaction for txid ${txid}`);
|
||||
res.status(400).send(`unable to get raw transaction`);
|
||||
return;
|
||||
}
|
||||
res.status(200).send(decodedTx);
|
||||
@@ -123,13 +127,13 @@ class BitcoinBackendRoutes {
|
||||
private async $sendRawTransaction(req: Request, res: Response): Promise<void> {
|
||||
const rawTx = req.body.rawTx;
|
||||
try {
|
||||
if (typeof(rawTx) !== 'string') {
|
||||
res.status(400).send(`invalid param rawTx ${rawTx}. must be a string`);
|
||||
if (typeof(rawTx) !== 'string' || !RAW_TX_REGEX.test(rawTx)) {
|
||||
res.status(400).send(`invalid param rawTx. must be a string of hexadecimal characters`);
|
||||
return;
|
||||
}
|
||||
const txHex = await bitcoinClient.sendRawTransaction(rawTx);
|
||||
if (!txHex) {
|
||||
res.status(400).send(`unable to send rawTx ${rawTx}`);
|
||||
res.status(400).send(`unable to send rawTx`);
|
||||
return;
|
||||
}
|
||||
res.status(200).send(txHex);
|
||||
@@ -141,13 +145,13 @@ class BitcoinBackendRoutes {
|
||||
private async $testMempoolAccept(req: Request, res: Response): Promise<void> {
|
||||
const rawTxs = req.body.rawTxs;
|
||||
try {
|
||||
if (typeof(rawTxs) !== 'object') {
|
||||
res.status(400).send(`invalid param rawTxs ${JSON.stringify(rawTxs)}. must be an array of string`);
|
||||
if (typeof(rawTxs) !== 'object' || !Array.isArray(rawTxs) || rawTxs.some((tx) => typeof(tx) !== 'string' || !RAW_TX_REGEX.test(tx))) {
|
||||
res.status(400).send(`invalid param rawTxs. must be an array of strings of hexadecimal characters`);
|
||||
return;
|
||||
}
|
||||
const txHex = await bitcoinClient.testMempoolAccept(rawTxs);
|
||||
if (typeof(txHex) !== 'object' || txHex.length === 0) {
|
||||
res.status(400).send(`testmempoolaccept failed for raw txs ${JSON.stringify(rawTxs)}, got an empty result`);
|
||||
res.status(400).send(`testmempoolaccept failed for raw txs, got an empty result`);
|
||||
return;
|
||||
}
|
||||
res.status(200).send(txHex);
|
||||
@@ -160,18 +164,18 @@ class BitcoinBackendRoutes {
|
||||
const txid = req.query.txid;
|
||||
const verbose = req.query.verbose;
|
||||
try {
|
||||
if (typeof(txid) !== 'string' || txid.length !== 64) {
|
||||
res.status(400).send(`invalid param txid ${txid}. must be a string of 64 char`);
|
||||
if (typeof(txid) !== 'string' || txid.length !== 64 || !TXID_REGEX.test(txid)) {
|
||||
res.status(400).send(`invalid param txid. must be 64 hexadecimal characters`);
|
||||
return;
|
||||
}
|
||||
if (typeof(verbose) !== 'string' || (verbose !== 'true' && verbose !== 'false')) {
|
||||
res.status(400).send(`invalid param verbose ${verbose}. must be a string ('true' | 'false')`);
|
||||
res.status(400).send(`invalid param verbose. must be a string ('true' | 'false')`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const ancestors = await bitcoinClient.getMempoolAncestors(txid, verbose === 'true' ? true : false);
|
||||
if (!ancestors) {
|
||||
res.status(400).send(`unable to get mempool ancestors for txid ${txid}`);
|
||||
res.status(400).send(`unable to get mempool ancestors`);
|
||||
return;
|
||||
}
|
||||
res.status(200).send(ancestors);
|
||||
@@ -184,23 +188,23 @@ class BitcoinBackendRoutes {
|
||||
const blockHash = req.query.hash;
|
||||
const verbosity = req.query.verbosity;
|
||||
try {
|
||||
if (typeof(blockHash) !== 'string' || blockHash.length !== 64) {
|
||||
res.status(400).send(`invalid param blockHash ${blockHash}. must be a string of 64 char`);
|
||||
if (typeof(blockHash) !== 'string' || blockHash.length !== 64 || !BLOCKHASH_REGEX.test(blockHash)) {
|
||||
res.status(400).send(`invalid param blockHash. must be 64 hexadecimal characters`);
|
||||
return;
|
||||
}
|
||||
if (typeof(verbosity) !== 'string') {
|
||||
res.status(400).send(`invalid param verbosity ${verbosity}. must be a string representing an integer`);
|
||||
res.status(400).send(`invalid param verbosity. must be a string representing an integer`);
|
||||
return;
|
||||
}
|
||||
const verbosityNumber = parseInt(verbosity, 10);
|
||||
if (typeof(verbosityNumber) !== 'number') {
|
||||
res.status(400).send(`invalid param verbosity ${verbosity}. must be a valid integer`);
|
||||
res.status(400).send(`invalid param verbosity. must be a valid integer`);
|
||||
return;
|
||||
}
|
||||
|
||||
const block = await bitcoinClient.getBlock(blockHash, verbosityNumber);
|
||||
if (!block) {
|
||||
res.status(400).send(`unable to get block for block hash ${blockHash}`);
|
||||
res.status(400).send(`unable to get block`);
|
||||
return;
|
||||
}
|
||||
res.status(200).send(block);
|
||||
@@ -213,18 +217,18 @@ class BitcoinBackendRoutes {
|
||||
const blockHeight = req.query.height;
|
||||
try {
|
||||
if (typeof(blockHeight) !== 'string') {
|
||||
res.status(400).send(`invalid param blockHeight ${blockHeight}, must be a string representing an integer`);
|
||||
res.status(400).send(`invalid param blockHeight, must be a string representing an integer`);
|
||||
return;
|
||||
}
|
||||
const blockHeightNumber = parseInt(blockHeight, 10);
|
||||
if (typeof(blockHeightNumber) !== 'number') {
|
||||
res.status(400).send(`invalid param blockHeight ${blockHeight}. must be a valid integer`);
|
||||
res.status(400).send(`invalid param blockHeight. must be a valid integer`);
|
||||
return;
|
||||
}
|
||||
|
||||
const block = await bitcoinClient.getBlockHash(blockHeightNumber);
|
||||
if (!block) {
|
||||
res.status(400).send(`unable to get block hash for block height ${blockHeightNumber}`);
|
||||
res.status(400).send(`unable to get block hash`);
|
||||
return;
|
||||
}
|
||||
res.status(200).send(block);
|
||||
@@ -247,4 +251,4 @@ class BitcoinBackendRoutes {
|
||||
}
|
||||
}
|
||||
|
||||
export default new BitcoinBackendRoutes
|
||||
export default new BitcoinBackendRoutes;
|
||||
@@ -22,6 +22,11 @@ import rbfCache from '../rbf-cache';
|
||||
import { calculateMempoolTxCpfp } from '../cpfp';
|
||||
import { handleError } from '../../utils/api';
|
||||
|
||||
const TXID_REGEX = /^[a-f0-9]{64}$/i;
|
||||
const BLOCK_HASH_REGEX = /^[a-f0-9]{64}$/i;
|
||||
const ADDRESS_REGEX = /^[a-z0-9]{2,120}$/i;
|
||||
const SCRIPT_HASH_REGEX = /^([a-f0-9]{2})+$/i;
|
||||
|
||||
class BitcoinRoutes {
|
||||
public initRoutes(app: Application) {
|
||||
app
|
||||
@@ -90,7 +95,7 @@ class BitcoinRoutes {
|
||||
res.set('Content-Type', 'application/json');
|
||||
res.send(result);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get init data');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,7 +114,7 @@ class BitcoinRoutes {
|
||||
const result = mempoolBlocks.getMempoolBlocks();
|
||||
res.json(result);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get mempool blocks');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +126,10 @@ class BitcoinRoutes {
|
||||
const txIds: string[] = [];
|
||||
for (const _txId in req.query.txId) {
|
||||
if (typeof req.query.txId[_txId] === 'string') {
|
||||
txIds.push(req.query.txId[_txId].toString());
|
||||
const txid = req.query.txId[_txId].toString();
|
||||
if (TXID_REGEX.test(txid)) {
|
||||
txIds.push(txid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,18 +148,22 @@ class BitcoinRoutes {
|
||||
handleError(req, res, 400, 'Too many txids requested');
|
||||
return;
|
||||
}
|
||||
if (txids.some((txid) => !TXID_REGEX.test(txid))) {
|
||||
handleError(req, res, 400, 'Invalid txids format');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const batchedOutspends = await bitcoinApi.$getBatchedOutspends(txids);
|
||||
res.json(batchedOutspends);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get batched outspends');
|
||||
}
|
||||
}
|
||||
|
||||
private async $getCpfpInfo(req: Request, res: Response) {
|
||||
if (!/^[a-fA-F0-9]{64}$/.test(req.params.txId)) {
|
||||
handleError(req, res, 501, `Invalid transaction ID.`);
|
||||
if (!TXID_REGEX.test(req.params.txId)) {
|
||||
handleError(req, res, 501, `Invalid transaction ID`);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -184,7 +196,7 @@ class BitcoinRoutes {
|
||||
try {
|
||||
cpfpInfo = await transactionRepository.$getCpfpInfo(req.params.txId);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, 'failed to get CPFP info');
|
||||
handleError(req, res, 500, 'Failed to get CPFP info');
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -205,6 +217,10 @@ class BitcoinRoutes {
|
||||
}
|
||||
|
||||
private async getTransaction(req: Request, res: Response) {
|
||||
if (!TXID_REGEX.test(req.params.txId)) {
|
||||
handleError(req, res, 501, `Invalid transaction ID`);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const transaction = await transactionUtils.$getTransactionExtended(req.params.txId, true, false, false, true);
|
||||
res.json(transaction);
|
||||
@@ -212,12 +228,18 @@ class BitcoinRoutes {
|
||||
let statusCode = 500;
|
||||
if (e instanceof Error && e instanceof Error && e.message && e.message.indexOf('No such mempool or blockchain transaction') > -1) {
|
||||
statusCode = 404;
|
||||
handleError(req, res, statusCode, 'No such mempool or blockchain transaction');
|
||||
return;
|
||||
}
|
||||
handleError(req, res, statusCode, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, statusCode, 'Failed to get transaction');
|
||||
}
|
||||
}
|
||||
|
||||
private async getRawTransaction(req: Request, res: Response) {
|
||||
if (!TXID_REGEX.test(req.params.txId)) {
|
||||
handleError(req, res, 501, `Invalid transaction ID`);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const transaction: IEsploraApi.Transaction = await bitcoinApi.$getRawTransaction(req.params.txId, true);
|
||||
res.setHeader('content-type', 'text/plain');
|
||||
@@ -226,8 +248,10 @@ class BitcoinRoutes {
|
||||
let statusCode = 500;
|
||||
if (e instanceof Error && e.message && e.message.indexOf('No such mempool or blockchain transaction') > -1) {
|
||||
statusCode = 404;
|
||||
handleError(req, res, statusCode, 'No such mempool or blockchain transaction');
|
||||
return;
|
||||
}
|
||||
handleError(req, res, statusCode, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, statusCode, 'Failed to get raw transaction');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,14 +316,18 @@ class BitcoinRoutes {
|
||||
}
|
||||
} catch (e: any) {
|
||||
if (e instanceof Error && new RegExp(notFoundError).test(e.message)) {
|
||||
handleError(req, res, 404, e.message);
|
||||
handleError(req, res, 404, notFoundError);
|
||||
} else {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to process PSBT');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async getTransactionStatus(req: Request, res: Response) {
|
||||
if (!TXID_REGEX.test(req.params.txId)) {
|
||||
handleError(req, res, 501, `Invalid transaction ID`);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const transaction = await transactionUtils.$getTransactionExtended(req.params.txId, true);
|
||||
res.json(transaction.status);
|
||||
@@ -307,36 +335,54 @@ class BitcoinRoutes {
|
||||
let statusCode = 500;
|
||||
if (e instanceof Error && e.message && e.message.indexOf('No such mempool or blockchain transaction') > -1) {
|
||||
statusCode = 404;
|
||||
handleError(req, res, statusCode, 'No such mempool or blockchain transaction');
|
||||
return;
|
||||
}
|
||||
handleError(req, res, statusCode, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, statusCode, 'Failed to get transaction status');
|
||||
}
|
||||
}
|
||||
|
||||
private async getStrippedBlockTransactions(req: Request, res: Response) {
|
||||
if (!BLOCK_HASH_REGEX.test(req.params.hash)) {
|
||||
handleError(req, res, 501, `Invalid block hash`);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const transactions = await blocks.$getStrippedBlockTransactions(req.params.hash);
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 3600 * 24 * 30).toUTCString());
|
||||
res.json(transactions);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get block summary');
|
||||
}
|
||||
}
|
||||
|
||||
private async getStrippedBlockTransaction(req: Request, res: Response) {
|
||||
if (!BLOCK_HASH_REGEX.test(req.params.hash)) {
|
||||
handleError(req, res, 501, `Invalid block hash`);
|
||||
return;
|
||||
}
|
||||
if (!TXID_REGEX.test(req.params.txid)) {
|
||||
handleError(req, res, 501, `Invalid transaction ID`);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const transaction = await blocks.$getSingleTxFromSummary(req.params.hash, req.params.txid);
|
||||
if (!transaction) {
|
||||
handleError(req, res, 404, `transaction not found in summary`);
|
||||
handleError(req, res, 404, `Transaction not found in summary`);
|
||||
return;
|
||||
}
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 3600 * 24 * 30).toUTCString());
|
||||
res.json(transaction);
|
||||
} catch (e) {
|
||||
res.status(500).send(e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get transaction from summary');
|
||||
}
|
||||
}
|
||||
|
||||
private async getBlock(req: Request, res: Response) {
|
||||
if (!BLOCK_HASH_REGEX.test(req.params.hash)) {
|
||||
handleError(req, res, 501, `Invalid block hash`);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const block = await blocks.$getBlock(req.params.hash);
|
||||
|
||||
@@ -348,53 +394,69 @@ class BitcoinRoutes {
|
||||
} else if (blockAge > 30 * day) {
|
||||
cacheDuration = 10 * day;
|
||||
} else {
|
||||
cacheDuration = 600
|
||||
cacheDuration = 600;
|
||||
}
|
||||
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * cacheDuration).toUTCString());
|
||||
res.json(block);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get block');
|
||||
}
|
||||
}
|
||||
|
||||
private async getBlockHeader(req: Request, res: Response) {
|
||||
if (!BLOCK_HASH_REGEX.test(req.params.hash)) {
|
||||
handleError(req, res, 501, `Invalid block hash`);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const blockHeader = await bitcoinApi.$getBlockHeader(req.params.hash);
|
||||
res.setHeader('content-type', 'text/plain');
|
||||
res.send(blockHeader);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get block header');
|
||||
}
|
||||
}
|
||||
|
||||
private async getBlockAuditSummary(req: Request, res: Response) {
|
||||
if (!BLOCK_HASH_REGEX.test(req.params.hash)) {
|
||||
handleError(req, res, 501, `Invalid block hash`);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const auditSummary = await blocks.$getBlockAuditSummary(req.params.hash);
|
||||
if (auditSummary) {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 3600 * 24 * 30).toUTCString());
|
||||
res.json(auditSummary);
|
||||
} else {
|
||||
handleError(req, res, 404, `audit not available`);
|
||||
handleError(req, res, 404, `Audit not available`);
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get block audit summary');
|
||||
}
|
||||
}
|
||||
|
||||
private async $getBlockTxAuditSummary(req: Request, res: Response) {
|
||||
if (!BLOCK_HASH_REGEX.test(req.params.hash)) {
|
||||
handleError(req, res, 501, `Invalid block hash`);
|
||||
return;
|
||||
}
|
||||
if (!TXID_REGEX.test(req.params.txid)) {
|
||||
handleError(req, res, 501, `Invalid transaction ID`);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const auditSummary = await blocks.$getBlockTxAuditSummary(req.params.hash, req.params.txid);
|
||||
if (auditSummary) {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 3600 * 24 * 30).toUTCString());
|
||||
res.json(auditSummary);
|
||||
} else {
|
||||
handleError(req, res, 404, `transaction audit not available`);
|
||||
handleError(req, res, 404, `Transaction audit not available`);
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
res.status(500).send(e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get transaction audit summary');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -408,7 +470,7 @@ class BitcoinRoutes {
|
||||
return await this.getLegacyBlocks(req, res);
|
||||
}
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get blocks');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -450,7 +512,7 @@ class BitcoinRoutes {
|
||||
res.json(await blocks.$getBlocksBetweenHeight(from, to));
|
||||
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get blocks');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -485,11 +547,15 @@ class BitcoinRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||
res.json(returnBlocks);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get blocks');
|
||||
}
|
||||
}
|
||||
|
||||
private async getBlockTransactions(req: Request, res: Response) {
|
||||
if (!BLOCK_HASH_REGEX.test(req.params.hash)) {
|
||||
handleError(req, res, 501, `Invalid block hash`);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
loadingIndicators.setProgress('blocktxs-' + req.params.hash, 0);
|
||||
|
||||
@@ -510,7 +576,7 @@ class BitcoinRoutes {
|
||||
res.json(transactions);
|
||||
} catch (e) {
|
||||
loadingIndicators.setProgress('blocktxs-' + req.params.hash, 100);
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get block transactions');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -519,7 +585,7 @@ class BitcoinRoutes {
|
||||
const blockHash = await bitcoinApi.$getBlockHash(parseInt(req.params.height, 10));
|
||||
res.send(blockHash);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get block at height');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -528,16 +594,20 @@ class BitcoinRoutes {
|
||||
handleError(req, res, 405, 'Address lookups cannot be used with bitcoind as backend.');
|
||||
return;
|
||||
}
|
||||
if (!ADDRESS_REGEX.test(req.params.address)) {
|
||||
handleError(req, res, 501, `Invalid address`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const addressData = await bitcoinApi.$getAddress(req.params.address);
|
||||
res.json(addressData);
|
||||
} catch (e) {
|
||||
if (e instanceof Error && e.message && (e.message.indexOf('too long') > 0 || e.message.indexOf('confirmed status') > 0)) {
|
||||
handleError(req, res, 413, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 413, e.message);
|
||||
return;
|
||||
}
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get address');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -546,6 +616,10 @@ class BitcoinRoutes {
|
||||
handleError(req, res, 405, 'Address lookups cannot be used with bitcoind as backend.');
|
||||
return;
|
||||
}
|
||||
if (!ADDRESS_REGEX.test(req.params.address)) {
|
||||
handleError(req, res, 501, `Invalid address`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
let lastTxId: string = '';
|
||||
@@ -556,10 +630,10 @@ class BitcoinRoutes {
|
||||
res.json(transactions);
|
||||
} catch (e) {
|
||||
if (e instanceof Error && e.message && (e.message.indexOf('too long') > 0 || e.message.indexOf('confirmed status') > 0)) {
|
||||
handleError(req, res, 413, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 413, e.message);
|
||||
return;
|
||||
}
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get address transactions');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -575,6 +649,10 @@ class BitcoinRoutes {
|
||||
handleError(req, res, 405, 'Address lookups cannot be used with bitcoind as backend.');
|
||||
return;
|
||||
}
|
||||
if (!SCRIPT_HASH_REGEX.test(req.params.scripthash)) {
|
||||
handleError(req, res, 501, `Invalid scripthash`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// electrum expects scripthashes in little-endian
|
||||
@@ -583,10 +661,10 @@ class BitcoinRoutes {
|
||||
res.json(addressData);
|
||||
} catch (e) {
|
||||
if (e instanceof Error && e.message && (e.message.indexOf('too long') > 0 || e.message.indexOf('confirmed status') > 0)) {
|
||||
handleError(req, res, 413, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 413, e.message);
|
||||
return;
|
||||
}
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get script hash');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -595,6 +673,10 @@ class BitcoinRoutes {
|
||||
handleError(req, res, 405, 'Address lookups cannot be used with bitcoind as backend.');
|
||||
return;
|
||||
}
|
||||
if (!SCRIPT_HASH_REGEX.test(req.params.scripthash)) {
|
||||
handleError(req, res, 501, `Invalid scripthash`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// electrum expects scripthashes in little-endian
|
||||
@@ -607,10 +689,10 @@ class BitcoinRoutes {
|
||||
res.json(transactions);
|
||||
} catch (e) {
|
||||
if (e instanceof Error && e.message && (e.message.indexOf('too long') > 0 || e.message.indexOf('confirmed status') > 0)) {
|
||||
handleError(req, res, 413, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 413, e.message);
|
||||
return;
|
||||
}
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get script hash transactions');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -623,10 +705,10 @@ class BitcoinRoutes {
|
||||
|
||||
private async getAddressPrefix(req: Request, res: Response) {
|
||||
try {
|
||||
const blockHash = await bitcoinApi.$getAddressPrefix(req.params.prefix);
|
||||
res.send(blockHash);
|
||||
const addressPrefix = await bitcoinApi.$getAddressPrefix(req.params.prefix);
|
||||
res.send(addressPrefix);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get address prefix');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -667,7 +749,7 @@ class BitcoinRoutes {
|
||||
res.setHeader('content-type', 'text/plain');
|
||||
res.send(result.toString());
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get height at tip');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -677,39 +759,55 @@ class BitcoinRoutes {
|
||||
res.setHeader('content-type', 'text/plain');
|
||||
res.send(result);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get hash at tip');
|
||||
}
|
||||
}
|
||||
|
||||
private async getRawBlock(req: Request, res: Response) {
|
||||
if (!BLOCK_HASH_REGEX.test(req.params.hash)) {
|
||||
handleError(req, res, 501, `Invalid block hash`);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const result = await bitcoinApi.$getRawBlock(req.params.hash);
|
||||
res.setHeader('content-type', 'application/octet-stream');
|
||||
res.send(result);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get raw block');
|
||||
}
|
||||
}
|
||||
|
||||
private async getTxIdsForBlock(req: Request, res: Response) {
|
||||
if (!BLOCK_HASH_REGEX.test(req.params.hash)) {
|
||||
handleError(req, res, 501, `Invalid block hash`);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const result = await bitcoinApi.$getTxIdsForBlock(req.params.hash);
|
||||
res.json(result);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get txids for block');
|
||||
}
|
||||
}
|
||||
|
||||
private async validateAddress(req: Request, res: Response) {
|
||||
if (!ADDRESS_REGEX.test(req.params.address)) {
|
||||
handleError(req, res, 501, `Invalid address`);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const result = await bitcoinClient.validateAddress(req.params.address);
|
||||
res.json(result);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to validate address');
|
||||
}
|
||||
}
|
||||
|
||||
private async getRbfHistory(req: Request, res: Response) {
|
||||
if (!TXID_REGEX.test(req.params.txId)) {
|
||||
handleError(req, res, 501, `Invalid transaction ID`);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const replacements = rbfCache.getRbfTree(req.params.txId) || null;
|
||||
const replaces = rbfCache.getReplaces(req.params.txId) || null;
|
||||
@@ -718,7 +816,7 @@ class BitcoinRoutes {
|
||||
replaces
|
||||
});
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get rbf history');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -727,7 +825,7 @@ class BitcoinRoutes {
|
||||
const result = rbfCache.getRbfTrees(false);
|
||||
res.json(result);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get rbf trees');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -736,11 +834,15 @@ class BitcoinRoutes {
|
||||
const result = rbfCache.getRbfTrees(true);
|
||||
res.json(result);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get full rbf replacements');
|
||||
}
|
||||
}
|
||||
|
||||
private async getCachedTx(req: Request, res: Response) {
|
||||
if (!TXID_REGEX.test(req.params.txId)) {
|
||||
handleError(req, res, 501, `Invalid transaction ID`);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const result = rbfCache.getTx(req.params.txId);
|
||||
if (result) {
|
||||
@@ -749,16 +851,20 @@ class BitcoinRoutes {
|
||||
res.status(204).send();
|
||||
}
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get cached tx');
|
||||
}
|
||||
}
|
||||
|
||||
private async getTransactionOutspends(req: Request, res: Response) {
|
||||
if (!TXID_REGEX.test(req.params.txId)) {
|
||||
handleError(req, res, 501, `Invalid transaction ID`);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const result = await bitcoinApi.$getOutspends(req.params.txId);
|
||||
res.json(result);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get transaction outspends');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -771,7 +877,7 @@ class BitcoinRoutes {
|
||||
handleError(req, res, 503, `Service Temporarily Unavailable`);
|
||||
}
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get difficulty change');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -782,8 +888,8 @@ class BitcoinRoutes {
|
||||
const txIdResult = await bitcoinApi.$sendRawTransaction(rawTx);
|
||||
res.send(txIdResult);
|
||||
} catch (e: any) {
|
||||
handleError(req, res, 400, e.message && e.code ? 'sendrawtransaction RPC error: ' + JSON.stringify({ code: e.code, message: e.message })
|
||||
: (e.message || 'Error'));
|
||||
handleError(req, res, 400, (e.message && e.code) ? 'sendrawtransaction RPC error: ' + JSON.stringify({ code: e.code })
|
||||
: 'Failed to send raw transaction');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -794,8 +900,8 @@ class BitcoinRoutes {
|
||||
const txIdResult = await bitcoinClient.sendRawTransaction(txHex);
|
||||
res.send(txIdResult);
|
||||
} catch (e: any) {
|
||||
handleError(req, res, 400, e.message && e.code ? 'sendrawtransaction RPC error: ' + JSON.stringify({ code: e.code, message: e.message })
|
||||
: (e.message || 'Error'));
|
||||
handleError(req, res, 400, (e.message && e.code) ? 'sendrawtransaction RPC error: ' + JSON.stringify({ code: e.code })
|
||||
: 'Failed to send raw transaction');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -806,8 +912,8 @@ class BitcoinRoutes {
|
||||
const result = await bitcoinApi.$testMempoolAccept(rawTxs, maxfeerate);
|
||||
res.send(result);
|
||||
} catch (e: any) {
|
||||
handleError(req, res, 400, e.message && e.code ? 'testmempoolaccept RPC error: ' + JSON.stringify({ code: e.code, message: e.message })
|
||||
: (e.message || 'Error'));
|
||||
handleError(req, res, 400, (e.message && e.code) ? 'testmempoolaccept RPC error: ' + JSON.stringify({ code: e.code })
|
||||
: 'Failed to test transactions');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -819,8 +925,8 @@ class BitcoinRoutes {
|
||||
const result = await bitcoinClient.submitPackage(rawTxs, maxfeerate ?? undefined, maxburnamount ?? undefined);
|
||||
res.send(result);
|
||||
} catch (e: any) {
|
||||
handleError(req, res, 400, e.message && e.code ? 'submitpackage RPC error: ' + JSON.stringify({ code: e.code, message: e.message })
|
||||
: (e.message || 'Error'));
|
||||
handleError(req, res, 400, (e.message && e.code) ? 'submitpackage RPC error: ' + JSON.stringify({ code: e.code })
|
||||
: 'Failed to submit package');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@ import { Application, Request, Response } from 'express';
|
||||
import channelsApi from './channels.api';
|
||||
import { handleError } from '../../utils/api';
|
||||
|
||||
const TXID_REGEX = /^[a-f0-9]{64}$/i;
|
||||
|
||||
class ChannelsRoutes {
|
||||
constructor() { }
|
||||
|
||||
@@ -23,7 +25,7 @@ class ChannelsRoutes {
|
||||
const channels = await channelsApi.$searchChannelsById(req.params.search);
|
||||
res.json(channels);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to search channels by id');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +41,7 @@ class ChannelsRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||
res.json(channel);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get channel');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +72,7 @@ class ChannelsRoutes {
|
||||
res.header('X-Total-Count', channelsCount.toString());
|
||||
res.json(channels);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get channels for node');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +85,10 @@ class ChannelsRoutes {
|
||||
const txIds: string[] = [];
|
||||
for (const _txId in req.query.txId) {
|
||||
if (typeof req.query.txId[_txId] === 'string') {
|
||||
txIds.push(req.query.txId[_txId].toString());
|
||||
const txid = req.query.txId[_txId].toString();
|
||||
if (TXID_REGEX.test(txid)) {
|
||||
txIds.push(txid);
|
||||
}
|
||||
}
|
||||
}
|
||||
const channels = await channelsApi.$getChannelsByTransactionId(txIds);
|
||||
@@ -108,7 +113,7 @@ class ChannelsRoutes {
|
||||
|
||||
res.json(result);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get channels by transaction ids');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,7 +125,7 @@ class ChannelsRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||
res.json(channels);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get penalty closed channels');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,7 +138,7 @@ class ChannelsRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||
res.json(channels);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get channel geodata');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ class GeneralLightningRoutes {
|
||||
channels: channels,
|
||||
});
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to search for nodes and channels');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ class GeneralLightningRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||
res.json(statistics);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get lightning statistics');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ class GeneralLightningRoutes {
|
||||
const statistics = await statisticsApi.$getLatestStatistics();
|
||||
res.json(statistics);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get lightning statistics');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ class NodesRoutes {
|
||||
const nodes = await nodesApi.$searchNodeByPublicKeyOrAlias(req.params.search);
|
||||
res.json(nodes);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to search for node');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,7 +188,7 @@ class NodesRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||
res.json(nodes);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get node group');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,7 +204,7 @@ class NodesRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||
res.json(node);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get node');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,7 +216,7 @@ class NodesRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||
res.json(statistics);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get historical node stats');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,7 +232,7 @@ class NodesRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||
res.json(node);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get fee histogram');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,7 +248,7 @@ class NodesRoutes {
|
||||
topByChannels: topChannelsNodes,
|
||||
});
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get nodes ranking');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,7 +260,7 @@ class NodesRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||
res.json(topCapacityNodes);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get top nodes by capacity');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,7 +272,7 @@ class NodesRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||
res.json(topCapacityNodes);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get top nodes by channels');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,7 +284,7 @@ class NodesRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||
res.json(topCapacityNodes);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get oldest nodes');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,7 +296,7 @@ class NodesRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 600).toUTCString());
|
||||
res.json(nodesPerAs);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get ISP ranking');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -308,7 +308,7 @@ class NodesRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 600).toUTCString());
|
||||
res.json(worldNodes);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get world nodes');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,7 +336,7 @@ class NodesRoutes {
|
||||
nodes: nodes,
|
||||
});
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get nodes per country');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,7 +363,7 @@ class NodesRoutes {
|
||||
nodes: nodes,
|
||||
});
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get nodes per ISP');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,7 +375,7 @@ class NodesRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 600).toUTCString());
|
||||
res.json(nodesPerAs);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get nodes per country');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ class LiquidRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60 * 60).toUTCString());
|
||||
res.json(pegs);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get pegs by month');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ class LiquidRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60 * 60).toUTCString());
|
||||
res.json(reserves);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get reserves by month');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ class LiquidRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString());
|
||||
res.json(currentSupply);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get pegs');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ class LiquidRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString());
|
||||
res.json(currentReserves);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get reserves');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ class LiquidRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString());
|
||||
res.json(auditStatus);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get federation audit status');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ class LiquidRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString());
|
||||
res.json(federationAddresses);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get federation addresses');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,7 +155,7 @@ class LiquidRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString());
|
||||
res.json(federationAddresses);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get federation addresses');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ class LiquidRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString());
|
||||
res.json(federationUtxos);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get federation utxos');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,7 +179,7 @@ class LiquidRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString());
|
||||
res.json(expiredUtxos);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get expired utxos');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,7 +191,7 @@ class LiquidRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString());
|
||||
res.json(federationUtxos);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get federation utxos number');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,7 +203,7 @@ class LiquidRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString());
|
||||
res.json(emergencySpentUtxos);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get emergency spent utxos');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,7 +215,7 @@ class LiquidRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString());
|
||||
res.json(emergencySpentUtxos);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get emergency spent utxos stats');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,7 +227,7 @@ class LiquidRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString());
|
||||
res.json(recentPegs);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get pegs list');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,7 +239,7 @@ class LiquidRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString());
|
||||
res.json(pegsVolume);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get pegs volume daily');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,7 +251,7 @@ class LiquidRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString());
|
||||
res.json(pegsCount);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get pegs count');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ class MiningRoutes {
|
||||
}
|
||||
res.status(200).send(response);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get historical prices');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ class MiningRoutes {
|
||||
if (e instanceof Error && e.message.indexOf('This mining pool does not exist') > -1) {
|
||||
handleError(req, res, 404, e.message);
|
||||
} else {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get pool');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -106,7 +106,7 @@ class MiningRoutes {
|
||||
if (e instanceof Error && e.message.indexOf('This mining pool does not exist') > -1) {
|
||||
handleError(req, res, 404, e.message);
|
||||
} else {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get blocks for pool');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -130,7 +130,7 @@ class MiningRoutes {
|
||||
res.json(pools);
|
||||
}
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get pools');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@ class MiningRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||
res.json(stats);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get pools');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,7 +158,7 @@ class MiningRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 300).toUTCString());
|
||||
res.json(hashrates);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get pools historical hashrate');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ class MiningRoutes {
|
||||
if (e instanceof Error && e.message.indexOf('This mining pool does not exist') > -1) {
|
||||
handleError(req, res, 404, e.message);
|
||||
} else {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get pool historical hashrate');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -204,7 +204,7 @@ class MiningRoutes {
|
||||
currentDifficulty: currentDifficulty,
|
||||
});
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get historical hashrate');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,7 +218,7 @@ class MiningRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||
res.json(blockFees);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get historical block fees');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,7 +236,7 @@ class MiningRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||
res.json(blockFees);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get historical block fees');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,7 +250,7 @@ class MiningRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||
res.json(blockRewards);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get historical block rewards');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,7 +264,7 @@ class MiningRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||
res.json(blockFeeRates);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get historical block fee rates');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,7 +282,7 @@ class MiningRoutes {
|
||||
weights: blockWeights
|
||||
});
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get historical block size and weight');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,7 +294,7 @@ class MiningRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 300).toUTCString());
|
||||
res.json(difficulty.map(adj => [adj.time, adj.height, adj.difficulty, adj.adjustment]));
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get historical difficulty adjustments');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,7 +304,7 @@ class MiningRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||
res.json(response);
|
||||
} catch (e) {
|
||||
res.status(500).end();
|
||||
handleError(req, res, 500, 'Failed to get reward stats');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,7 +318,7 @@ class MiningRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||
res.json(blocksHealth.map(health => [health.time, health.height, health.match_rate]));
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get historical blocks health');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,7 +336,7 @@ class MiningRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 3600 * 24).toUTCString());
|
||||
res.json(audit);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get block audit');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,7 +359,7 @@ class MiningRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 300).toUTCString());
|
||||
res.json(result);
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get height from timestamp');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -372,7 +372,7 @@ class MiningRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||
res.json(await BlocksAuditsRepository.$getBlockAuditScores(height, height - 15));
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get block audit scores');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -385,7 +385,7 @@ class MiningRoutes {
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 3600 * 24).toUTCString());
|
||||
res.json(audit || 'null');
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get block audit score');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -400,7 +400,7 @@ class MiningRoutes {
|
||||
}
|
||||
res.status(200).send(await AccelerationRepository.$getAccelerationInfo(req.params.slug));
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get accelerations by pool');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -416,7 +416,7 @@ class MiningRoutes {
|
||||
const height = req.params.height === undefined ? undefined : parseInt(req.params.height, 10);
|
||||
res.status(200).send(await AccelerationRepository.$getAccelerationInfo(null, height));
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get accelerations by height');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -431,7 +431,7 @@ class MiningRoutes {
|
||||
}
|
||||
res.status(200).send(await AccelerationRepository.$getAccelerationInfo(null, null, req.params.interval));
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get recent accelerations');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -446,7 +446,7 @@ class MiningRoutes {
|
||||
}
|
||||
res.status(200).send(await AccelerationRepository.$getAccelerationTotals(<string>req.query.pool, <string>req.query.interval));
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get acceleration totals');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -461,7 +461,7 @@ class MiningRoutes {
|
||||
}
|
||||
res.status(200).send(Object.values(accelerationApi.getAccelerations() || {}));
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get active accelerations');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -473,7 +473,7 @@ class MiningRoutes {
|
||||
accelerationApi.accelerationRequested(req.params.txid);
|
||||
res.status(200).send();
|
||||
} catch (e) {
|
||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to request acceleration');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Application, Request, Response } from 'express';
|
||||
import config from '../../config';
|
||||
import WalletApi from './wallets';
|
||||
import { handleError } from '../../utils/api';
|
||||
|
||||
class ServicesRoutes {
|
||||
public initRoutes(app: Application): void {
|
||||
@@ -18,7 +19,7 @@ class ServicesRoutes {
|
||||
const wallet = await WalletApi.getWallet(walletId);
|
||||
res.status(200).send(wallet);
|
||||
} catch (e) {
|
||||
res.status(500).send(e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get wallet');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Application, Request, Response } from 'express';
|
||||
import config from '../../config';
|
||||
import statisticsApi from './statistics-api';
|
||||
|
||||
import { handleError } from '../../utils/api';
|
||||
class StatisticsRoutes {
|
||||
public initRoutes(app: Application) {
|
||||
app
|
||||
@@ -65,7 +65,7 @@ class StatisticsRoutes {
|
||||
}
|
||||
res.json(result);
|
||||
} catch (e) {
|
||||
res.status(500).send(e instanceof Error ? e.message : e);
|
||||
handleError(req, res, 500, 'Failed to get statistics');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,7 +324,9 @@ class Server {
|
||||
|
||||
setUpHttpApiRoutes(): void {
|
||||
bitcoinRoutes.initRoutes(this.app);
|
||||
bitcoinCoreRoutes.initRoutes(this.app);
|
||||
if (config.MEMPOOL.OFFICIAL) {
|
||||
bitcoinCoreRoutes.initRoutes(this.app);
|
||||
}
|
||||
pricesRoutes.initRoutes(this.app);
|
||||
if (config.STATISTICS.ENABLED && config.DATABASE.ENABLED && config.MEMPOOL.ENABLED) {
|
||||
statisticsRoutes.initRoutes(this.app);
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
{
|
||||
"theme": "wiz",
|
||||
"enterprise": "river",
|
||||
"branding": {
|
||||
"name": "river",
|
||||
"title": "river",
|
||||
"site_id": 22,
|
||||
"header_img": "/resources/riverlogo.svg",
|
||||
"footer_img": "/resources/riverlogo.svg"
|
||||
},
|
||||
"dashboard": {
|
||||
"widgets": [
|
||||
{
|
||||
"component": "fees",
|
||||
"mobileOrder": 4
|
||||
},
|
||||
{
|
||||
"component": "walletBalance",
|
||||
"mobileOrder": 1,
|
||||
"props": {
|
||||
"wallet": "RIVER"
|
||||
}
|
||||
},
|
||||
{
|
||||
"component": "twitter",
|
||||
"mobileOrder": 5,
|
||||
"props": {
|
||||
"handle": "River"
|
||||
}
|
||||
},
|
||||
{
|
||||
"component": "wallet",
|
||||
"mobileOrder": 2,
|
||||
"props": {
|
||||
"wallet": "RIVER",
|
||||
"period": "all"
|
||||
}
|
||||
},
|
||||
{
|
||||
"component": "blocks"
|
||||
},
|
||||
{
|
||||
"component": "walletTransactions",
|
||||
"mobileOrder": 3,
|
||||
"props": {
|
||||
"wallet": "RIVER"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,12 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"component": "blocks"
|
||||
"component": "simpleproof",
|
||||
"mobileOrder": 6,
|
||||
"props": {
|
||||
"label": "Executive Decrees",
|
||||
"key": "el_salvador_decretos"
|
||||
}
|
||||
},
|
||||
{
|
||||
"component": "addressTransactions",
|
||||
|
||||
331
frontend/package-lock.json
generated
@@ -23,9 +23,9 @@
|
||||
"@angular/router": "^17.3.1",
|
||||
"@angular/ssr": "^17.3.1",
|
||||
"@fortawesome/angular-fontawesome": "~0.14.1",
|
||||
"@fortawesome/fontawesome-common-types": "~6.6.0",
|
||||
"@fortawesome/fontawesome-svg-core": "~6.6.0",
|
||||
"@fortawesome/free-solid-svg-icons": "~6.6.0",
|
||||
"@fortawesome/fontawesome-common-types": "~6.7.2",
|
||||
"@fortawesome/fontawesome-svg-core": "~6.7.2",
|
||||
"@fortawesome/free-solid-svg-icons": "~6.7.2",
|
||||
"@mempool/mempool.js": "2.3.0",
|
||||
"@ng-bootstrap/ng-bootstrap": "^16.0.0",
|
||||
"@types/qrcode": "~1.5.0",
|
||||
@@ -35,7 +35,6 @@
|
||||
"domino": "^2.1.6",
|
||||
"echarts": "~5.5.0",
|
||||
"esbuild": "^0.24.0",
|
||||
"lightweight-charts": "~3.8.0",
|
||||
"ngx-echarts": "~17.2.0",
|
||||
"ngx-infinite-scroll": "^17.0.0",
|
||||
"qrcode": "1.5.1",
|
||||
@@ -62,7 +61,7 @@
|
||||
"optionalDependencies": {
|
||||
"@cypress/schematic": "^2.5.0",
|
||||
"@types/cypress": "^1.1.3",
|
||||
"cypress": "^13.15.0",
|
||||
"cypress": "^13.17.0",
|
||||
"cypress-fail-on-console-error": "~5.1.0",
|
||||
"cypress-wait-until": "^2.0.1",
|
||||
"mock-socket": "~9.3.1",
|
||||
@@ -3113,9 +3112,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@cypress/request": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.5.tgz",
|
||||
"integrity": "sha512-v+XHd9XmWbufxF1/bTaVm2yhbxY+TB4YtWRqF2zaXBlDNMkls34KiATz0AVDLavL3iB6bQk9/7n3oY1EoLSWGA==",
|
||||
"version": "3.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.7.tgz",
|
||||
"integrity": "sha512-LzxlLEMbBOPYB85uXrDqvD4MgcenjRBLIns3zyhx7vTPj/0u2eQhzXvPiGcaJrV38Q9dbkExWp6cOHPJ+EtFYg==",
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"aws-sign2": "~0.7.0",
|
||||
@@ -3131,9 +3131,9 @@
|
||||
"json-stringify-safe": "~5.0.1",
|
||||
"mime-types": "~2.1.19",
|
||||
"performance-now": "^2.1.0",
|
||||
"qs": "6.13.0",
|
||||
"qs": "6.13.1",
|
||||
"safe-buffer": "^5.1.2",
|
||||
"tough-cookie": "^4.1.3",
|
||||
"tough-cookie": "^5.0.0",
|
||||
"tunnel-agent": "^0.6.0",
|
||||
"uuid": "^8.3.2"
|
||||
},
|
||||
@@ -3141,6 +3141,22 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/@cypress/request/node_modules/qs": {
|
||||
"version": "6.13.1",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.1.tgz",
|
||||
"integrity": "sha512-EJPeIn0CYrGu+hli1xilKAPXODtJ12T0sP63Ijx2/khC2JtuaN3JyNIpvmnkmaEtha9ocbG4A4cMcr+TvqvwQg==",
|
||||
"license": "BSD-3-Clause",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"side-channel": "^1.0.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/@cypress/schematic": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@cypress/schematic/-/schematic-2.5.0.tgz",
|
||||
@@ -3674,30 +3690,33 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@fortawesome/fontawesome-common-types": {
|
||||
"version": "6.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.6.0.tgz",
|
||||
"integrity": "sha512-xyX0X9mc0kyz9plIyryrRbl7ngsA9jz77mCZJsUkLl+ZKs0KWObgaEBoSgQiYWAsSmjz/yjl0F++Got0Mdp4Rw==",
|
||||
"version": "6.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.7.2.tgz",
|
||||
"integrity": "sha512-Zs+YeHUC5fkt7Mg1l6XTniei3k4bwG/yo3iFUtZWd/pMx9g3fdvkSK9E0FOC+++phXOka78uJcYb8JaFkW52Xg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@fortawesome/fontawesome-svg-core": {
|
||||
"version": "6.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.6.0.tgz",
|
||||
"integrity": "sha512-KHwPkCk6oRT4HADE7smhfsKudt9N/9lm6EJ5BVg0tD1yPA5hht837fB87F8pn15D8JfTqQOjhKTktwmLMiD7Kg==",
|
||||
"version": "6.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.7.2.tgz",
|
||||
"integrity": "sha512-yxtOBWDrdi5DD5o1pmVdq3WMCvnobT0LU6R8RyyVXPvFRd2o79/0NCuQoCjNTeZz9EzA9xS3JxNWfv54RIHFEA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-common-types": "6.6.0"
|
||||
"@fortawesome/fontawesome-common-types": "6.7.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@fortawesome/free-solid-svg-icons": {
|
||||
"version": "6.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.6.0.tgz",
|
||||
"integrity": "sha512-IYv/2skhEDFc2WGUcqvFJkeK39Q+HyPf5GHUrT/l2pKbtgEIv1al1TKd6qStR5OIwQdN1GZP54ci3y4mroJWjA==",
|
||||
"version": "6.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.7.2.tgz",
|
||||
"integrity": "sha512-GsBrnOzU8uj0LECDfD5zomZJIjrPhIlWU82AHwa2s40FKH+kcxQaBvBo3Z4TxyZHIyX8XTDxsyA33/Vx9eFuQA==",
|
||||
"license": "(CC-BY-4.0 AND MIT)",
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-common-types": "6.6.0"
|
||||
"@fortawesome/fontawesome-common-types": "6.7.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
@@ -5673,6 +5692,7 @@
|
||||
"version": "0.2.6",
|
||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
|
||||
"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"safer-buffer": "~2.1.0"
|
||||
@@ -5707,6 +5727,7 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
|
||||
"integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
@@ -5827,6 +5848,7 @@
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
|
||||
"integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==",
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": "*"
|
||||
@@ -5836,6 +5858,7 @@
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz",
|
||||
"integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/axios": {
|
||||
@@ -5993,6 +6016,7 @@
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
|
||||
"integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
|
||||
"license": "BSD-3-Clause",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tweetnacl": "^0.14.3"
|
||||
@@ -7068,6 +7092,7 @@
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
|
||||
"integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==",
|
||||
"license": "Apache-2.0",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/chai": {
|
||||
@@ -7170,15 +7195,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ci-info": {
|
||||
"version": "3.8.0",
|
||||
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz",
|
||||
"integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==",
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.1.0.tgz",
|
||||
"integrity": "sha512-HutrvTNsF48wnxkzERIXOe5/mlcfFcbfCmwcg6CJnizbSue78AbDt+1cgl26zwn61WFxhcPykPfZrbqjGmBb4A==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/sibiraj-s"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@@ -7953,13 +7979,14 @@
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/cypress": {
|
||||
"version": "13.15.0",
|
||||
"resolved": "https://registry.npmjs.org/cypress/-/cypress-13.15.0.tgz",
|
||||
"integrity": "sha512-53aO7PwOfi604qzOkCSzNlWquCynLlKE/rmmpSPcziRH6LNfaDUAklQT6WJIsD8ywxlIy+uVZsnTMCCQVd2kTw==",
|
||||
"version": "13.17.0",
|
||||
"resolved": "https://registry.npmjs.org/cypress/-/cypress-13.17.0.tgz",
|
||||
"integrity": "sha512-5xWkaPurwkIljojFidhw8lFScyxhtiFHl/i/3zov+1Z5CmY4t9tjIdvSXfu82Y3w7wt0uR9KkucbhkVvJZLQSA==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@cypress/request": "^3.0.4",
|
||||
"@cypress/request": "^3.0.6",
|
||||
"@cypress/xvfb": "^1.2.4",
|
||||
"@types/sinonjs__fake-timers": "8.1.1",
|
||||
"@types/sizzle": "^2.3.2",
|
||||
@@ -7970,6 +7997,7 @@
|
||||
"cachedir": "^2.3.0",
|
||||
"chalk": "^4.1.0",
|
||||
"check-more-types": "^2.24.0",
|
||||
"ci-info": "^4.0.0",
|
||||
"cli-cursor": "^3.1.0",
|
||||
"cli-table3": "~0.6.1",
|
||||
"commander": "^6.2.1",
|
||||
@@ -7984,7 +8012,6 @@
|
||||
"figures": "^3.2.0",
|
||||
"fs-extra": "^9.1.0",
|
||||
"getos": "^3.2.1",
|
||||
"is-ci": "^3.0.1",
|
||||
"is-installed-globally": "~0.4.0",
|
||||
"lazy-ass": "^1.6.0",
|
||||
"listr2": "^3.8.3",
|
||||
@@ -7999,6 +8026,7 @@
|
||||
"semver": "^7.5.3",
|
||||
"supports-color": "^8.1.1",
|
||||
"tmp": "~0.2.3",
|
||||
"tree-kill": "1.2.2",
|
||||
"untildify": "^4.0.0",
|
||||
"yauzl": "^2.10.0"
|
||||
},
|
||||
@@ -8201,6 +8229,7 @@
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
|
||||
"integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"assert-plus": "^1.0.0"
|
||||
@@ -8687,6 +8716,7 @@
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
|
||||
"integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"jsbn": "~0.1.0",
|
||||
@@ -9905,6 +9935,7 @@
|
||||
"engines": [
|
||||
"node >=0.6.0"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/falafel": {
|
||||
@@ -9921,11 +9952,6 @@
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fancy-canvas": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/fancy-canvas/-/fancy-canvas-0.2.2.tgz",
|
||||
"integrity": "sha512-50qi8xA0QkHbjmb8h7XQ6k2fvD7y/yMfiUw9YTarJ7rWrq6o5/3CCXPouYk+XSLASvvxtjyiQLRBFt3qkE3oyA=="
|
||||
},
|
||||
"node_modules/fast-deep-equal": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
@@ -10193,6 +10219,7 @@
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
|
||||
"integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==",
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": "*"
|
||||
@@ -10400,6 +10427,7 @@
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
|
||||
"integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"assert-plus": "^1.0.0"
|
||||
@@ -10854,6 +10882,7 @@
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.4.0.tgz",
|
||||
"integrity": "sha512-G5akfn7eKbpDN+8nPS/cb57YeA1jLTVxjpCj7tmm3QKPdyDy7T+qSC40e9ptydSWvkwjSXw1VbkpyEm39ukeAg==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"assert-plus": "^1.0.0",
|
||||
@@ -11220,18 +11249,6 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-ci": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz",
|
||||
"integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"ci-info": "^3.2.0"
|
||||
},
|
||||
"bin": {
|
||||
"is-ci": "bin.js"
|
||||
}
|
||||
},
|
||||
"node_modules/is-core-module": {
|
||||
"version": "2.13.1",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz",
|
||||
@@ -11481,6 +11498,7 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
|
||||
"integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/is-unicode-supported": {
|
||||
@@ -11545,6 +11563,7 @@
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
|
||||
"integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/istanbul-lib-coverage": {
|
||||
@@ -11678,6 +11697,7 @@
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
|
||||
"integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/jsesc": {
|
||||
@@ -11706,6 +11726,7 @@
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
|
||||
"integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
|
||||
"license": "(AFL-2.1 OR BSD-3-Clause)",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/json-schema-traverse": {
|
||||
@@ -11723,6 +11744,7 @@
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
|
||||
"integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
|
||||
"license": "ISC",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/json5": {
|
||||
@@ -11783,6 +11805,7 @@
|
||||
"engines": [
|
||||
"node >=0.6.0"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"assert-plus": "1.0.0",
|
||||
@@ -12106,14 +12129,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/lightweight-charts": {
|
||||
"version": "3.8.0",
|
||||
"resolved": "https://registry.npmjs.org/lightweight-charts/-/lightweight-charts-3.8.0.tgz",
|
||||
"integrity": "sha512-7yFGnYuE1RjRJG9RwUTBz5wvF1QtjBOSW4FFlikr8Dh+/TDNt4ci+HsWSYmStgQUpawpvkCJ3j5/W25GppGj9Q==",
|
||||
"dependencies": {
|
||||
"fancy-canvas": "0.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/limiter": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz",
|
||||
@@ -14110,6 +14125,7 @@
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
|
||||
"integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
@@ -14540,12 +14556,6 @@
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/psl": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
|
||||
"integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/public-encrypt": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz",
|
||||
@@ -14661,12 +14671,6 @@
|
||||
"node": ">=0.4.x"
|
||||
}
|
||||
},
|
||||
"node_modules/querystringify": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
|
||||
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/queue-microtask": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||
@@ -16028,6 +16032,7 @@
|
||||
"version": "1.18.0",
|
||||
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz",
|
||||
"integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"asn1": "~0.2.3",
|
||||
@@ -16577,6 +16582,26 @@
|
||||
"readable-stream": "3"
|
||||
}
|
||||
},
|
||||
"node_modules/tldts": {
|
||||
"version": "6.1.70",
|
||||
"resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.70.tgz",
|
||||
"integrity": "sha512-/W1YVgYVJd9ZDjey5NXadNh0mJXkiUMUue9Zebd0vpdo1sU+H4zFFTaJ1RKD4N6KFoHfcXy6l+Vu7bh+bdWCzA==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tldts-core": "^6.1.70"
|
||||
},
|
||||
"bin": {
|
||||
"tldts": "bin/cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/tldts-core": {
|
||||
"version": "6.1.70",
|
||||
"resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.70.tgz",
|
||||
"integrity": "sha512-RNnIXDB1FD4T9cpQRErEqw6ZpjLlGdMOitdV+0xtbsnwr4YFka1zpc7D4KD+aAn8oSG5JyFrdasZTE04qDE9Yg==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/tlite": {
|
||||
"version": "0.1.9",
|
||||
"resolved": "https://registry.npmjs.org/tlite/-/tlite-0.1.9.tgz",
|
||||
@@ -16621,27 +16646,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tough-cookie": {
|
||||
"version": "4.1.4",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz",
|
||||
"integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==",
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.0.0.tgz",
|
||||
"integrity": "sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==",
|
||||
"license": "BSD-3-Clause",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"psl": "^1.1.33",
|
||||
"punycode": "^2.1.1",
|
||||
"universalify": "^0.2.0",
|
||||
"url-parse": "^1.5.3"
|
||||
"tldts": "^6.1.32"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/tough-cookie/node_modules/universalify": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
|
||||
"integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">= 4.0.0"
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/transform-ast": {
|
||||
@@ -16810,6 +16824,7 @@
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
||||
"integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"safe-buffer": "^5.0.1"
|
||||
@@ -16822,6 +16837,7 @@
|
||||
"version": "0.14.5",
|
||||
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
|
||||
"integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==",
|
||||
"license": "Unlicense",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/type": {
|
||||
@@ -17130,16 +17146,6 @@
|
||||
"querystring": "0.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/url-parse": {
|
||||
"version": "1.5.10",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
|
||||
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"querystringify": "^2.1.1",
|
||||
"requires-port": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/url/node_modules/punycode": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
|
||||
@@ -17207,6 +17213,7 @@
|
||||
"engines": [
|
||||
"node >=0.6.0"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"assert-plus": "^1.0.0",
|
||||
@@ -20348,9 +20355,9 @@
|
||||
}
|
||||
},
|
||||
"@cypress/request": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.5.tgz",
|
||||
"integrity": "sha512-v+XHd9XmWbufxF1/bTaVm2yhbxY+TB4YtWRqF2zaXBlDNMkls34KiATz0AVDLavL3iB6bQk9/7n3oY1EoLSWGA==",
|
||||
"version": "3.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.7.tgz",
|
||||
"integrity": "sha512-LzxlLEMbBOPYB85uXrDqvD4MgcenjRBLIns3zyhx7vTPj/0u2eQhzXvPiGcaJrV38Q9dbkExWp6cOHPJ+EtFYg==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"aws-sign2": "~0.7.0",
|
||||
@@ -20366,11 +20373,22 @@
|
||||
"json-stringify-safe": "~5.0.1",
|
||||
"mime-types": "~2.1.19",
|
||||
"performance-now": "^2.1.0",
|
||||
"qs": "6.13.0",
|
||||
"qs": "6.13.1",
|
||||
"safe-buffer": "^5.1.2",
|
||||
"tough-cookie": "^4.1.3",
|
||||
"tough-cookie": "^5.0.0",
|
||||
"tunnel-agent": "^0.6.0",
|
||||
"uuid": "^8.3.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"qs": {
|
||||
"version": "6.13.1",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.1.tgz",
|
||||
"integrity": "sha512-EJPeIn0CYrGu+hli1xilKAPXODtJ12T0sP63Ijx2/khC2JtuaN3JyNIpvmnkmaEtha9ocbG4A4cMcr+TvqvwQg==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"side-channel": "^1.0.6"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@cypress/schematic": {
|
||||
@@ -20649,24 +20667,24 @@
|
||||
}
|
||||
},
|
||||
"@fortawesome/fontawesome-common-types": {
|
||||
"version": "6.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.6.0.tgz",
|
||||
"integrity": "sha512-xyX0X9mc0kyz9plIyryrRbl7ngsA9jz77mCZJsUkLl+ZKs0KWObgaEBoSgQiYWAsSmjz/yjl0F++Got0Mdp4Rw=="
|
||||
"version": "6.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.7.2.tgz",
|
||||
"integrity": "sha512-Zs+YeHUC5fkt7Mg1l6XTniei3k4bwG/yo3iFUtZWd/pMx9g3fdvkSK9E0FOC+++phXOka78uJcYb8JaFkW52Xg=="
|
||||
},
|
||||
"@fortawesome/fontawesome-svg-core": {
|
||||
"version": "6.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.6.0.tgz",
|
||||
"integrity": "sha512-KHwPkCk6oRT4HADE7smhfsKudt9N/9lm6EJ5BVg0tD1yPA5hht837fB87F8pn15D8JfTqQOjhKTktwmLMiD7Kg==",
|
||||
"version": "6.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.7.2.tgz",
|
||||
"integrity": "sha512-yxtOBWDrdi5DD5o1pmVdq3WMCvnobT0LU6R8RyyVXPvFRd2o79/0NCuQoCjNTeZz9EzA9xS3JxNWfv54RIHFEA==",
|
||||
"requires": {
|
||||
"@fortawesome/fontawesome-common-types": "6.6.0"
|
||||
"@fortawesome/fontawesome-common-types": "6.7.2"
|
||||
}
|
||||
},
|
||||
"@fortawesome/free-solid-svg-icons": {
|
||||
"version": "6.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.6.0.tgz",
|
||||
"integrity": "sha512-IYv/2skhEDFc2WGUcqvFJkeK39Q+HyPf5GHUrT/l2pKbtgEIv1al1TKd6qStR5OIwQdN1GZP54ci3y4mroJWjA==",
|
||||
"version": "6.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.7.2.tgz",
|
||||
"integrity": "sha512-GsBrnOzU8uj0LECDfD5zomZJIjrPhIlWU82AHwa2s40FKH+kcxQaBvBo3Z4TxyZHIyX8XTDxsyA33/Vx9eFuQA==",
|
||||
"requires": {
|
||||
"@fortawesome/fontawesome-common-types": "6.6.0"
|
||||
"@fortawesome/fontawesome-common-types": "6.7.2"
|
||||
}
|
||||
},
|
||||
"@goto-bus-stop/common-shake": {
|
||||
@@ -23298,9 +23316,9 @@
|
||||
"integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg=="
|
||||
},
|
||||
"ci-info": {
|
||||
"version": "3.8.0",
|
||||
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz",
|
||||
"integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==",
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.1.0.tgz",
|
||||
"integrity": "sha512-HutrvTNsF48wnxkzERIXOe5/mlcfFcbfCmwcg6CJnizbSue78AbDt+1cgl26zwn61WFxhcPykPfZrbqjGmBb4A==",
|
||||
"optional": true
|
||||
},
|
||||
"cipher-base": {
|
||||
@@ -23896,12 +23914,12 @@
|
||||
"peer": true
|
||||
},
|
||||
"cypress": {
|
||||
"version": "13.15.0",
|
||||
"resolved": "https://registry.npmjs.org/cypress/-/cypress-13.15.0.tgz",
|
||||
"integrity": "sha512-53aO7PwOfi604qzOkCSzNlWquCynLlKE/rmmpSPcziRH6LNfaDUAklQT6WJIsD8ywxlIy+uVZsnTMCCQVd2kTw==",
|
||||
"version": "13.17.0",
|
||||
"resolved": "https://registry.npmjs.org/cypress/-/cypress-13.17.0.tgz",
|
||||
"integrity": "sha512-5xWkaPurwkIljojFidhw8lFScyxhtiFHl/i/3zov+1Z5CmY4t9tjIdvSXfu82Y3w7wt0uR9KkucbhkVvJZLQSA==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"@cypress/request": "^3.0.4",
|
||||
"@cypress/request": "^3.0.6",
|
||||
"@cypress/xvfb": "^1.2.4",
|
||||
"@types/sinonjs__fake-timers": "8.1.1",
|
||||
"@types/sizzle": "^2.3.2",
|
||||
@@ -23912,6 +23930,7 @@
|
||||
"cachedir": "^2.3.0",
|
||||
"chalk": "^4.1.0",
|
||||
"check-more-types": "^2.24.0",
|
||||
"ci-info": "^4.0.0",
|
||||
"cli-cursor": "^3.1.0",
|
||||
"cli-table3": "~0.6.1",
|
||||
"commander": "^6.2.1",
|
||||
@@ -23926,7 +23945,6 @@
|
||||
"figures": "^3.2.0",
|
||||
"fs-extra": "^9.1.0",
|
||||
"getos": "^3.2.1",
|
||||
"is-ci": "^3.0.1",
|
||||
"is-installed-globally": "~0.4.0",
|
||||
"lazy-ass": "^1.6.0",
|
||||
"listr2": "^3.8.3",
|
||||
@@ -23941,6 +23959,7 @@
|
||||
"semver": "^7.5.3",
|
||||
"supports-color": "^8.1.1",
|
||||
"tmp": "~0.2.3",
|
||||
"tree-kill": "1.2.2",
|
||||
"untildify": "^4.0.0",
|
||||
"yauzl": "^2.10.0"
|
||||
},
|
||||
@@ -25433,11 +25452,6 @@
|
||||
"object-keys": "^1.0.6"
|
||||
}
|
||||
},
|
||||
"fancy-canvas": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/fancy-canvas/-/fancy-canvas-0.2.2.tgz",
|
||||
"integrity": "sha512-50qi8xA0QkHbjmb8h7XQ6k2fvD7y/yMfiUw9YTarJ7rWrq6o5/3CCXPouYk+XSLASvvxtjyiQLRBFt3qkE3oyA=="
|
||||
},
|
||||
"fast-deep-equal": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
@@ -26373,15 +26387,6 @@
|
||||
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz",
|
||||
"integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ=="
|
||||
},
|
||||
"is-ci": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz",
|
||||
"integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ci-info": "^3.2.0"
|
||||
}
|
||||
},
|
||||
"is-core-module": {
|
||||
"version": "2.13.1",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz",
|
||||
@@ -27015,14 +27020,6 @@
|
||||
"webpack-sources": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"lightweight-charts": {
|
||||
"version": "3.8.0",
|
||||
"resolved": "https://registry.npmjs.org/lightweight-charts/-/lightweight-charts-3.8.0.tgz",
|
||||
"integrity": "sha512-7yFGnYuE1RjRJG9RwUTBz5wvF1QtjBOSW4FFlikr8Dh+/TDNt4ci+HsWSYmStgQUpawpvkCJ3j5/W25GppGj9Q==",
|
||||
"requires": {
|
||||
"fancy-canvas": "0.2.2"
|
||||
}
|
||||
},
|
||||
"limiter": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz",
|
||||
@@ -28806,12 +28803,6 @@
|
||||
"event-stream": "=3.3.4"
|
||||
}
|
||||
},
|
||||
"psl": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
|
||||
"integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==",
|
||||
"optional": true
|
||||
},
|
||||
"public-encrypt": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz",
|
||||
@@ -28903,12 +28894,6 @@
|
||||
"resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz",
|
||||
"integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM="
|
||||
},
|
||||
"querystringify": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
|
||||
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
|
||||
"optional": true
|
||||
},
|
||||
"queue-microtask": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||
@@ -30373,6 +30358,21 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"tldts": {
|
||||
"version": "6.1.70",
|
||||
"resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.70.tgz",
|
||||
"integrity": "sha512-/W1YVgYVJd9ZDjey5NXadNh0mJXkiUMUue9Zebd0vpdo1sU+H4zFFTaJ1RKD4N6KFoHfcXy6l+Vu7bh+bdWCzA==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"tldts-core": "^6.1.70"
|
||||
}
|
||||
},
|
||||
"tldts-core": {
|
||||
"version": "6.1.70",
|
||||
"resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.70.tgz",
|
||||
"integrity": "sha512-RNnIXDB1FD4T9cpQRErEqw6ZpjLlGdMOitdV+0xtbsnwr4YFka1zpc7D4KD+aAn8oSG5JyFrdasZTE04qDE9Yg==",
|
||||
"optional": true
|
||||
},
|
||||
"tlite": {
|
||||
"version": "0.1.9",
|
||||
"resolved": "https://registry.npmjs.org/tlite/-/tlite-0.1.9.tgz",
|
||||
@@ -30405,23 +30405,12 @@
|
||||
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="
|
||||
},
|
||||
"tough-cookie": {
|
||||
"version": "4.1.4",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz",
|
||||
"integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==",
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.0.0.tgz",
|
||||
"integrity": "sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"psl": "^1.1.33",
|
||||
"punycode": "^2.1.1",
|
||||
"universalify": "^0.2.0",
|
||||
"url-parse": "^1.5.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"universalify": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
|
||||
"integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
|
||||
"optional": true
|
||||
}
|
||||
"tldts": "^6.1.32"
|
||||
}
|
||||
},
|
||||
"transform-ast": {
|
||||
@@ -30757,16 +30746,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"url-parse": {
|
||||
"version": "1.5.10",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
|
||||
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"querystringify": "^2.1.1",
|
||||
"requires-port": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
|
||||
@@ -76,9 +76,9 @@
|
||||
"@angular/router": "^17.3.1",
|
||||
"@angular/ssr": "^17.3.1",
|
||||
"@fortawesome/angular-fontawesome": "~0.14.1",
|
||||
"@fortawesome/fontawesome-common-types": "~6.6.0",
|
||||
"@fortawesome/fontawesome-svg-core": "~6.6.0",
|
||||
"@fortawesome/free-solid-svg-icons": "~6.6.0",
|
||||
"@fortawesome/fontawesome-common-types": "~6.7.2",
|
||||
"@fortawesome/fontawesome-svg-core": "~6.7.2",
|
||||
"@fortawesome/free-solid-svg-icons": "~6.7.2",
|
||||
"@mempool/mempool.js": "2.3.0",
|
||||
"@ng-bootstrap/ng-bootstrap": "^16.0.0",
|
||||
"@types/qrcode": "~1.5.0",
|
||||
@@ -87,7 +87,6 @@
|
||||
"clipboard": "^2.0.11",
|
||||
"domino": "^2.1.6",
|
||||
"echarts": "~5.5.0",
|
||||
"lightweight-charts": "~3.8.0",
|
||||
"ngx-echarts": "~17.2.0",
|
||||
"ngx-infinite-scroll": "^17.0.0",
|
||||
"qrcode": "1.5.1",
|
||||
@@ -115,7 +114,7 @@
|
||||
"optionalDependencies": {
|
||||
"@cypress/schematic": "^2.5.0",
|
||||
"@types/cypress": "^1.1.3",
|
||||
"cypress": "^13.15.0",
|
||||
"cypress": "^13.17.0",
|
||||
"cypress-fail-on-console-error": "~5.1.0",
|
||||
"cypress-wait-until": "^2.0.1",
|
||||
"mock-socket": "~9.3.1",
|
||||
|
||||
@@ -439,4 +439,39 @@ export const fiatCurrencies = {
|
||||
code: 'ZAR',
|
||||
indexed: true,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export interface Timezone {
|
||||
offset: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export const timezones: Timezone[] = [
|
||||
{ offset: '-12', name: 'Anywhere on Earth (AoE)' },
|
||||
{ offset: '-11', name: 'Samoa Standard Time (SST)' },
|
||||
{ offset: '-10', name: 'Hawaii Standard Time (HST)' },
|
||||
{ offset: '-9', name: 'Alaska Standard Time (AKST)' },
|
||||
{ offset: '-8', name: 'Pacific Standard Time (PST)' },
|
||||
{ offset: '-7', name: 'Mountain Standard Time (MST)' },
|
||||
{ offset: '-6', name: 'Central Standard Time (CST)' },
|
||||
{ offset: '-5', name: 'Eastern Standard Time (EST)' },
|
||||
{ offset: '-4', name: 'Atlantic Standard Time (AST)' },
|
||||
{ offset: '-3', name: 'Argentina Time (ART)' },
|
||||
{ offset: '-2', name: 'Fernando de Noronha Time (FNT)' },
|
||||
{ offset: '-1', name: 'Azores Time (AZOT)' },
|
||||
{ offset: '+0', name: 'Greenwich Mean Time (GMT)' },
|
||||
{ offset: '+1', name: 'Central European Time (CET)' },
|
||||
{ offset: '+2', name: 'Eastern European Time (EET)' },
|
||||
{ offset: '+3', name: 'Moscow Standard Time (MSK)' },
|
||||
{ offset: '+4', name: 'Armenia Time (AMT)' },
|
||||
{ offset: '+5', name: 'Pakistan Standard Time (PKT)' },
|
||||
{ offset: '+6', name: 'Xinjiang Time (XJT)' },
|
||||
{ offset: '+7', name: 'Indochina Time (ICT)' },
|
||||
{ offset: '+8', name: 'Hong Kong Time (HKT)' },
|
||||
{ offset: '+9', name: 'Japan Standard Time (JST)' },
|
||||
{ offset: '+10', name: 'Australian Eastern Standard Time (AEST)' },
|
||||
{ offset: '+11', name: 'Norfolk Time (NFT)' },
|
||||
{ offset: '+12', name: 'New Zealand Standard Time (NZST)' },
|
||||
{ offset: '+13', name: 'Tonga Time (TOT)' },
|
||||
{ offset: '+14', name: 'Line Islands Time (LINT)' }
|
||||
];
|
||||
@@ -612,10 +612,18 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
|
||||
this.processing = false;
|
||||
return;
|
||||
}
|
||||
const verificationToken = await this.$verifyBuyer(this.payments, tokenResult.token, tokenResult.details, costUSD.toFixed(2));
|
||||
if (!verificationToken) {
|
||||
console.error(`SCA verification failed`);
|
||||
this.accelerateError = 'SCA Verification Failed. Payment Declined.';
|
||||
this.processing = false;
|
||||
return;
|
||||
}
|
||||
const cardTag = md5(`${card.brand}${card.expMonth}${card.expYear}${card.last4}`.toLowerCase());
|
||||
this.servicesApiService.accelerateWithGooglePay$(
|
||||
this.tx.txid,
|
||||
tokenResult.token,
|
||||
verificationToken,
|
||||
cardTag,
|
||||
`accelerator-${this.tx.txid.substring(0, 15)}-${Math.round(new Date().getTime() / 1000)}`,
|
||||
costUSD
|
||||
@@ -743,6 +751,32 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Required in SCA Mandated Regions: Learn more at https://developer.squareup.com/docs/sca-overview
|
||||
*/
|
||||
async $verifyBuyer(payments, token, details, amount) {
|
||||
const verificationDetails = {
|
||||
amount: amount,
|
||||
currencyCode: 'USD',
|
||||
intent: 'CHARGE',
|
||||
billingContact: {
|
||||
givenName: details.card?.billing?.givenName,
|
||||
familyName: details.card?.billing?.familyName,
|
||||
phone: details.card?.billing?.phone,
|
||||
addressLines: details.card?.billing?.addressLines,
|
||||
city: details.card?.billing?.city,
|
||||
state: details.card?.billing?.state,
|
||||
countryCode: details.card?.billing?.countryCode,
|
||||
},
|
||||
};
|
||||
|
||||
const verificationResults = await payments.verifyBuyer(
|
||||
token,
|
||||
verificationDetails,
|
||||
);
|
||||
return verificationResults.token;
|
||||
}
|
||||
|
||||
/**
|
||||
* BTCPay
|
||||
*/
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
</div>
|
||||
</td>
|
||||
<td class="timestamp" *ngIf="!widget" [ngClass]="{'widget': widget, 'legacy': !isMempoolModule}">
|
||||
‎{{ block.timestamp * 1000 | date:'yyyy-MM-dd HH:mm:ss' }}
|
||||
<app-timestamp [customFormat]="'yyyy-MM-dd HH:mm:ss'" [unixTime]="block.timestamp" [hideTimeSince]="true"></app-timestamp>
|
||||
</td>
|
||||
<td *ngIf="auditAvailable" class="health text-right" [ngClass]="{'widget': widget, 'legacy': !isMempoolModule}">
|
||||
<a
|
||||
|
||||
@@ -305,6 +305,20 @@
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@case ('simpleproof') {
|
||||
<div class="col" style="max-height: 410px" [style.order]="isMobile && widget.mobileOrder || 8">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<a class="title-link" href="" [routerLink]="['/sp/verified' | relativeUrl]">
|
||||
<h5 class="card-title d-inline" i18n="dashboard.recent-blocks">{{ widget.props?.label }}</h5>
|
||||
<span> </span>
|
||||
<fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true" style="vertical-align: text-top; font-size: 13px; color: var(--title-fg)"></fa-icon>
|
||||
</a>
|
||||
<app-simpleproof-widget [label]="widget.props.label" [key]="widget.props.key" [widget]="true"></app-simpleproof-widget>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -56,8 +56,7 @@
|
||||
</ng-template>
|
||||
</td>
|
||||
<td class="timestamp text-left">
|
||||
‎{{ utxo.blocktime * 1000 | date:'yyyy-MM-dd HH:mm' }}
|
||||
<div class="symbol lg-inline relative-time"><i>(<app-time kind="since" [time]="utxo.blocktime"></app-time>)</i></div>
|
||||
<app-timestamp [customFormat]="'yyyy-MM-dd HH:mm'" [unixTime]="utxo.blocktime"></app-timestamp>
|
||||
</td>
|
||||
<td class="expires-in text-left" [ngStyle]="{ 'color': getGradientColor(utxo.blocknumber + utxo.timelock - lastReservesBlockUpdate) }">
|
||||
{{ utxo.blocknumber + utxo.timelock - lastReservesBlockUpdate < 0 ? -(utxo.blocknumber + utxo.timelock - lastReservesBlockUpdate) : utxo.blocknumber + utxo.timelock - lastReservesBlockUpdate }} <span i18n="shared.blocks" class="symbol">blocks</span>
|
||||
|
||||
@@ -53,8 +53,7 @@
|
||||
</ng-container>
|
||||
</td>
|
||||
<td class="timestamp text-left">
|
||||
‎{{ peg.blocktime * 1000 | date:'yyyy-MM-dd HH:mm' }}
|
||||
<div class="symbol lg-inline relative-time"><i>(<app-time kind="since" [time]="peg.blocktime"></app-time>)</i></div>
|
||||
<app-timestamp [customFormat]="'yyyy-MM-dd HH:mm'" [unixTime]="peg.blocktime"></app-timestamp>
|
||||
</td>
|
||||
<td class="amount text-right" [ngClass]="{'credit': peg.amount > 0, 'debit': peg.amount < 0, 'glow-effect': peg.amount < 0 && peg.bitcoinaddress && !peg.bitcointxid}">
|
||||
<app-amount [satoshis]="peg.amount" [noFiat]="true" [forceBtc]="true" [addPlus]="true"></app-amount>
|
||||
|
||||
@@ -194,7 +194,7 @@
|
||||
<a [routerLink]="['/block' | relativeUrl, block.id]">{{ block.height }}</a>
|
||||
</td>
|
||||
<td class="timestamp">
|
||||
‎{{ block.timestamp * 1000 | date:'yyyy-MM-dd HH:mm:ss' }}
|
||||
<app-timestamp [customFormat]="'yyyy-MM-dd HH:mm:ss'" [unixTime]="block.timestamp" [hideTimeSince]="true"></app-timestamp>
|
||||
</td>
|
||||
<td class="mined">
|
||||
<app-time kind="since" [time]="block.timestamp" [fastRender]="true" [showTooltip]="true"></app-time>
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
<div class="container-xl" style="min-height: 335px" [ngClass]="{'widget': widget, 'full-height': !widget}">
|
||||
<div *ngIf="!widget" class="float-left" style="display: flex; width: 100%; align-items: center;">
|
||||
<h1>{{ label }}</h1>
|
||||
<div *ngIf="!widget && isLoading" class="spinner-border" role="status"></div>
|
||||
</div>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
|
||||
@if (isLoading) {
|
||||
loading!
|
||||
<div class="spinner-wrapper">
|
||||
<div class="ml-2 spinner-border text-light" style="width: 25px; height: 25px"></div>
|
||||
</div>
|
||||
} @else if (error || !verified.length) {
|
||||
<div class="error-wrapper">
|
||||
<span>temporarily unavailable</span>
|
||||
</div>
|
||||
} @else {
|
||||
<div style="min-height: 295px">
|
||||
<table class="table table-borderless" [class.table-fixed]="widget">
|
||||
<thead>
|
||||
<th class="filename text-left" [ngClass]="{'widget': widget}" i18n="simpleproof.filename">Filename</th>
|
||||
<th class="hash text-left" [ngClass]="{'widget': widget}" i18n="simpleproof.hash">Hash</th>
|
||||
<th class="verified text-right" [ngClass]="{'widget': widget}" i18n="simpleproof.verified">Verified</th>
|
||||
<th class="proof text-right" [ngClass]="{'widget': widget}" i18n="simpleproof.proof">Proof</th>
|
||||
</thead>
|
||||
<tbody *ngIf="verifiedPage; else skeleton" [style]="isLoading ? 'opacity: 0.75' : ''">
|
||||
<tr *ngFor="let item of verifiedPage">
|
||||
<td class="filename text-left" [class]="widget ? 'widget' : ''">{{ item.file_name }}</td>
|
||||
<td class="hash text-left" [class]="widget ? 'widget' : ''">{{ item.sha256 }}</td>
|
||||
<td class="verified text-right" [class]="widget ? 'widget' : ''">
|
||||
<app-timestamp [unixTime]="item.block_time" [customFormat]="'yyyy-MM-dd'" [hideTimeSince]="true"></app-timestamp>
|
||||
</td>
|
||||
<td class="proof text-right" [class]="widget ? 'widget' : ''">
|
||||
<a [href]="item.sanitized_url" target="_blank" class="badge badge-primary badge-verify">
|
||||
<span class="icon">
|
||||
<img class="icon-img" src="/resources/sp.svg">
|
||||
</span>
|
||||
<span i18n="simpleproof.verify">Verify</span>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<ng-template #skeleton>
|
||||
<tbody>
|
||||
<tr *ngFor="let item of [].constructor(itemsPerPage)">
|
||||
<td class="filename text-left" [ngClass]="{'widget': widget}">
|
||||
<span class="skeleton-loader" style="max-width: 75px"></span>
|
||||
</td>
|
||||
<td class="hash text-left" [ngClass]="{'widget': widget}">
|
||||
<span class="skeleton-loader" style="max-width: 75px"></span>
|
||||
</td>
|
||||
<td class="verified text-right" [ngClass]="{'widget': widget}">
|
||||
<span class="skeleton-loader" style="max-width: 75px"></span>
|
||||
</td>
|
||||
<td class="proof text-right" [ngClass]="{'widget': widget}">
|
||||
<span class="skeleton-loader" style="max-width: 75px"></span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</ng-template>
|
||||
</table>
|
||||
|
||||
<ngb-pagination *ngIf="!widget" class="pagination-container float-right mt-2" [class]="isLoading ? 'disabled' : ''"
|
||||
[collectionSize]="verified.length" [rotate]="true" [maxSize]="paginationMaxSize" [pageSize]="itemsPerPage" [(page)]="page"
|
||||
(pageChange)="pageChange(page)" [boundaryLinks]="true" [ellipses]="false">
|
||||
</ngb-pagination>
|
||||
|
||||
<ng-template [ngIf]="!widget">
|
||||
<div class="clearfix"></div>
|
||||
<br>
|
||||
</ng-template>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@@ -0,0 +1,114 @@
|
||||
.spinner-wrapper, .error-wrapper {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.spinner-border {
|
||||
height: 25px;
|
||||
width: 25px;
|
||||
margin-top: -10px;
|
||||
margin-left: -13px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.container-xl {
|
||||
max-width: 1400px;
|
||||
}
|
||||
.container-xl.widget {
|
||||
padding-left: 0px;
|
||||
padding-bottom: 0px;
|
||||
padding-right: 0px;
|
||||
}
|
||||
.container-xl.legacy {
|
||||
max-width: 1140px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
tr, td, th {
|
||||
border: 0px;
|
||||
padding-top: 0.71rem !important;
|
||||
padding-bottom: 0.7rem !important;
|
||||
}
|
||||
|
||||
.clear-link {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
pointer-events: none;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.progress {
|
||||
background-color: var(--secondary);
|
||||
}
|
||||
|
||||
.filename {
|
||||
width: 50%;
|
||||
max-width: 300px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.hash {
|
||||
width: 25%;
|
||||
max-width: 700px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
td.hash {
|
||||
font-family: monospace;
|
||||
}
|
||||
.widget .hash {
|
||||
display: none;
|
||||
}
|
||||
@media (max-width: 1200px) {
|
||||
.hash {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.verified {
|
||||
width: 25%;
|
||||
}
|
||||
td.verified {
|
||||
color: var(--tertiary);
|
||||
}
|
||||
|
||||
.proof {
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
.badge-verify {
|
||||
font-size: 1.05em;
|
||||
font-weight: normal;
|
||||
background: var(--nav-bg);
|
||||
color: white;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: auto;
|
||||
|
||||
.icon {
|
||||
margin: -0.25em;
|
||||
margin-right: 0.5em;
|
||||
|
||||
.icon-img {
|
||||
width: 16px;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
import { Component, Input, SecurityContext, SimpleChanges, OnChanges } from '@angular/core';
|
||||
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
|
||||
import { ServicesApiServices } from '@app/services/services-api.service';
|
||||
import { catchError, of } from 'rxjs';
|
||||
|
||||
export interface SimpleProof {
|
||||
file_name: string;
|
||||
sha256: string;
|
||||
ots_verification: string;
|
||||
block_height: number;
|
||||
block_hash: string;
|
||||
block_time: number;
|
||||
simpleproof_url: string;
|
||||
key?: string;
|
||||
sanitized_url?: SafeResourceUrl;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-simpleproof-widget',
|
||||
templateUrl: './simpleproof-widget.component.html',
|
||||
styleUrls: ['./simpleproof-widget.component.scss'],
|
||||
})
|
||||
export class SimpleProofWidgetComponent implements OnChanges {
|
||||
@Input() key: string = window['__env']?.customize?.dashboard.widgets?.find(w => w.component ==='simpleproof')?.props?.key ?? '';
|
||||
@Input() label: string = window['__env']?.customize?.dashboard.widgets?.find(w => w.component ==='simpleproof')?.props?.label ?? 'Verified Documents';
|
||||
@Input() widget: boolean = false;
|
||||
@Input() width = 300;
|
||||
@Input() height = 400;
|
||||
|
||||
verified: SimpleProof[] = [];
|
||||
verifiedPage: SimpleProof[] = [];
|
||||
isLoading: boolean = true;
|
||||
error: boolean = false;
|
||||
page = 1;
|
||||
lastPage = 1;
|
||||
itemsPerPage = 15;
|
||||
paginationMaxSize = window.innerWidth <= 767.98 ? 3 : 5;
|
||||
|
||||
constructor(
|
||||
private servicesApiService: ServicesApiServices,
|
||||
public sanitizer: DomSanitizer,
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.loadVerifications();
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (changes.widget) {
|
||||
this.itemsPerPage = this.widget ? 6 : 15;
|
||||
}
|
||||
if (changes.key) {
|
||||
this.loadVerifications();
|
||||
}
|
||||
}
|
||||
|
||||
loadVerifications(): void {
|
||||
if (this.key) {
|
||||
this.isLoading = true;
|
||||
this.servicesApiService.getSimpleProofs$(this.key).pipe(
|
||||
catchError(() => {
|
||||
this.isLoading = false;
|
||||
this.error = true;
|
||||
return of({});
|
||||
}),
|
||||
).subscribe((data: Record<string, SimpleProof>) => {
|
||||
if (Object.keys(data).length) {
|
||||
this.verified = Object.keys(data).map(key => ({
|
||||
...data[key],
|
||||
file_name: data[key].file_name.replace('source-', '').replace('_', ' '),
|
||||
key,
|
||||
sanitized_url: this.sanitizer.bypassSecurityTrustResourceUrl(this.sanitizer.sanitize(SecurityContext.URL, data[key]['simpleproof-url']) ?? ''),
|
||||
})).sort((a, b) => b.key.localeCompare(a.key));
|
||||
this.verifiedPage = this.verified.slice((this.page - 1) * this.itemsPerPage, this.page * this.itemsPerPage);
|
||||
this.isLoading = false;
|
||||
this.error = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pageChange(page: number): void {
|
||||
this.page = page;
|
||||
this.verifiedPage = this.verified.slice((this.page - 1) * this.itemsPerPage, this.page * this.itemsPerPage);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<div [formGroup]="timezoneForm" class="text-small text-center">
|
||||
<select formControlName="mode" class="custom-select custom-select-sm form-control-secondary form-control mx-auto" style="width: 110px;" (change)="changeMode()">
|
||||
<option value="local">UTC{{ localTimezoneOffset !== '+0' ? localTimezoneOffset : '' }} {{ localTimezoneName ? '- ' + localTimezoneName : '' }}</option>
|
||||
<option value="+0" *ngIf="localTimezoneOffset !== '+0'">UTC - Greenwich Mean Time (GMT)</option>
|
||||
<option disabled>────</option>
|
||||
<option *ngFor="let timezone of timezones" [value]="timezone.offset">UTC{{ timezone.offset !== '+0' ? timezone.offset : '' }} - {{ timezone.name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
@@ -0,0 +1,58 @@
|
||||
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
||||
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
|
||||
import { StorageService } from '@app/services/storage.service';
|
||||
import { StateService } from '@app/services/state.service';
|
||||
import { timezones } from '@app/app.constants';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-timezone-selector',
|
||||
templateUrl: './timezone-selector.component.html',
|
||||
styleUrls: ['./timezone-selector.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class TimezoneSelectorComponent implements OnInit {
|
||||
timezoneForm: UntypedFormGroup;
|
||||
timezones = timezones;
|
||||
localTimezoneOffset: string = '';
|
||||
localTimezoneName: string;
|
||||
|
||||
constructor(
|
||||
private formBuilder: UntypedFormBuilder,
|
||||
private stateService: StateService,
|
||||
private storageService: StorageService,
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.setLocalTimezone();
|
||||
this.timezoneForm = this.formBuilder.group({
|
||||
mode: ['local'],
|
||||
});
|
||||
this.stateService.timezone$.subscribe((mode) => {
|
||||
this.timezoneForm.get('mode')?.setValue(mode);
|
||||
});
|
||||
}
|
||||
|
||||
changeMode() {
|
||||
const newMode = this.timezoneForm.get('mode')?.value;
|
||||
this.storageService.setValue('timezone-preference', newMode);
|
||||
this.stateService.timezone$.next(newMode);
|
||||
}
|
||||
|
||||
setLocalTimezone() {
|
||||
const offset = new Date().getTimezoneOffset();
|
||||
const sign = offset <= 0 ? "+" : "-";
|
||||
const absOffset = Math.abs(offset);
|
||||
const hours = String(Math.floor(absOffset / 60));
|
||||
const minutes = String(absOffset % 60).padStart(2, '0');
|
||||
if (minutes === '00') {
|
||||
this.localTimezoneOffset = `${sign}${hours}`;
|
||||
} else {
|
||||
this.localTimezoneOffset = `${sign}${hours.padStart(2, '0')}:${minutes}`;
|
||||
}
|
||||
|
||||
const timezone = this.timezones.find(tz => tz.offset === this.localTimezoneOffset);
|
||||
this.timezones = this.timezones.filter(tz => tz.offset !== this.localTimezoneOffset && tz.offset !== '+0');
|
||||
this.localTimezoneName = timezone ? timezone.name : '';
|
||||
}
|
||||
}
|
||||
@@ -88,7 +88,7 @@
|
||||
<div class="field narrower mt-2">
|
||||
<div class="label" i18n="transaction.confirmed-at">Confirmed at</div>
|
||||
<div class="value">
|
||||
‎{{ tx.status.block_time * 1000 | date:'yyyy-MM-dd HH:mm' }}
|
||||
<app-timestamp [customFormat]="'yyyy-MM-dd HH:mm'" [unixTime]="tx.status.block_time" [hideTimeSince]="true"></app-timestamp>
|
||||
<div class="lg-inline">
|
||||
<i class="symbol">(<app-time kind="since" [time]="tx.status.block_time" [fastRender]="true" [showTooltip]="true"></app-time>)</i>
|
||||
</div>
|
||||
|
||||
@@ -61,10 +61,7 @@
|
||||
<tr>
|
||||
<td i18n="block.timestamp">Timestamp</td>
|
||||
<td>
|
||||
‎{{ tx.status.block_time * 1000 | date:'yyyy-MM-dd HH:mm:ss' }}
|
||||
<div class="lg-inline">
|
||||
<i class="symbol">(<app-time kind="since" [time]="tx.status.block_time" [fastRender]="true"></app-time>)</i>
|
||||
</div>
|
||||
<app-timestamp [customFormat]="'yyyy-MM-dd HH:mm:ss'" [unixTime]="tx.status.block_time"></app-timestamp>
|
||||
</td>
|
||||
</tr>
|
||||
} @else {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<app-truncate [text]="tx.txid"></app-truncate>
|
||||
</a>
|
||||
<div>
|
||||
<ng-template [ngIf]="tx.status.confirmed">‎{{ tx.status.block_time * 1000 | date:'yyyy-MM-dd HH:mm:ss' }}</ng-template>
|
||||
<ng-template [ngIf]="tx.status.confirmed"><app-timestamp [customFormat]="'yyyy-MM-dd HH:mm:ss'" [unixTime]="tx.status.block_time" [hideTimeSince]="true"></app-timestamp></ng-template>
|
||||
<ng-template [ngIf]="!tx.status.confirmed && tx.firstSeen">
|
||||
<i><app-time kind="since" [time]="tx.firstSeen" [fastRender]="true" [showTooltip]="true"></app-time></i>
|
||||
</ng-template>
|
||||
@@ -81,7 +81,7 @@
|
||||
</ng-container>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-right nowrap amount" [class]="{large: vin?.prevout?.value > 1000000000 || vin.isInscription}">
|
||||
<td class="text-right nowrap amount" [class]="{large: tx.largeInput}">
|
||||
<button *ngIf="vin.isInscription" (click)="toggleOrdData(tx.txid, 'vin', vindex)" type="button" class="btn btn-sm badge badge-ord primary" style="margin-right: 10px;">Inscription</button>
|
||||
<ng-template [ngIf]="vin.prevout && vin.prevout.asset && vin.prevout.asset !== nativeAssetId" [ngIfElse]="defaultOutput">
|
||||
<div *ngIf="assetsMinimal && assetsMinimal[vin.prevout.asset] else assetVinNotFound">
|
||||
@@ -257,7 +257,7 @@
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
</td>
|
||||
<td class="text-right nowrap amount" [class]="{large: vout?.value > 1000000000}">
|
||||
<td class="text-right nowrap amount" [class]="{large: tx.largeOutput}">
|
||||
<ng-template [ngIf]="vout.asset && vout.asset !== nativeAssetId" [ngIfElse]="defaultOutput">
|
||||
<div *ngIf="assetsMinimal && assetsMinimal[vout.asset] else assetNotFound">
|
||||
<ng-container *ngTemplateOutlet="assetBox; context:{ $implicit: vout }"></ng-container>
|
||||
|
||||
@@ -258,6 +258,7 @@ export class TransactionsListComponent implements OnInit, OnChanges {
|
||||
const hasAnnex = tx.vin[i].witness?.[tx.vin[i].witness.length - 1].startsWith('50');
|
||||
if (tx.vin[i].witness.length > (hasAnnex ? 2 : 1) && tx.vin[i].witness[tx.vin[i].witness.length - (hasAnnex ? 3 : 2)].includes('0063036f7264')) {
|
||||
tx.vin[i].isInscription = true;
|
||||
tx.largeInput = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -268,6 +269,9 @@ export class TransactionsListComponent implements OnInit, OnChanges {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tx.largeInput = tx.largeInput || tx.vin.some(vin => (vin?.prevout?.value > 1000000000));
|
||||
tx.largeOutput = tx.vout.some(vout => (vout?.value > 1000000000));
|
||||
});
|
||||
|
||||
if (this.blockTime && this.transactions?.length && this.currency) {
|
||||
@@ -351,8 +355,12 @@ export class TransactionsListComponent implements OnInit, OnChanges {
|
||||
this.electrsApiService.getTransaction$(tx.txid)
|
||||
.subscribe((newTx) => {
|
||||
tx['@vinLoaded'] = true;
|
||||
let temp = tx.vin;
|
||||
tx.vin = newTx.vin;
|
||||
tx.fee = newTx.fee;
|
||||
for (const [index, vin] of temp.entries()) {
|
||||
newTx.vin[index].isInscription = vin.isInscription;
|
||||
}
|
||||
this.ref.markForCheck();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@ export interface Transaction {
|
||||
price?: Price;
|
||||
sigops?: number;
|
||||
flags?: bigint;
|
||||
largeInput?: boolean;
|
||||
largeOutput?: boolean;
|
||||
}
|
||||
|
||||
export interface TransactionChannels {
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<tbody>
|
||||
<tr>
|
||||
<td i18n="lightning.created">Created</td>
|
||||
<td>{{ channel.created | date:'yyyy-MM-dd HH:mm' }}</td>
|
||||
<td><app-timestamp [customFormat]="'yyyy-MM-dd HH:mm'" [unixTime]="channel.created" [hideTimeSince]="true"></app-timestamp></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td i18n="lightning.capacity">Capacity</td>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<ng-container *ngFor="let channel of channels;">
|
||||
<tr>
|
||||
<td class="timestamp">
|
||||
‎{{ channel.closing_date | date:'yyyy-MM-dd HH:mm' }}
|
||||
<app-timestamp [customFormat]="'yyyy-MM-dd HH:mm'" [unixTime]="channel.closing_date" [hideTimeSince]="true"></app-timestamp>
|
||||
</td>
|
||||
<td class="capacity text-right">
|
||||
<app-amount *ngIf="channel.capacity > 100000000; else smallnode" [satoshis]="channel.capacity" [digitsInfo]="'1.2-2'" [noFiat]="true"></app-amount>
|
||||
|
||||
@@ -13,6 +13,7 @@ import { RbfList } from '@components/rbf-list/rbf-list.component';
|
||||
import { ServerHealthComponent } from '@components/server-health/server-health.component';
|
||||
import { ServerStatusComponent } from '@components/server-health/server-status.component';
|
||||
import { FaucetComponent } from '@components/faucet/faucet.component'
|
||||
import { SimpleProofWidgetComponent } from './components/simpleproof-widget/simpleproof-widget.component';
|
||||
|
||||
const browserWindow = window || {};
|
||||
// @ts-ignore
|
||||
@@ -130,6 +131,13 @@ if (window['__env']?.OFFICIAL_MEMPOOL_SPACE) {
|
||||
}
|
||||
}
|
||||
|
||||
if (window['__env']?.customize?.dashboard.widgets?.some(w => w.component ==='simpleproof')) {
|
||||
routes[0].children.push({
|
||||
path: 'sp/verified',
|
||||
component: SimpleProofWidgetComponent,
|
||||
});
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forChild(routes)
|
||||
|
||||
@@ -55,7 +55,7 @@ export class EtaService {
|
||||
|
||||
return {
|
||||
hashratePercentage: acceleratingHashrateFraction * 100,
|
||||
ETA: Date.now() + da.timeAvg * mempoolPosition.block,
|
||||
ETA: Date.now() + da.adjustedTimeAvg * mempoolPosition.block,
|
||||
acceleratedETA: this.calculateETAFromShares([
|
||||
{ block: mempoolPosition.block, hashrateShare: (1 - acceleratingHashrateFraction) },
|
||||
{ block: 0, hashrateShare: acceleratingHashrateFraction },
|
||||
@@ -216,7 +216,7 @@ export class EtaService {
|
||||
}
|
||||
// at max depth, the transaction is guaranteed to be mined in the next block if it hasn't already
|
||||
Q += ((max + 1) * (1-tailProb));
|
||||
const eta = da.timeAvg * Q; // T x Q
|
||||
const eta = da.adjustedTimeAvg * Q; // T x Q
|
||||
|
||||
return {
|
||||
now,
|
||||
|
||||
@@ -8,6 +8,7 @@ import { Observable, of, ReplaySubject, tap, catchError, share, filter, switchMa
|
||||
import { IBackendInfo } from '@interfaces/websocket.interface';
|
||||
import { Acceleration, AccelerationHistoryParams } from '@interfaces/node-api.interface';
|
||||
import { AccelerationStats } from '@components/acceleration/acceleration-stats/acceleration-stats.component';
|
||||
import { SimpleProof } from '../components/simpleproof-widget/simpleproof-widget.component';
|
||||
|
||||
export interface IUser {
|
||||
username: string;
|
||||
@@ -143,8 +144,8 @@ export class ServicesApiServices {
|
||||
return this.httpClient.post<any>(`${this.stateService.env.SERVICES_API}/accelerator/accelerate/applePay`, { txInput: txInput, cardTag: cardTag, token: token, referenceId: referenceId, userApprovedUSD: userApprovedUSD });
|
||||
}
|
||||
|
||||
accelerateWithGooglePay$(txInput: string, token: string, cardTag: string, referenceId: string, userApprovedUSD: number) {
|
||||
return this.httpClient.post<any>(`${this.stateService.env.SERVICES_API}/accelerator/accelerate/googlePay`, { txInput: txInput, cardTag: cardTag, token: token, referenceId: referenceId, userApprovedUSD: userApprovedUSD });
|
||||
accelerateWithGooglePay$(txInput: string, token: string, verificationToken: string, cardTag: string, referenceId: string, userApprovedUSD: number) {
|
||||
return this.httpClient.post<any>(`${this.stateService.env.SERVICES_API}/accelerator/accelerate/googlePay`, { txInput: txInput, cardTag: cardTag, token: token, verificationToken: verificationToken, referenceId: referenceId, userApprovedUSD: userApprovedUSD });
|
||||
}
|
||||
|
||||
getAccelerations$(): Observable<Acceleration[]> {
|
||||
@@ -217,4 +218,8 @@ export class ServicesApiServices {
|
||||
getPaymentStatus$(orderId: string): Observable<any> {
|
||||
return this.httpClient.get<any>(`${this.stateService.env.SERVICES_API}/payments/bitcoin/check?order_id=${orderId}`, { observe: 'response' });
|
||||
}
|
||||
|
||||
getSimpleProofs$(key: string): Observable<Record<string, SimpleProof>> {
|
||||
return this.httpClient.get<Record<string, SimpleProof>>(`${this.stateService.env.SERVICES_API}/sp/verified/${key}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,6 +186,7 @@ export class StateService {
|
||||
live2Chart$ = new Subject<OptimizedMempoolStats>();
|
||||
|
||||
viewAmountMode$: BehaviorSubject<'btc' | 'sats' | 'fiat'>;
|
||||
timezone$: BehaviorSubject<string>;
|
||||
connectionState$ = new BehaviorSubject<0 | 1 | 2>(2);
|
||||
isTabHidden$: Observable<boolean>;
|
||||
|
||||
@@ -347,6 +348,9 @@ export class StateService {
|
||||
const viewAmountModePreference = this.storageService.getValue('view-amount-mode') as 'btc' | 'sats' | 'fiat';
|
||||
this.viewAmountMode$ = new BehaviorSubject<'btc' | 'sats' | 'fiat'>(viewAmountModePreference || 'btc');
|
||||
|
||||
const timezonePreference = this.storageService.getValue('timezone-preference');
|
||||
this.timezone$ = new BehaviorSubject<string>(timezonePreference || 'local');
|
||||
|
||||
this.backend$.subscribe(backend => {
|
||||
this.backend = backend;
|
||||
});
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
<app-fiat-selector></app-fiat-selector>
|
||||
</div>
|
||||
<div class="selector">
|
||||
<app-rate-unit-selector></app-rate-unit-selector>
|
||||
<app-timezone-selector></app-timezone-selector>
|
||||
</div>
|
||||
<div class="selector d-none" [ngClass]="isServicesPage ? 'd-lg-flex' : 'd-md-flex'">
|
||||
<app-amount-selector></app-amount-selector>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<span *ngIf="seconds === undefined">-</span>
|
||||
<span *ngIf="seconds !== undefined">
|
||||
‎{{ seconds * 1000 | date: customFormat ?? 'yyyy-MM-dd HH:mm' }}
|
||||
‎{{ seconds * 1000 | date: customFormat ?? 'yyyy-MM-dd HH:mm' : (stateService.timezone$ | async) }}
|
||||
<div class="lg-inline" *ngIf="!hideTimeSince">
|
||||
<i class="symbol">(<app-time kind="since" [time]="seconds" [fastRender]="true" [precision]="precision" [minUnit]="minUnit"></app-time>)</i>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/core';
|
||||
import { StateService } from '@app/services/state.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-timestamp',
|
||||
@@ -16,6 +17,10 @@ export class TimestampComponent implements OnChanges {
|
||||
|
||||
seconds: number | undefined = undefined;
|
||||
|
||||
constructor(
|
||||
public stateService: StateService,
|
||||
) { }
|
||||
|
||||
ngOnChanges(): void {
|
||||
if (this.unixTime) {
|
||||
this.seconds = this.unixTime;
|
||||
|
||||
@@ -36,6 +36,7 @@ import { FiatSelectorComponent } from '@components/fiat-selector/fiat-selector.c
|
||||
import { RateUnitSelectorComponent } from '@components/rate-unit-selector/rate-unit-selector.component';
|
||||
import { ThemeSelectorComponent } from '@components/theme-selector/theme-selector.component';
|
||||
import { AmountSelectorComponent } from '@components/amount-selector/amount-selector.component';
|
||||
import { TimezoneSelectorComponent } from '@components/timezone-selector/timezone-selector.component';
|
||||
import { BrowserOnlyDirective } from '@app/shared/directives/browser-only.directive';
|
||||
import { ServerOnlyDirective } from '@app/shared/directives/server-only.directive';
|
||||
import { ColoredPriceDirective } from '@app/shared/directives/colored-price.directive';
|
||||
@@ -115,6 +116,7 @@ import { CalculatorComponent } from '@components/calculator/calculator.component
|
||||
import { BitcoinsatoshisPipe } from '@app/shared/pipes/bitcoinsatoshis.pipe';
|
||||
import { HttpErrorComponent } from '@app/shared/components/http-error/http-error.component';
|
||||
import { TwitterWidgetComponent } from '@components/twitter-widget/twitter-widget.component';
|
||||
import { SimpleProofWidgetComponent } from '@components/simpleproof-widget/simpleproof-widget.component';
|
||||
import { FaucetComponent } from '@components/faucet/faucet.component';
|
||||
import { TwitterLogin } from '@components/twitter-login/twitter-login.component';
|
||||
import { BitcoinInvoiceComponent } from '@components/bitcoin-invoice/bitcoin-invoice.component';
|
||||
@@ -134,6 +136,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from '@app/shared/components/
|
||||
ThemeSelectorComponent,
|
||||
RateUnitSelectorComponent,
|
||||
AmountSelectorComponent,
|
||||
TimezoneSelectorComponent,
|
||||
ScriptpubkeyTypePipe,
|
||||
RelativeUrlPipe,
|
||||
NoSanitizePipe,
|
||||
@@ -233,6 +236,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from '@app/shared/components/
|
||||
OrdDataComponent,
|
||||
HttpErrorComponent,
|
||||
TwitterWidgetComponent,
|
||||
SimpleProofWidgetComponent,
|
||||
FaucetComponent,
|
||||
TwitterLogin,
|
||||
BitcoinInvoiceComponent,
|
||||
@@ -283,6 +287,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from '@app/shared/components/
|
||||
RateUnitSelectorComponent,
|
||||
ThemeSelectorComponent,
|
||||
AmountSelectorComponent,
|
||||
TimezoneSelectorComponent,
|
||||
ScriptpubkeyTypePipe,
|
||||
RelativeUrlPipe,
|
||||
Hex2asciiPipe,
|
||||
@@ -366,6 +371,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from '@app/shared/components/
|
||||
OrdDataComponent,
|
||||
HttpErrorComponent,
|
||||
TwitterWidgetComponent,
|
||||
SimpleProofWidgetComponent,
|
||||
TwitterLogin,
|
||||
BitcoinInvoiceComponent,
|
||||
BitcoinsatoshisPipe,
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>River | Invest in Bitcoin with confidence</title>
|
||||
<script src="/resources/config.js"></script>
|
||||
<script src="/resources/customize.js"></script>
|
||||
<base href="/">
|
||||
|
||||
<meta name="description" content="Easily buy Bitcoin in minutes. Zero fees on recurring buys. Invest in Bitcoin with confidence with world-class security." />
|
||||
<meta property="og:image" content="https://mempool.space/resources/river/river-preview.jpg" />
|
||||
<meta property="og:image:type" content="image/jpeg" />
|
||||
<meta property="og:image:width" content="2000" />
|
||||
<meta property="og:image:height" content="1000" />
|
||||
<meta property="og:description" content="Easily buy Bitcoin in minutes. Zero fees on recurring buys. Invest in Bitcoin with confidence with world-class security." />
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:site" content="@mempool">
|
||||
<meta name="twitter:creator" content="@mempool">
|
||||
<meta name="twitter:title" content="River | Invest in Bitcoin with confidence">
|
||||
<meta name="twitter:description" content="Easily buy Bitcoin in minutes. Zero fees on recurring buys. Invest in Bitcoin with confidence with world-class security." />
|
||||
<meta name="twitter:image" content="https://mempool.space/resources/river/river-preview.jpg" />
|
||||
<meta name="twitter:domain" content="river.mempool.space">
|
||||
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/resources/river/favicons/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/resources/river/favicons/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/resources/river/favicons/favicon-16x16.png">
|
||||
<link rel="manifest" href="/resources/river/favicons/site.webmanifest">
|
||||
<link rel="shortcut icon" href="/resources/river/favicons/favicon.ico">
|
||||
<link id="canonical" rel="canonical" href="https://river.mempool.space">
|
||||
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||
<meta name="msapplication-TileColor" content="#000000">
|
||||
<meta name="msapplication-config" content="/resources/favicons/browserconfig.xml">
|
||||
<meta name="theme-color" content="#1d1f31">
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
Before Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 378 B |
|
Before Width: | Height: | Size: 772 B |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 16 KiB |
@@ -1 +0,0 @@
|
||||
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
|
||||
@@ -1,4 +0,0 @@
|
||||
<svg width="160" height="40" viewBox="0 0 160 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M75.8865 31.2087H70.2185L66.2029 23.3666H59.531V31.2087H54.5116V8.52042H66.8515C69.4627 8.52042 71.4874 9.22873 72.9256 10.6425C74.3609 12.0591 75.08 13.8144 75.08 15.9082C75.08 17.3135 74.7388 18.5975 74.0592 19.7658C73.3796 20.9341 72.4011 21.8286 71.1293 22.4551L75.8893 31.2059L75.8865 31.2087ZM59.531 18.3407H67.1109C68.0612 18.3407 68.7888 18.1291 69.2964 17.7086C69.8039 17.2881 70.0577 16.7096 70.0577 15.9759C70.0577 15.2422 69.8039 14.6525 69.2964 14.2094C68.7888 13.7664 68.0612 13.5463 67.1109 13.5463H59.531V18.3435V18.3407ZM84.2954 31.2087H79.276V8.52042H84.2954V31.2087ZM99.709 31.0083L106.192 8.52042H111.699L105.135 31.2087H94.0862L87.7949 8.52042H93.3022L99.7119 31.0083H99.709ZM134.718 26.1857V31.2087H114.638V8.52042H134.557V13.5434H119.66V17.3671H134.397V22.3591H119.66V26.1828H134.721L134.718 26.1857ZM159.914 31.2087H154.246L150.23 23.3666H143.559V31.2087H138.539V8.52042H150.879C153.49 8.52042 155.515 9.22873 156.953 10.6425C158.388 12.0591 159.108 13.8144 159.108 15.9082C159.108 17.3135 158.766 18.5975 158.087 19.7658C157.407 20.9341 156.429 21.8286 155.157 22.4551L159.917 31.2059L159.914 31.2087ZM143.559 18.3407H151.138C152.089 18.3407 152.816 18.1291 153.324 17.7086C153.831 17.2881 154.085 16.7096 154.085 15.9759C154.085 15.2422 153.831 14.6525 153.324 14.2094C152.816 13.7664 152.089 13.5463 151.138 13.5463H143.559V18.3435V18.3407Z" fill="#F9F9F9"/>
|
||||
<path d="M6.50834 35.4049L19.1838 7.60612C19.2346 7.49324 19.4038 7.56097 19.3643 7.67949L10.2673 34.0024C11.6265 33.5734 13.0195 33.2207 14.4407 32.9442L20.481 8.00683C20.5092 7.88549 20.6896 7.92218 20.6727 8.04352L17.1591 32.5124C18.3181 32.3713 19.4968 32.281 20.684 32.2415L21.8289 8.15357C21.8345 8.02941 22.0178 8.02941 22.0235 8.15357L23.1683 32.2415C24.3583 32.2782 25.5342 32.3685 26.6932 32.5124L23.1796 8.04352C23.1627 7.92218 23.3404 7.88549 23.3714 8.00683L29.4116 32.9442C30.8328 33.2207 32.2259 33.5763 33.5851 34.0024L24.4881 7.67949C24.4486 7.56379 24.6178 7.49324 24.6685 7.60612L37.344 35.4049C39.6338 36.3982 41.8107 37.6031 43.8495 39L21.9248 1L0 39C2.03597 37.6031 4.21294 36.3982 6.50552 35.4049" fill="#C5A063"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.2 KiB |
36
frontend/src/resources/sp.svg
Normal file
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
id="Layer_1"
|
||||
version="1.1"
|
||||
viewBox="0 0 492.10001 575.79999"
|
||||
width="492.10001"
|
||||
height="575.79999"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<!-- Generator: Adobe Illustrator 29.0.0, SVG Export Plug-In . SVG Version: 2.1.0 Build 186) -->
|
||||
<defs
|
||||
id="defs184">
|
||||
<style
|
||||
id="style182">
|
||||
.st0 {
|
||||
fill: #fff;
|
||||
}
|
||||
|
||||
.st1 {
|
||||
fill: #f88e2b;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<g
|
||||
id="g216"
|
||||
transform="translate(-159.5,-152.1)">
|
||||
<polygon
|
||||
class="st0"
|
||||
points="296.6,375.6 296.6,459.1 404.9,524.2 651.6,378.5 651.6,294.6 651.6,294.5 405.3,440.4 "
|
||||
id="polygon212" />
|
||||
<polygon
|
||||
class="st1"
|
||||
points="405.5,644.5 231.3,542 231.3,335.6 405.5,235 520.9,301.7 592.1,259.8 405.5,152.1 159.5,294.1 159.5,583.1 405.5,727.9 651.6,583.1 651.6,447.1 579.7,489.4 579.7,542 "
|
||||
id="polygon214" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 996 B |
@@ -131,7 +131,7 @@ export NVM_DIR="${HOME}/.nvm"
|
||||
source "${NVM_DIR}/nvm.sh"
|
||||
|
||||
# what to look for
|
||||
frontends=(mainnet liquid onbtc bitb meta river)
|
||||
frontends=(mainnet liquid onbtc bitb meta)
|
||||
backends=(mainnet testnet testnet4 signet liquid liquidtestnet onbtc bitb)
|
||||
frontend_repos=()
|
||||
backend_repos=()
|
||||
|
||||
@@ -153,6 +153,6 @@
|
||||
},
|
||||
"WALLETS": {
|
||||
"ENABLED": true,
|
||||
"WALLETS": ["BITB", "3350", "RIVER"]
|
||||
"WALLETS": ["BITB", "3350"]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"OFFICIAL_MEMPOOL_SPACE": true,
|
||||
"TESTNET_ENABLED": true,
|
||||
"TESTNET4_ENABLED": true,
|
||||
"LIQUID_ENABLED": true,
|
||||
"LIQUID_TESTNET_ENABLED": true,
|
||||
"BISQ_ENABLED": true,
|
||||
"BISQ_SEPARATE_BACKEND": true,
|
||||
"SIGNET_ENABLED": true,
|
||||
"MEMPOOL_WEBSITE_URL": "https://mempool.space",
|
||||
"LIQUID_WEBSITE_URL": "https://liquid.network",
|
||||
"BISQ_WEBSITE_URL": "https://bisq.markets",
|
||||
"ITEMS_PER_PAGE": 25,
|
||||
"LIGHTNING": true,
|
||||
"ACCELERATOR": true,
|
||||
"PUBLIC_ACCELERATIONS": true,
|
||||
"AUDIT": true,
|
||||
"CUSTOMIZATION": "custom-river-config.json"
|
||||
}
|
||||
@@ -16,7 +16,7 @@ screen -dmS x startx
|
||||
sleep 3
|
||||
|
||||
# start unfurlers for each frontend
|
||||
for site in mainnet liquid onbtc bitb meta river;do
|
||||
for site in mainnet liquid onbtc bitb meta;do
|
||||
cd "$HOME/${site}/unfurler" && \
|
||||
echo "starting mempool unfurler: ${site}" && \
|
||||
screen -dmS "unfurler-${site}" sh -c 'while true;do npm run unfurler;sleep 2;done'
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"SERVER": {
|
||||
"HOST": "https://river.tk7.mempool.space",
|
||||
"HTTP_PORT": 8007
|
||||
},
|
||||
"MEMPOOL": {
|
||||
"HTTP_HOST": "http://127.0.0.1",
|
||||
"HTTP_PORT": 7,
|
||||
"NETWORK": "river"
|
||||
},
|
||||
"PUPPETEER": {
|
||||
"CLUSTER_SIZE": 8,
|
||||
"EXEC_PATH": "/usr/local/bin/chrome",
|
||||
"MAX_PAGE_AGE": 86400,
|
||||
"RENDER_TIMEOUT": 3000
|
||||
}
|
||||
}
|
||||
@@ -333,28 +333,6 @@ export const networks = {
|
||||
routes: routes.lightning.routes,
|
||||
}
|
||||
}
|
||||
},
|
||||
river: {
|
||||
networkName: 'River',
|
||||
title: 'River | Invest in Bitcoin with confidence',
|
||||
description: 'Easily buy Bitcoin in minutes. Zero fees on recurring buys. Invest in Bitcoin with confidence with world-class security.',
|
||||
fallbackImg: '/resources/river/river-preview.jpg',
|
||||
routes: { // only dynamic routes supported
|
||||
block: routes.block,
|
||||
address: routes.address,
|
||||
wallet: routes.wallet,
|
||||
tx: routes.tx,
|
||||
mining: {
|
||||
title: "Mining",
|
||||
routes: {
|
||||
pool: routes.mining.routes.pool,
|
||||
}
|
||||
},
|
||||
lightning: {
|
||||
title: "Lightning",
|
||||
routes: routes.lightning.routes,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||