diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1a140dd59..62cd5666d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,7 +35,7 @@ jobs: - name: Install ${{ steps.gettoolchain.outputs.toolchain }} Rust toolchain # Latest version available on this commit is 1.71.1 # Commit date is Aug 3, 2023 - uses: dtolnay/rust-toolchain@be73d7920c329f220ce78e0234b8f96b7ae60248 + uses: dtolnay/rust-toolchain@dc6353516c68da0f06325f42ad880f76a5e77ec9 with: toolchain: ${{ steps.gettoolchain.outputs.toolchain }} @@ -251,7 +251,6 @@ jobs: strategy: fail-fast: false matrix: - # module: ["mempool", "liquid", "bisq"] Disabling bisq support for now module: ["mempool", "liquid"] include: - module: "mempool" @@ -263,9 +262,6 @@ jobs: spec: | cypress/e2e/liquid/liquid.spec.ts cypress/e2e/liquidtestnet/liquidtestnet.spec.ts - # - module: "bisq" - # spec: | - # cypress/e2e/bisq/bisq.spec.ts name: E2E tests for ${{ matrix.module }} steps: diff --git a/.github/workflows/on-tag.yml b/.github/workflows/on-tag.yml index 55a5585cc..634a27ab9 100644 --- a/.github/workflows/on-tag.yml +++ b/.github/workflows/on-tag.yml @@ -101,5 +101,7 @@ jobs: --platform linux/amd64,linux/arm64 \ --tag ${{ secrets.DOCKER_HUB_USER }}/${{ matrix.service }}:$TAG \ --tag ${{ secrets.DOCKER_HUB_USER }}/${{ matrix.service }}:latest \ + --build-context rustgbt=./rust \ + --build-context backend=./backend \ --output "type=registry" ./${{ matrix.service }}/ \ --build-arg commitHash=$SHORT_SHA diff --git a/backend/mempool-config.sample.json b/backend/mempool-config.sample.json index 79afbae13..c93774fba 100644 --- a/backend/mempool-config.sample.json +++ b/backend/mempool-config.sample.json @@ -97,10 +97,6 @@ "GEOLITE2_ASN": "/usr/local/share/GeoIP/GeoLite2-ASN.mmdb", "GEOIP2_ISP": "/usr/local/share/GeoIP/GeoIP2-ISP.mmdb" }, - "BISQ": { - "ENABLED": false, - "DATA_PATH": "/bisq/statsnode-data/btc_mainnet/db" - }, "LIGHTNING": { "ENABLED": false, "BACKEND": "lnd", @@ -131,9 +127,7 @@ "MEMPOOL_API": "https://mempool.space/api/v1", "MEMPOOL_ONION": "http://mempoolhqx4isw62xs7abwphsq7ldayuidyx2v2oethdhhj6mlo2r6ad.onion/api/v1", "LIQUID_API": "https://liquid.network/api/v1", - "LIQUID_ONION": "http://liquidmom47f6s3m53ebfxn47p76a6tlnxib3wp6deux7wuzotdr6cyd.onion/api/v1", - "BISQ_URL": "https://bisq.markets/api", - "BISQ_ONION": "http://bisqmktse2cabavbr2xjq7xw3h6g5ottemo5rolfcwt6aly6tp5fdryd.onion/api" + "LIQUID_ONION": "http://liquidmom47f6s3m53ebfxn47p76a6tlnxib3wp6deux7wuzotdr6cyd.onion/api/v1" }, "REDIS": { "ENABLED": false, diff --git a/backend/npm_package_rm_build_deps.sh b/backend/npm_package_rm_build_deps.sh index 692ba24eb..5d7af2457 100755 --- a/backend/npm_package_rm_build_deps.sh +++ b/backend/npm_package_rm_build_deps.sh @@ -3,7 +3,7 @@ set -e # Cleaning up inside the node_modules folder cd package/node_modules -rm -r \ +rm -rf \ typescript \ @typescript-eslint \ @napi-rs diff --git a/backend/src/__fixtures__/mempool-config.template.json b/backend/src/__fixtures__/mempool-config.template.json index 41e9cce4e..1c973c45b 100644 --- a/backend/src/__fixtures__/mempool-config.template.json +++ b/backend/src/__fixtures__/mempool-config.template.json @@ -93,10 +93,6 @@ "ENABLED": false, "TX_PER_SECOND_SAMPLE_PERIOD": 20 }, - "BISQ": { - "ENABLED": true, - "DATA_PATH": "__BISQ_DATA_PATH__" - }, "SOCKS5PROXY": { "ENABLED": true, "USE_ONION": true, @@ -109,9 +105,7 @@ "MEMPOOL_API": "__EXTERNAL_DATA_SERVER_MEMPOOL_API__", "MEMPOOL_ONION": "__EXTERNAL_DATA_SERVER_MEMPOOL_ONION__", "LIQUID_API": "__EXTERNAL_DATA_SERVER_LIQUID_API__", - "LIQUID_ONION": "__EXTERNAL_DATA_SERVER_LIQUID_ONION__", - "BISQ_URL": "__EXTERNAL_DATA_SERVER_BISQ_URL__", - "BISQ_ONION": "__EXTERNAL_DATA_SERVER_BISQ_ONION__" + "LIQUID_ONION": "__EXTERNAL_DATA_SERVER_LIQUID_ONION__" }, "LIGHTNING": { "ENABLED": true, diff --git a/backend/src/__tests__/config.test.ts b/backend/src/__tests__/config.test.ts index 75661951e..a7f447941 100644 --- a/backend/src/__tests__/config.test.ts +++ b/backend/src/__tests__/config.test.ts @@ -107,8 +107,6 @@ describe('Mempool Backend Config', () => { expect(config.STATISTICS).toStrictEqual({ ENABLED: true, TX_PER_SECOND_SAMPLE_PERIOD: 150 }); - expect(config.BISQ).toStrictEqual({ ENABLED: false, DATA_PATH: '/bisq/statsnode-data/btc_mainnet/db' }); - expect(config.SOCKS5PROXY).toStrictEqual({ ENABLED: false, USE_ONION: true, @@ -122,9 +120,7 @@ describe('Mempool Backend Config', () => { MEMPOOL_API: 'https://mempool.space/api/v1', MEMPOOL_ONION: 'http://mempoolhqx4isw62xs7abwphsq7ldayuidyx2v2oethdhhj6mlo2r6ad.onion/api/v1', LIQUID_API: 'https://liquid.network/api/v1', - LIQUID_ONION: 'http://liquidmom47f6s3m53ebfxn47p76a6tlnxib3wp6deux7wuzotdr6cyd.onion/api/v1', - BISQ_URL: 'https://bisq.markets/api', - BISQ_ONION: 'http://bisqmktse2cabavbr2xjq7xw3h6g5ottemo5rolfcwt6aly6tp5fdryd.onion/api' + LIQUID_ONION: 'http://liquidmom47f6s3m53ebfxn47p76a6tlnxib3wp6deux7wuzotdr6cyd.onion/api/v1' }); expect(config.MAXMIND).toStrictEqual({ @@ -182,8 +178,6 @@ describe('Mempool Backend Config', () => { expect(config.STATISTICS).toStrictEqual(fixture.STATISTICS); - expect(config.BISQ).toStrictEqual(fixture.BISQ); - expect(config.SOCKS5PROXY).toStrictEqual(fixture.SOCKS5PROXY); expect(config.EXTERNAL_DATA_SERVER).toStrictEqual(fixture.EXTERNAL_DATA_SERVER); diff --git a/backend/src/api/audit.ts b/backend/src/api/audit.ts index 0ddfc2cc2..fb0d05baa 100644 --- a/backend/src/api/audit.ts +++ b/backend/src/api/audit.ts @@ -7,13 +7,14 @@ const PROPAGATION_MARGIN = 180; // in seconds, time since a transaction is first class Audit { auditBlock(transactions: MempoolTransactionExtended[], projectedBlocks: MempoolBlockWithTransactions[], mempool: { [txId: string]: MempoolTransactionExtended }, useAccelerations: boolean = false) - : { censored: string[], added: string[], fresh: string[], sigop: string[], fullrbf: string[], accelerated: string[], score: number, similarity: number } { + : { censored: string[], added: string[], prioritized: string[], fresh: string[], sigop: string[], fullrbf: string[], accelerated: string[], score: number, similarity: number } { if (!projectedBlocks?.[0]?.transactionIds || !mempool) { - return { censored: [], added: [], fresh: [], sigop: [], fullrbf: [], accelerated: [], score: 1, similarity: 1 }; + return { censored: [], added: [], prioritized: [], fresh: [], sigop: [], fullrbf: [], accelerated: [], score: 1, similarity: 1 }; } const matches: string[] = []; // present in both mined block and template const added: string[] = []; // present in mined block, not in template + const prioritized: string[] = [] // present in the mined block, not in the template, but further down in the mempool const fresh: string[] = []; // missing, but firstSeen or lastBoosted within PROPAGATION_MARGIN const rbf: string[] = []; // either missing or present, and either part of a full-rbf replacement, or a conflict with the mined block const accelerated: string[] = []; // prioritized by the mempool accelerator @@ -68,20 +69,27 @@ class Audit { // we can expect an honest miner to include 'displaced' transactions in place of recent arrivals and censored txs // these displaced transactions should occupy the first N weight units of the next projected block - let displacedWeightRemaining = displacedWeight; + let displacedWeightRemaining = displacedWeight + 4000; let index = 0; let lastFeeRate = Infinity; let failures = 0; - while (projectedBlocks[1] && index < projectedBlocks[1].transactionIds.length && failures < 500) { - const txid = projectedBlocks[1].transactionIds[index]; + let blockIndex = 1; + while (projectedBlocks[blockIndex] && failures < 500) { + if (index >= projectedBlocks[blockIndex].transactionIds.length) { + index = 0; + blockIndex++; + } + const txid = projectedBlocks[blockIndex].transactionIds[index]; const tx = mempool[txid]; if (tx) { const fits = (tx.weight - displacedWeightRemaining) < 4000; - const feeMatches = tx.effectiveFeePerVsize >= lastFeeRate; + // 0.005 margin of error for any remaining vsize rounding issues + const feeMatches = tx.effectiveFeePerVsize >= (lastFeeRate - 0.005); if (fits || feeMatches) { isDisplaced[txid] = true; if (fits) { - lastFeeRate = Math.min(lastFeeRate, tx.effectiveFeePerVsize); + // (tx.effectiveFeePerVsize * tx.vsize) / Math.ceil(tx.vsize) attempts to correct for vsize rounding in the simple non-CPFP case + lastFeeRate = Math.min(lastFeeRate, (tx.effectiveFeePerVsize * tx.vsize) / Math.ceil(tx.vsize)); } if (tx.firstSeen == null || (now - (tx?.firstSeen || 0)) > PROPAGATION_MARGIN) { displacedWeightRemaining -= tx.weight; @@ -106,7 +114,11 @@ class Audit { if (rbfCache.has(tx.txid)) { rbf.push(tx.txid); } else if (!isDisplaced[tx.txid]) { - added.push(tx.txid); + if (mempool[tx.txid]) { + prioritized.push(tx.txid); + } else { + added.push(tx.txid); + } } overflowWeight += tx.weight; } @@ -155,6 +167,7 @@ class Audit { return { censored: Object.keys(isCensored), added, + prioritized, fresh, sigop: [], fullrbf: rbf, diff --git a/backend/src/api/bisq/bisq.routes.ts b/backend/src/api/bisq/bisq.routes.ts deleted file mode 100644 index 8f002836f..000000000 --- a/backend/src/api/bisq/bisq.routes.ts +++ /dev/null @@ -1,381 +0,0 @@ -import { Application, Request, Response } from 'express'; -import config from '../../config'; -import { RequiredSpec } from '../../mempool.interfaces'; -import bisq from './bisq'; -import { MarketsApiError } from './interfaces'; -import marketsApi from './markets-api'; - -class BisqRoutes { - public initRoutes(app: Application) { - app - .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/stats', this.getBisqStats) - .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/tx/:txId', this.getBisqTransaction) - .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/block/:hash', this.getBisqBlock) - .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/blocks/tip/height', this.getBisqTip) - .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/blocks/:index/:length', this.getBisqBlocks) - .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/address/:address', this.getBisqAddress) - .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/txs/:index/:length', this.getBisqTransactions) - .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/markets/currencies', this.getBisqMarketCurrencies.bind(this)) - .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/markets/depth', this.getBisqMarketDepth.bind(this)) - .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/markets/hloc', this.getBisqMarketHloc.bind(this)) - .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/markets/markets', this.getBisqMarketMarkets.bind(this)) - .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/markets/offers', this.getBisqMarketOffers.bind(this)) - .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/markets/ticker', this.getBisqMarketTicker.bind(this)) - .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/markets/trades', this.getBisqMarketTrades.bind(this)) - .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/markets/volumes', this.getBisqMarketVolumes.bind(this)) - .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/markets/volumes/7d', this.getBisqMarketVolumes7d.bind(this)) - ; - } - - - private getBisqStats(req: Request, res: Response) { - const result = bisq.getStats(); - res.json(result); - } - - private getBisqTip(req: Request, res: Response) { - const result = bisq.getLatestBlockHeight(); - res.type('text/plain'); - res.send(result.toString()); - } - - private getBisqTransaction(req: Request, res: Response) { - const result = bisq.getTransaction(req.params.txId); - if (result) { - res.json(result); - } else { - res.status(404).send('Bisq transaction not found'); - } - } - - private getBisqTransactions(req: Request, res: Response) { - const types: string[] = []; - req.query.types = req.query.types || []; - if (!Array.isArray(req.query.types)) { - res.status(500).send('Types is not an array'); - return; - } - - for (const _type in req.query.types) { - if (typeof req.query.types[_type] === 'string') { - types.push(req.query.types[_type].toString()); - } - } - - const index = parseInt(req.params.index, 10) || 0; - const length = parseInt(req.params.length, 10) > 100 ? 100 : parseInt(req.params.length, 10) || 25; - const [transactions, count] = bisq.getTransactions(index, length, types); - res.header('X-Total-Count', count.toString()); - res.json(transactions); - } - - private getBisqBlock(req: Request, res: Response) { - const result = bisq.getBlock(req.params.hash); - if (result) { - res.json(result); - } else { - res.status(404).send('Bisq block not found'); - } - } - - private getBisqBlocks(req: Request, res: Response) { - const index = parseInt(req.params.index, 10) || 0; - const length = parseInt(req.params.length, 10) > 100 ? 100 : parseInt(req.params.length, 10) || 25; - const [transactions, count] = bisq.getBlocks(index, length); - res.header('X-Total-Count', count.toString()); - res.json(transactions); - } - - private getBisqAddress(req: Request, res: Response) { - const result = bisq.getAddress(req.params.address.substr(1)); - if (result) { - res.json(result); - } else { - res.status(404).send('Bisq address not found'); - } - } - - private getBisqMarketCurrencies(req: Request, res: Response) { - const constraints: RequiredSpec = { - 'type': { - required: false, - types: ['crypto', 'fiat', 'all'] - }, - }; - - const p = this.parseRequestParameters(req.query, constraints); - if (p.error) { - res.status(400).json(this.getBisqMarketErrorResponse(p.error)); - return; - } - - const result = marketsApi.getCurrencies(p.type); - if (result) { - res.json(result); - } else { - res.status(500).json(this.getBisqMarketErrorResponse('getBisqMarketCurrencies error')); - } - } - - private getBisqMarketDepth(req: Request, res: Response) { - const constraints: RequiredSpec = { - 'market': { - required: true, - types: ['@string'] - }, - }; - - const p = this.parseRequestParameters(req.query, constraints); - if (p.error) { - res.status(400).json(this.getBisqMarketErrorResponse(p.error)); - return; - } - - const result = marketsApi.getDepth(p.market); - if (result) { - res.json(result); - } else { - res.status(500).json(this.getBisqMarketErrorResponse('getBisqMarketDepth error')); - } - } - - private getBisqMarketMarkets(req: Request, res: Response) { - const result = marketsApi.getMarkets(); - if (result) { - res.json(result); - } else { - res.status(500).json(this.getBisqMarketErrorResponse('getBisqMarketMarkets error')); - } - } - - private getBisqMarketTrades(req: Request, res: Response) { - const constraints: RequiredSpec = { - 'market': { - required: true, - types: ['@string'] - }, - 'timestamp_from': { - required: false, - types: ['@number'] - }, - 'timestamp_to': { - required: false, - types: ['@number'] - }, - 'trade_id_to': { - required: false, - types: ['@string'] - }, - 'trade_id_from': { - required: false, - types: ['@string'] - }, - 'direction': { - required: false, - types: ['buy', 'sell'] - }, - 'limit': { - required: false, - types: ['@number'] - }, - 'sort': { - required: false, - types: ['asc', 'desc'] - } - }; - - const p = this.parseRequestParameters(req.query, constraints); - if (p.error) { - res.status(400).json(this.getBisqMarketErrorResponse(p.error)); - return; - } - - const result = marketsApi.getTrades(p.market, p.timestamp_from, - p.timestamp_to, p.trade_id_from, p.trade_id_to, p.direction, p.limit, p.sort); - if (result) { - res.json(result); - } else { - res.status(500).json(this.getBisqMarketErrorResponse('getBisqMarketTrades error')); - } - } - - private getBisqMarketOffers(req: Request, res: Response) { - const constraints: RequiredSpec = { - 'market': { - required: true, - types: ['@string'] - }, - 'direction': { - required: false, - types: ['buy', 'sell'] - }, - }; - - const p = this.parseRequestParameters(req.query, constraints); - if (p.error) { - res.status(400).json(this.getBisqMarketErrorResponse(p.error)); - return; - } - - const result = marketsApi.getOffers(p.market, p.direction); - if (result) { - res.json(result); - } else { - res.status(500).json(this.getBisqMarketErrorResponse('getBisqMarketOffers error')); - } - } - - private getBisqMarketVolumes(req: Request, res: Response) { - const constraints: RequiredSpec = { - 'market': { - required: false, - types: ['@string'] - }, - 'interval': { - required: false, - types: ['minute', 'half_hour', 'hour', 'half_day', 'day', 'week', 'month', 'year', 'auto'] - }, - 'timestamp_from': { - required: false, - types: ['@number'] - }, - 'timestamp_to': { - required: false, - types: ['@number'] - }, - 'milliseconds': { - required: false, - types: ['@boolean'] - }, - 'timestamp': { - required: false, - types: ['no', 'yes'] - }, - }; - - const p = this.parseRequestParameters(req.query, constraints); - if (p.error) { - res.status(400).json(this.getBisqMarketErrorResponse(p.error)); - return; - } - - const result = marketsApi.getVolumes(p.market, p.timestamp_from, p.timestamp_to, p.interval, p.milliseconds, p.timestamp); - if (result) { - res.json(result); - } else { - res.status(500).json(this.getBisqMarketErrorResponse('getBisqMarketVolumes error')); - } - } - - private getBisqMarketHloc(req: Request, res: Response) { - const constraints: RequiredSpec = { - 'market': { - required: true, - types: ['@string'] - }, - 'interval': { - required: false, - types: ['minute', 'half_hour', 'hour', 'half_day', 'day', 'week', 'month', 'year', 'auto'] - }, - 'timestamp_from': { - required: false, - types: ['@number'] - }, - 'timestamp_to': { - required: false, - types: ['@number'] - }, - 'milliseconds': { - required: false, - types: ['@boolean'] - }, - 'timestamp': { - required: false, - types: ['no', 'yes'] - }, - }; - - const p = this.parseRequestParameters(req.query, constraints); - if (p.error) { - res.status(400).json(this.getBisqMarketErrorResponse(p.error)); - return; - } - - const result = marketsApi.getHloc(p.market, p.interval, p.timestamp_from, p.timestamp_to, p.milliseconds, p.timestamp); - if (result) { - res.json(result); - } else { - res.status(500).json(this.getBisqMarketErrorResponse('getBisqMarketHloc error')); - } - } - - private getBisqMarketTicker(req: Request, res: Response) { - const constraints: RequiredSpec = { - 'market': { - required: false, - types: ['@string'] - }, - }; - - const p = this.parseRequestParameters(req.query, constraints); - if (p.error) { - res.status(400).json(this.getBisqMarketErrorResponse(p.error)); - return; - } - - const result = marketsApi.getTicker(p.market); - if (result) { - res.json(result); - } else { - res.status(500).json(this.getBisqMarketErrorResponse('getBisqMarketTicker error')); - } - } - - private getBisqMarketVolumes7d(req: Request, res: Response) { - const result = marketsApi.getVolumesByTime(604800); - if (result) { - res.json(result); - } else { - res.status(500).json(this.getBisqMarketErrorResponse('getBisqMarketVolumes7d error')); - } - } - - private parseRequestParameters(requestParams: object, params: RequiredSpec): { [name: string]: any; } { - const final = {}; - for (const i in params) { - if (params.hasOwnProperty(i)) { - if (params[i].required && requestParams[i] === undefined) { - return { error: i + ' parameter missing'}; - } - if (typeof requestParams[i] === 'string') { - const str = (requestParams[i] || '').toString().toLowerCase(); - if (params[i].types.indexOf('@number') > -1) { - const number = parseInt((str).toString(), 10); - final[i] = number; - } else if (params[i].types.indexOf('@string') > -1) { - final[i] = str; - } else if (params[i].types.indexOf('@boolean') > -1) { - final[i] = str === 'true' || str === 'yes'; - } else if (params[i].types.indexOf(str) > -1) { - final[i] = str; - } else { - return { error: i + ' parameter invalid'}; - } - } else if (typeof requestParams[i] === 'number') { - final[i] = requestParams[i]; - } - } - } - return final; - } - - private getBisqMarketErrorResponse(message: string): MarketsApiError { - return { - 'success': 0, - 'error': message - }; - } - -} - -export default new BisqRoutes; diff --git a/backend/src/api/bisq/bisq.ts b/backend/src/api/bisq/bisq.ts deleted file mode 100644 index 4171284bb..000000000 --- a/backend/src/api/bisq/bisq.ts +++ /dev/null @@ -1,359 +0,0 @@ -import config from '../../config'; -import * as fs from 'fs'; -import axios, { AxiosResponse } from 'axios'; -import * as http from 'http'; -import * as https from 'https'; -import { SocksProxyAgent } from 'socks-proxy-agent'; -import { BisqBlocks, BisqBlock, BisqTransaction, BisqStats, BisqTrade } from './interfaces'; -import { Common } from '../common'; -import { BlockExtended } from '../../mempool.interfaces'; -import backendInfo from '../backend-info'; -import logger from '../../logger'; - -class Bisq { - private static BLOCKS_JSON_FILE_PATH = config.BISQ.DATA_PATH + '/json/all/blocks.json'; - private latestBlockHeight = 0; - private blocks: BisqBlock[] = []; - private allBlocks: BisqBlock[] = []; - private transactions: BisqTransaction[] = []; - private transactionIndex: { [txId: string]: BisqTransaction } = {}; - private blockIndex: { [hash: string]: BisqBlock } = {}; - private addressIndex: { [address: string]: BisqTransaction[] } = {}; - private stats: BisqStats = { - minted: 0, - burnt: 0, - addresses: 0, - unspent_txos: 0, - spent_txos: 0, - }; - private price: number = 0; - private priceUpdateCallbackFunction: ((price: number) => void) | undefined; - private topDirectoryWatcher: fs.FSWatcher | undefined; - private subdirectoryWatcher: fs.FSWatcher | undefined; - - constructor() {} - - startBisqService(): void { - try { - this.checkForBisqDataFolder(); - } catch (e) { - logger.info('Retrying to start bisq service in 3 minutes'); - setTimeout(this.startBisqService.bind(this), 180000); - return; - } - this.loadBisqDumpFile(); - setInterval(this.updatePrice.bind(this), 1000 * 60 * 60); - this.updatePrice(); - this.startTopDirectoryWatcher(); - this.startSubDirectoryWatcher(); - } - - handleNewBitcoinBlock(block: BlockExtended): void { - if (block.height - 10 > this.latestBlockHeight && this.latestBlockHeight !== 0) { - logger.warn(`Bitcoin block height (#${block.height}) has diverged from the latest Bisq block height (#${this.latestBlockHeight}). Restarting watchers...`); - this.startTopDirectoryWatcher(); - this.startSubDirectoryWatcher(); - } - } - - getTransaction(txId: string): BisqTransaction | undefined { - return this.transactionIndex[txId]; - } - - getTransactions(start: number, length: number, types: string[]): [BisqTransaction[], number] { - let transactions = this.transactions; - if (types.length) { - transactions = transactions.filter((tx) => types.indexOf(tx.txType) > -1); - } - return [transactions.slice(start, length + start), transactions.length]; - } - - getBlock(hash: string): BisqBlock | undefined { - return this.blockIndex[hash]; - } - - getAddress(hash: string): BisqTransaction[] { - return this.addressIndex[hash]; - } - - getBlocks(start: number, length: number): [BisqBlock[], number] { - return [this.blocks.slice(start, length + start), this.blocks.length]; - } - - getStats(): BisqStats { - return this.stats; - } - - setPriceCallbackFunction(fn: (price: number) => void) { - this.priceUpdateCallbackFunction = fn; - } - - getLatestBlockHeight(): number { - return this.latestBlockHeight; - } - - private checkForBisqDataFolder() { - if (!fs.existsSync(Bisq.BLOCKS_JSON_FILE_PATH)) { - logger.warn(Bisq.BLOCKS_JSON_FILE_PATH + ` doesn't exist. Make sure Bisq is running and the config is correct before starting the server.`); - throw new Error(`Cannot load BISQ ${Bisq.BLOCKS_JSON_FILE_PATH} file`); - } - } - - private startTopDirectoryWatcher() { - if (this.topDirectoryWatcher) { - this.topDirectoryWatcher.close(); - } - let fsWait: NodeJS.Timeout | null = null; - this.topDirectoryWatcher = fs.watch(config.BISQ.DATA_PATH + '/json', () => { - if (fsWait) { - clearTimeout(fsWait); - } - if (this.subdirectoryWatcher) { - this.subdirectoryWatcher.close(); - } - fsWait = setTimeout(() => { - logger.debug(`Bisq restart detected. Resetting both watchers in 3 minutes.`); - setTimeout(() => { - this.startTopDirectoryWatcher(); - this.startSubDirectoryWatcher(); - this.loadBisqDumpFile(); - }, 180000); - }, 15000); - }); - } - - private startSubDirectoryWatcher() { - if (this.subdirectoryWatcher) { - this.subdirectoryWatcher.close(); - } - if (!fs.existsSync(Bisq.BLOCKS_JSON_FILE_PATH)) { - logger.warn(Bisq.BLOCKS_JSON_FILE_PATH + ` doesn't exist. Trying to restart sub directory watcher again in 3 minutes.`); - setTimeout(() => this.startSubDirectoryWatcher(), 180000); - return; - } - let fsWait: NodeJS.Timeout | null = null; - this.subdirectoryWatcher = fs.watch(config.BISQ.DATA_PATH + '/json/all', () => { - if (fsWait) { - clearTimeout(fsWait); - } - fsWait = setTimeout(() => { - logger.debug(`Change detected in the Bisq data folder.`); - this.loadBisqDumpFile(); - }, 2000); - }); - } - private async updatePrice() { - type axiosOptions = { - headers: { - 'User-Agent': string - }; - timeout: number; - httpAgent?: http.Agent; - httpsAgent?: https.Agent; - } - const setDelay = (secs: number = 1): Promise => new Promise(resolve => setTimeout(() => resolve(), secs * 1000)); - const BISQ_URL = (config.SOCKS5PROXY.ENABLED === true) && (config.SOCKS5PROXY.USE_ONION === true) ? config.EXTERNAL_DATA_SERVER.BISQ_ONION : config.EXTERNAL_DATA_SERVER.BISQ_URL; - const isHTTP = (new URL(BISQ_URL).protocol.split(':')[0] === 'http') ? true : false; - const axiosOptions: axiosOptions = { - headers: { - 'User-Agent': (config.MEMPOOL.USER_AGENT === 'mempool') ? `mempool/v${backendInfo.getBackendInfo().version}` : `${config.MEMPOOL.USER_AGENT}` - }, - timeout: config.SOCKS5PROXY.ENABLED ? 30000 : 10000 - }; - let retry = 0; - - while(retry < config.MEMPOOL.EXTERNAL_MAX_RETRY) { - try { - if (config.SOCKS5PROXY.ENABLED) { - const socksOptions: any = { - agentOptions: { - keepAlive: true, - }, - hostname: config.SOCKS5PROXY.HOST, - port: config.SOCKS5PROXY.PORT - }; - - if (config.SOCKS5PROXY.USERNAME && config.SOCKS5PROXY.PASSWORD) { - socksOptions.username = config.SOCKS5PROXY.USERNAME; - socksOptions.password = config.SOCKS5PROXY.PASSWORD; - } else { - // Retry with different tor circuits https://stackoverflow.com/a/64960234 - socksOptions.username = `circuit${retry}`; - } - - // Handle proxy agent for onion addresses - if (isHTTP) { - axiosOptions.httpAgent = new SocksProxyAgent(socksOptions); - } else { - axiosOptions.httpsAgent = new SocksProxyAgent(socksOptions); - } - } - - const data: AxiosResponse = await axios.get(`${BISQ_URL}/trades/?market=bsq_btc`, axiosOptions); - if (data.statusText === 'error' || !data.data) { - throw new Error(`Could not fetch data from Bisq market, Error: ${data.status}`); - } - const prices: number[] = []; - data.data.forEach((trade) => { - prices.push(parseFloat(trade.price) * 100000000); - }); - prices.sort((a, b) => a - b); - this.price = Common.median(prices); - if (this.priceUpdateCallbackFunction) { - this.priceUpdateCallbackFunction(this.price); - } - logger.debug('Successfully updated Bisq market price'); - break; - } catch (e) { - logger.err('Error updating Bisq market price: ' + (e instanceof Error ? e.message : e)); - await setDelay(config.MEMPOOL.EXTERNAL_RETRY_INTERVAL); - retry++; - } - } - } - - private async loadBisqDumpFile(): Promise { - this.allBlocks = []; - try { - await this.loadData(); - this.buildIndex(); - this.calculateStats(); - } catch (e) { - logger.info('Cannot load bisq dump file because: ' + (e instanceof Error ? e.message : e)); - } - } - - private buildIndex() { - const start = new Date().getTime(); - this.transactions = []; - this.transactionIndex = {}; - this.addressIndex = {}; - - this.allBlocks.forEach((block) => { - /* Build block index */ - if (!this.blockIndex[block.hash]) { - this.blockIndex[block.hash] = block; - } - - /* Build transactions index */ - block.txs.forEach((tx) => { - this.transactions.push(tx); - this.transactionIndex[tx.id] = tx; - }); - }); - - /* Build address index */ - this.transactions.forEach((tx) => { - tx.inputs.forEach((input) => { - if (!this.addressIndex[input.address]) { - this.addressIndex[input.address] = []; - } - if (this.addressIndex[input.address].indexOf(tx) === -1) { - this.addressIndex[input.address].push(tx); - } - }); - tx.outputs.forEach((output) => { - if (!this.addressIndex[output.address]) { - this.addressIndex[output.address] = []; - } - if (this.addressIndex[output.address].indexOf(tx) === -1) { - this.addressIndex[output.address].push(tx); - } - }); - }); - - const time = new Date().getTime() - start; - logger.debug('Bisq data index rebuilt in ' + time + ' ms'); - } - - private calculateStats() { - let minted = 0; - let burned = 0; - let unspent = 0; - let spent = 0; - - this.transactions.forEach((tx) => { - tx.outputs.forEach((output) => { - if (output.opReturn) { - return; - } - if (output.txOutputType === 'GENESIS_OUTPUT' || output.txOutputType === 'ISSUANCE_CANDIDATE_OUTPUT' && output.isVerified) { - minted += output.bsqAmount; - } - if (output.isUnspent) { - unspent++; - } else { - spent++; - } - }); - burned += tx['burntFee']; - }); - - this.stats = { - addresses: Object.keys(this.addressIndex).length, - minted: minted / 100, - burnt: burned / 100, - spent_txos: spent, - unspent_txos: unspent, - }; - } - - private async loadData(): Promise { - if (!fs.existsSync(Bisq.BLOCKS_JSON_FILE_PATH)) { - throw new Error(Bisq.BLOCKS_JSON_FILE_PATH + ` doesn't exist`); - } - - const readline = require('readline'); - const events = require('events'); - - const rl = readline.createInterface({ - input: fs.createReadStream(Bisq.BLOCKS_JSON_FILE_PATH), - crlfDelay: Infinity - }); - - let blockBuffer = ''; - let readingBlock = false; - let lineCount = 1; - const start = new Date().getTime(); - - logger.debug('Processing Bisq data dump...'); - - rl.on('line', (line) => { - if (lineCount === 2) { - line = line.replace(' "chainHeight": ', ''); - this.latestBlockHeight = parseInt(line, 10); - } - - if (line === ' {') { - readingBlock = true; - } else if (line === ' },') { - blockBuffer += '}'; - try { - const block: BisqBlock = JSON.parse(blockBuffer); - this.allBlocks.push(block); - readingBlock = false; - blockBuffer = ''; - } catch (e) { - logger.debug(blockBuffer); - throw Error(`Unable to parse Bisq data dump at line ${lineCount}` + (e instanceof Error ? e.message : e)); - } - } - - if (readingBlock === true) { - blockBuffer += line; - } - - ++lineCount; - }); - - await events.once(rl, 'close'); - - this.allBlocks.reverse(); - this.blocks = this.allBlocks.filter((block) => block.txs.length > 0); - - const time = new Date().getTime() - start; - logger.debug('Bisq dump processed in ' + time + ' ms'); - } -} - -export default new Bisq(); diff --git a/backend/src/api/bisq/interfaces.ts b/backend/src/api/bisq/interfaces.ts deleted file mode 100644 index eb10d2fa7..000000000 --- a/backend/src/api/bisq/interfaces.ts +++ /dev/null @@ -1,258 +0,0 @@ - -export interface BisqBlocks { - chainHeight: number; - blocks: BisqBlock[]; -} - -export interface BisqBlock { - height: number; - time: number; - hash: string; - previousBlockHash: string; - txs: BisqTransaction[]; -} - -export interface BisqTransaction { - txVersion: string; - id: string; - blockHeight: number; - blockHash: string; - time: number; - inputs: BisqInput[]; - outputs: BisqOutput[]; - txType: string; - txTypeDisplayString: string; - burntFee: number; - invalidatedBsq: number; - unlockBlockHeight: number; -} - -export interface BisqStats { - minted: number; - burnt: number; - addresses: number; - unspent_txos: number; - spent_txos: number; -} - -interface BisqInput { - spendingTxOutputIndex: number; - spendingTxId: string; - bsqAmount: number; - isVerified: boolean; - address: string; - time: number; -} - -interface BisqOutput { - txVersion: string; - txId: string; - index: number; - bsqAmount: number; - btcAmount: number; - height: number; - isVerified: boolean; - burntFee: number; - invalidatedBsq: number; - address: string; - scriptPubKey: BisqScriptPubKey; - time: any; - txType: string; - txTypeDisplayString: string; - txOutputType: string; - txOutputTypeDisplayString: string; - lockTime: number; - isUnspent: boolean; - spentInfo: SpentInfo; - opReturn?: string; -} - -interface BisqScriptPubKey { - addresses: string[]; - asm: string; - hex: string; - reqSigs?: number; - type: string; -} - -interface SpentInfo { - height: number; - inputIndex: number; - txId: string; -} - -export interface BisqTrade { - direction: string; - price: string; - amount: string; - volume: string; - payment_method: string; - trade_id: string; - trade_date: number; - market?: string; -} - -export interface Currencies { [txid: string]: Currency; } - -export interface Currency { - code: string; - name: string; - precision: number; - - _type: string; -} - -export interface Depth { [market: string]: Market; } - -interface Market { - 'buys': string[]; - 'sells': string[]; -} - -export interface HighLowOpenClose { - period_start: number | string; - open: string; - high: string; - low: string; - close: string; - volume_left: string; - volume_right: string; - avg: string; -} - -export interface Markets { [txid: string]: Pair; } - -interface Pair { - pair: string; - lname: string; - rname: string; - lsymbol: string; - rsymbol: string; - lprecision: number; - rprecision: number; - ltype: string; - rtype: string; - name: string; -} - -export interface Offers { [market: string]: OffersMarket; } - -interface OffersMarket { - buys: Offer[] | null; - sells: Offer[] | null; -} - -export interface OffersData { - direction: string; - currencyCode: string; - minAmount: number; - amount: number; - price: number; - date: number; - useMarketBasedPrice: boolean; - marketPriceMargin: number; - paymentMethod: string; - id: string; - currencyPair: string; - primaryMarketDirection: string; - priceDisplayString: string; - primaryMarketAmountDisplayString: string; - primaryMarketMinAmountDisplayString: string; - primaryMarketVolumeDisplayString: string; - primaryMarketMinVolumeDisplayString: string; - primaryMarketPrice: number; - primaryMarketAmount: number; - primaryMarketMinAmount: number; - primaryMarketVolume: number; - primaryMarketMinVolume: number; -} - -export interface Offer { - offer_id: string; - offer_date: number; - direction: string; - min_amount: string; - amount: string; - price: string; - volume: string; - payment_method: string; - offer_fee_txid: any; -} - -export interface Tickers { [market: string]: Ticker | null; } - -export interface Ticker { - last: string; - high: string; - low: string; - volume_left: string; - volume_right: string; - buy: string | null; - sell: string | null; -} - -export interface Trade { - direction: string; - price: string; - amount: string; - volume: string; - payment_method: string; - trade_id: string; - trade_date: number; -} - -export interface TradesData { - currency: string; - direction: string; - tradePrice: number; - tradeAmount: number; - tradeDate: number; - paymentMethod: string; - offerDate: number; - useMarketBasedPrice: boolean; - marketPriceMargin: number; - offerAmount: number; - offerMinAmount: number; - offerId: string; - depositTxId?: string; - currencyPair: string; - primaryMarketDirection: string; - primaryMarketTradePrice: number; - primaryMarketTradeAmount: number; - primaryMarketTradeVolume: number; - - _market: string; - _tradePriceStr: string; - _tradeAmountStr: string; - _tradeVolumeStr: string; - _offerAmountStr: string; - _tradePrice: number; - _tradeAmount: number; - _tradeVolume: number; - _offerAmount: number; -} - -export interface MarketVolume { - period_start: number; - num_trades: number; - volume: string; -} - -export interface MarketsApiError { - success: number; - error: string; -} - -export type Interval = 'minute' | 'half_hour' | 'hour' | 'half_day' | 'day' | 'week' | 'month' | 'year' | 'auto'; - -export interface SummarizedIntervals { [market: string]: SummarizedInterval; } -export interface SummarizedInterval { - 'period_start': number; - 'open': number; - 'close': number; - 'high': number; - 'low': number; - 'avg': number; - 'volume_right': number; - 'volume_left': number; -} diff --git a/backend/src/api/bisq/markets-api.ts b/backend/src/api/bisq/markets-api.ts deleted file mode 100644 index 1b5b93059..000000000 --- a/backend/src/api/bisq/markets-api.ts +++ /dev/null @@ -1,679 +0,0 @@ -import { Currencies, OffersData, TradesData, Depth, Currency, Interval, HighLowOpenClose, - Markets, Offers, Offer, BisqTrade, MarketVolume, Tickers, Ticker, SummarizedIntervals, SummarizedInterval } from './interfaces'; - -const strtotime = require('./strtotime'); - -class BisqMarketsApi { - private cryptoCurrencyData: Currency[] = []; - private fiatCurrencyData: Currency[] = []; - private activeCryptoCurrencyData: Currency[] = []; - private activeFiatCurrencyData: Currency[] = []; - private offersData: OffersData[] = []; - private tradesData: TradesData[] = []; - private fiatCurrenciesIndexed: { [code: string]: true } = {}; - private allCurrenciesIndexed: { [code: string]: Currency } = {}; - private tradeDataByMarket: { [market: string]: TradesData[] } = {}; - private tickersCache: Ticker | Tickers | null = null; - - constructor() { } - - setOffersData(offers: OffersData[]) { - this.offersData = offers; - } - - setTradesData(trades: TradesData[]) { - this.tradesData = trades; - this.tradeDataByMarket = {}; - - this.tradesData.forEach((trade) => { - trade._market = trade.currencyPair.toLowerCase().replace('/', '_'); - if (!this.tradeDataByMarket[trade._market]) { - this.tradeDataByMarket[trade._market] = []; - } - this.tradeDataByMarket[trade._market].push(trade); - }); - } - - setCurrencyData(cryptoCurrency: Currency[], fiatCurrency: Currency[], activeCryptoCurrency: Currency[], activeFiatCurrency: Currency[]) { - this.cryptoCurrencyData = cryptoCurrency, - this.fiatCurrencyData = fiatCurrency, - this.activeCryptoCurrencyData = activeCryptoCurrency, - this.activeFiatCurrencyData = activeFiatCurrency; - - this.fiatCurrenciesIndexed = {}; - this.allCurrenciesIndexed = {}; - - this.fiatCurrencyData.forEach((currency) => { - currency._type = 'fiat'; - this.fiatCurrenciesIndexed[currency.code] = true; - this.allCurrenciesIndexed[currency.code] = currency; - }); - this.cryptoCurrencyData.forEach((currency) => { - currency._type = 'crypto'; - this.allCurrenciesIndexed[currency.code] = currency; - }); - } - - updateCache() { - this.tickersCache = null; - this.tickersCache = this.getTicker(); - } - - getCurrencies( - type: 'crypto' | 'fiat' | 'active' | 'all' = 'all', - ): Currencies { - let currencies: Currency[]; - - switch (type) { - case 'fiat': - currencies = this.fiatCurrencyData; - break; - case 'crypto': - currencies = this.cryptoCurrencyData; - break; - case 'active': - currencies = this.activeCryptoCurrencyData.concat(this.activeFiatCurrencyData); - break; - case 'all': - default: - currencies = this.cryptoCurrencyData.concat(this.fiatCurrencyData); - } - const result = {}; - currencies.forEach((currency) => { - result[currency.code] = currency; - }); - return result; - } - - getDepth( - market: string, - ): Depth { - const currencyPair = market.replace('_', '/').toUpperCase(); - - const buys = this.offersData - .filter((offer) => offer.currencyPair === currencyPair && offer.primaryMarketDirection === 'BUY') - .map((offer) => offer.price) - .sort((a, b) => b - a) - .map((price) => this.intToBtc(price)); - - const sells = this.offersData - .filter((offer) => offer.currencyPair === currencyPair && offer.primaryMarketDirection === 'SELL') - .map((offer) => offer.price) - .sort((a, b) => a - b) - .map((price) => this.intToBtc(price)); - - const result = {}; - result[market] = { - 'buys': buys, - 'sells': sells, - }; - return result; - } - - getOffers( - market: string, - direction?: 'buy' | 'sell', - ): Offers { - const currencyPair = market.replace('_', '/').toUpperCase(); - - let buys: Offer[] | null = null; - let sells: Offer[] | null = null; - - if (!direction || direction === 'buy') { - buys = this.offersData - .filter((offer) => offer.currencyPair === currencyPair && offer.primaryMarketDirection === 'BUY') - .sort((a, b) => b.price - a.price) - .map((offer) => this.offerDataToOffer(offer, market)); - } - - if (!direction || direction === 'sell') { - sells = this.offersData - .filter((offer) => offer.currencyPair === currencyPair && offer.primaryMarketDirection === 'SELL') - .sort((a, b) => a.price - b.price) - .map((offer) => this.offerDataToOffer(offer, market)); - } - - const result: Offers = {}; - result[market] = { - 'buys': buys, - 'sells': sells, - }; - return result; - } - - getMarkets(): Markets { - const allCurrencies = this.getCurrencies(); - const activeCurrencies = this.getCurrencies('active'); - const markets = {}; - - for (const currency of Object.keys(activeCurrencies)) { - if (allCurrencies[currency].code === 'BTC') { - continue; - } - - const isFiat = allCurrencies[currency]._type === 'fiat'; - const pmarketname = allCurrencies['BTC']['name']; - - const lsymbol = isFiat ? 'BTC' : currency; - const rsymbol = isFiat ? currency : 'BTC'; - const lname = isFiat ? pmarketname : allCurrencies[currency].name; - const rname = isFiat ? allCurrencies[currency].name : pmarketname; - const ltype = isFiat ? 'crypto' : allCurrencies[currency]._type; - const rtype = isFiat ? 'fiat' : 'crypto'; - const lprecision = 8; - const rprecision = isFiat ? 2 : 8; - const pair = lsymbol.toLowerCase() + '_' + rsymbol.toLowerCase(); - - markets[pair] = { - 'pair': pair, - 'lname': lname, - 'rname': rname, - 'lsymbol': lsymbol, - 'rsymbol': rsymbol, - 'lprecision': lprecision, - 'rprecision': rprecision, - 'ltype': ltype, - 'rtype': rtype, - 'name': lname + '/' + rname, - }; - } - - return markets; - } - - getTrades( - market: string, - timestamp_from?: number, - timestamp_to?: number, - trade_id_from?: string, - trade_id_to?: string, - direction?: 'buy' | 'sell', - limit: number = 100, - sort: 'asc' | 'desc' = 'desc', - ): BisqTrade[] { - limit = Math.min(limit, 2000); - const _market = market === 'all' ? undefined : market; - - if (!timestamp_from) { - timestamp_from = new Date('2016-01-01').getTime() / 1000; - } - if (!timestamp_to) { - timestamp_to = new Date().getTime() / 1000; - } - - const matches = this.getTradesByCriteria(_market, timestamp_to, timestamp_from, - trade_id_to, trade_id_from, direction, sort, limit, false); - - if (sort === 'asc') { - matches.sort((a, b) => a.tradeDate - b.tradeDate); - } else { - matches.sort((a, b) => b.tradeDate - a.tradeDate); - } - - return matches.map((trade) => { - const bsqTrade: BisqTrade = { - direction: trade.primaryMarketDirection, - price: trade._tradePriceStr, - amount: trade._tradeAmountStr, - volume: trade._tradeVolumeStr, - payment_method: trade.paymentMethod, - trade_id: trade.offerId, - trade_date: trade.tradeDate, - }; - if (market === 'all') { - bsqTrade.market = trade._market; - } - return bsqTrade; - }); - } - - getVolumes( - market?: string, - timestamp_from?: number, - timestamp_to?: number, - interval: Interval = 'auto', - milliseconds?: boolean, - timestamp: 'no' | 'yes' = 'yes', - ): MarketVolume[] { - if (milliseconds) { - timestamp_from = timestamp_from ? timestamp_from / 1000 : timestamp_from; - timestamp_to = timestamp_to ? timestamp_to / 1000 : timestamp_to; - } - if (!timestamp_from) { - timestamp_from = new Date('2016-01-01').getTime() / 1000; - } - if (!timestamp_to) { - timestamp_to = new Date().getTime() / 1000; - } - - const trades = this.getTradesByCriteria(market, timestamp_to, timestamp_from, - undefined, undefined, undefined, 'asc', Number.MAX_SAFE_INTEGER); - - if (interval === 'auto') { - const range = timestamp_to - timestamp_from; - interval = this.getIntervalFromRange(range); - } - - const intervals: any = {}; - const marketVolumes: MarketVolume[] = []; - - for (const trade of trades) { - const traded_at = trade['tradeDate'] / 1000; - const interval_start = this.intervalStart(traded_at, interval); - - if (!intervals[interval_start]) { - intervals[interval_start] = { - 'volume': 0, - 'num_trades': 0, - }; - } - - const period = intervals[interval_start]; - period['period_start'] = interval_start; - period['volume'] += this.fiatCurrenciesIndexed[trade.currency] ? trade._tradeAmount : trade._tradeVolume; - period['num_trades']++; - } - - for (const p in intervals) { - if (intervals.hasOwnProperty(p)) { - const period = intervals[p]; - marketVolumes.push({ - period_start: timestamp === 'no' ? new Date(period['period_start'] * 1000).toISOString() : period['period_start'], - num_trades: period['num_trades'], - volume: this.intToBtc(period['volume']), - }); - } - } - - return marketVolumes; - } - - getTicker( - market?: string, - ): Tickers | Ticker | null { - if (market) { - return this.getTickerFromMarket(market); - } - - if (this.tickersCache) { - return this.tickersCache; - } - - const allMarkets = this.getMarkets(); - const tickers = {}; - for (const m in allMarkets) { - if (allMarkets.hasOwnProperty(m)) { - tickers[allMarkets[m].pair] = this.getTickerFromMarket(allMarkets[m].pair); - } - } - - return tickers; - } - - getTickerFromMarket(market: string): Ticker | null { - let ticker: Ticker; - const timestamp_from = strtotime('-24 hour'); - const timestamp_to = new Date().getTime() / 1000; - const trades = this.getTradesByCriteria(market, timestamp_to, timestamp_from, - undefined, undefined, undefined, 'asc', Number.MAX_SAFE_INTEGER); - - const periods: SummarizedInterval[] = Object.values(this.getTradesSummarized(trades, timestamp_from)); - - const allCurrencies = this.getCurrencies(); - const currencyRight = allCurrencies[market.split('_')[1].toUpperCase()]; - - if (periods[0]) { - ticker = { - 'last': this.intToBtc(periods[0].close), - 'high': this.intToBtc(periods[0].high), - 'low': this.intToBtc(periods[0].low), - 'volume_left': this.intToBtc(periods[0].volume_left), - 'volume_right': this.intToBtc(periods[0].volume_right), - 'buy': null, - 'sell': null, - }; - } else { - const lastTrade = this.tradeDataByMarket[market]; - if (!lastTrade) { - return null; - } - const tradePrice = lastTrade[0].primaryMarketTradePrice * Math.pow(10, 8 - currencyRight.precision); - - const lastTradePrice = this.intToBtc(tradePrice); - ticker = { - 'last': lastTradePrice, - 'high': lastTradePrice, - 'low': lastTradePrice, - 'volume_left': '0', - 'volume_right': '0', - 'buy': null, - 'sell': null, - }; - } - - const timestampFromMilli = timestamp_from * 1000; - const timestampToMilli = timestamp_to * 1000; - - const currencyPair = market.replace('_', '/').toUpperCase(); - const offersData = this.offersData.slice().sort((a, b) => a.price - b.price); - - const buy = offersData.find((offer) => offer.currencyPair === currencyPair - && offer.primaryMarketDirection === 'BUY' - && offer.date >= timestampFromMilli - && offer.date <= timestampToMilli - ); - const sell = offersData.find((offer) => offer.currencyPair === currencyPair - && offer.primaryMarketDirection === 'SELL' - && offer.date >= timestampFromMilli - && offer.date <= timestampToMilli - ); - - if (buy) { - ticker.buy = this.intToBtc(buy.primaryMarketPrice * Math.pow(10, 8 - currencyRight.precision)); - } - if (sell) { - ticker.sell = this.intToBtc(sell.primaryMarketPrice * Math.pow(10, 8 - currencyRight.precision)); - } - - return ticker; - } - - getHloc( - market: string, - interval: Interval = 'auto', - timestamp_from?: number, - timestamp_to?: number, - milliseconds?: boolean, - timestamp: 'no' | 'yes' = 'yes', - ): HighLowOpenClose[] { - if (milliseconds) { - timestamp_from = timestamp_from ? timestamp_from / 1000 : timestamp_from; - timestamp_to = timestamp_to ? timestamp_to / 1000 : timestamp_to; - } - if (!timestamp_from) { - timestamp_from = new Date('2016-01-01').getTime() / 1000; - } - if (!timestamp_to) { - timestamp_to = new Date().getTime() / 1000; - } - - const trades = this.getTradesByCriteria(market, timestamp_to, timestamp_from, - undefined, undefined, undefined, 'asc', Number.MAX_SAFE_INTEGER); - - if (interval === 'auto') { - const range = timestamp_to - timestamp_from; - interval = this.getIntervalFromRange(range); - } - - const intervals = this.getTradesSummarized(trades, timestamp_from, interval); - - const hloc: HighLowOpenClose[] = []; - - for (const p in intervals) { - if (intervals.hasOwnProperty(p)) { - const period = intervals[p]; - hloc.push({ - period_start: timestamp === 'no' ? new Date(period['period_start'] * 1000).toISOString() : period['period_start'], - open: this.intToBtc(period['open']), - close: this.intToBtc(period['close']), - high: this.intToBtc(period['high']), - low: this.intToBtc(period['low']), - avg: this.intToBtc(period['avg']), - volume_right: this.intToBtc(period['volume_right']), - volume_left: this.intToBtc(period['volume_left']), - }); - } - } - - return hloc; - } - - private getIntervalFromRange(range: number): Interval { - // two days range loads minute data - if (range <= 3600) { - // up to one hour range loads minutely data - return 'minute'; - } else if (range <= 1 * 24 * 3600) { - // up to one day range loads half-hourly data - return 'half_hour'; - } else if (range <= 3 * 24 * 3600) { - // up to 3 day range loads hourly data - return 'hour'; - } else if (range <= 7 * 24 * 3600) { - // up to 7 day range loads half-daily data - return 'half_day'; - } else if (range <= 60 * 24 * 3600) { - // up to 2 month range loads daily data - return 'day'; - } else if (range <= 12 * 31 * 24 * 3600) { - // up to one year range loads weekly data - return 'week'; - } else if (range <= 12 * 31 * 24 * 3600) { - // up to 5 year range loads monthly data - return 'month'; - } else { - // greater range loads yearly data - return 'year'; - } - } - - getVolumesByTime(time: number): MarketVolume[] { - const timestamp_from = new Date().getTime() / 1000 - time; - const timestamp_to = new Date().getTime() / 1000; - - const trades = this.getTradesByCriteria(undefined, timestamp_to, timestamp_from, - undefined, undefined, undefined, 'asc', Number.MAX_SAFE_INTEGER); - - const markets: any = {}; - - for (const trade of trades) { - if (!markets[trade._market]) { - markets[trade._market] = { - 'volume': 0, - 'num_trades': 0, - }; - } - - markets[trade._market]['volume'] += this.fiatCurrenciesIndexed[trade.currency] ? trade._tradeAmount : trade._tradeVolume; - markets[trade._market]['num_trades']++; - } - - return markets; - } - - private getTradesSummarized(trades: TradesData[], timestamp_from: number, interval?: string): SummarizedIntervals { - const intervals: any = {}; - const intervals_prices: any = {}; - - for (const trade of trades) { - const traded_at = trade.tradeDate / 1000; - const interval_start = !interval ? timestamp_from : this.intervalStart(traded_at, interval); - - if (!intervals[interval_start]) { - intervals[interval_start] = { - 'open': 0, - 'close': 0, - 'high': 0, - 'low': 0, - 'avg': 0, - 'volume_right': 0, - 'volume_left': 0, - }; - intervals_prices[interval_start] = []; - } - const period = intervals[interval_start]; - const price = trade._tradePrice; - - if (!intervals_prices[interval_start]['leftvol']) { - intervals_prices[interval_start]['leftvol'] = []; - } - if (!intervals_prices[interval_start]['rightvol']) { - intervals_prices[interval_start]['rightvol'] = []; - } - - intervals_prices[interval_start]['leftvol'].push(trade._tradeAmount); - intervals_prices[interval_start]['rightvol'].push(trade._tradeVolume); - - if (price) { - const plow = period['low']; - period['period_start'] = interval_start; - period['open'] = period['open'] || price; - period['close'] = price; - period['high'] = price > period['high'] ? price : period['high']; - period['low'] = (plow && price > plow) ? period['low'] : price; - period['avg'] = intervals_prices[interval_start]['rightvol'].reduce((p: number, c: number) => c + p, 0) - / intervals_prices[interval_start]['leftvol'].reduce((c: number, p: number) => c + p, 0) * 100000000; - period['volume_left'] += trade._tradeAmount; - period['volume_right'] += trade._tradeVolume; - } - } - return intervals; - } - - private getTradesByCriteria( - market: string | undefined, - timestamp_to: number, - timestamp_from: number, - trade_id_to: string | undefined, - trade_id_from: string | undefined, - direction: 'buy' | 'sell' | undefined, - sort: string, - limit: number, - integerAmounts: boolean = true, - ): TradesData[] { - let trade_id_from_ts: number | null = null; - let trade_id_to_ts: number | null = null; - const allCurrencies = this.getCurrencies(); - - const timestampFromMilli = timestamp_from * 1000; - const timestampToMilli = timestamp_to * 1000; - - // note: the offer_id_from/to depends on iterating over trades in - // descending chronological order. - const tradesDataSorted = this.tradesData.slice(); - if (sort === 'asc') { - tradesDataSorted.reverse(); - } - - let matches: TradesData[] = []; - for (const trade of tradesDataSorted) { - if (trade_id_from === trade.offerId) { - trade_id_from_ts = trade.tradeDate; - } - if (trade_id_to === trade.offerId) { - trade_id_to_ts = trade.tradeDate; - } - if (trade_id_to && trade_id_to_ts === null) { - continue; - } - if (trade_id_from && trade_id_from_ts != null && trade_id_from_ts !== trade.tradeDate) { - continue; - } - if (market && market !== trade._market) { - continue; - } - if (timestampFromMilli && timestampFromMilli > trade.tradeDate) { - continue; - } - if (timestampToMilli && timestampToMilli < trade.tradeDate) { - continue; - } - if (direction && direction !== trade.direction.toLowerCase()) { - continue; - } - - // Filter out bogus trades with BTC/BTC or XXX/XXX market. - // See github issue: https://github.com/bitsquare/bitsquare/issues/883 - const currencyPairs = trade.currencyPair.split('/'); - if (currencyPairs[0] === currencyPairs[1]) { - continue; - } - - const currencyLeft = allCurrencies[currencyPairs[0]]; - const currencyRight = allCurrencies[currencyPairs[1]]; - - if (!currencyLeft || !currencyRight) { - continue; - } - - const tradePrice = trade.primaryMarketTradePrice * Math.pow(10, 8 - currencyRight.precision); - const tradeAmount = trade.primaryMarketTradeAmount * Math.pow(10, 8 - currencyLeft.precision); - const tradeVolume = trade.primaryMarketTradeVolume * Math.pow(10, 8 - currencyRight.precision); - - if (integerAmounts) { - trade._tradePrice = tradePrice; - trade._tradeAmount = tradeAmount; - trade._tradeVolume = tradeVolume; - trade._offerAmount = trade.offerAmount; - } else { - trade._tradePriceStr = this.intToBtc(tradePrice); - trade._tradeAmountStr = this.intToBtc(tradeAmount); - trade._tradeVolumeStr = this.intToBtc(tradeVolume); - trade._offerAmountStr = this.intToBtc(trade.offerAmount); - } - - matches.push(trade); - - if (matches.length >= limit) { - break; - } - } - - if ((trade_id_from && !trade_id_from_ts) || (trade_id_to && !trade_id_to_ts)) { - matches = []; - } - return matches; - } - - private intervalStart(ts: number, interval: string): number { - switch (interval) { - case 'minute': - return (ts - (ts % 60)); - case '10_minute': - return (ts - (ts % 600)); - case 'half_hour': - return (ts - (ts % 1800)); - case 'hour': - return (ts - (ts % 3600)); - case 'half_day': - return (ts - (ts % (3600 * 12))); - case 'day': - return strtotime('midnight today', ts); - case 'week': - return strtotime('midnight sunday last week', ts); - case 'month': - return strtotime('midnight first day of this month', ts); - case 'year': - return strtotime('midnight first day of january', ts); - default: - throw new Error('Unsupported interval'); - } -} - - private offerDataToOffer(offer: OffersData, market: string): Offer { - const currencyPairs = market.split('_'); - const currencyRight = this.allCurrenciesIndexed[currencyPairs[1].toUpperCase()]; - const currencyLeft = this.allCurrenciesIndexed[currencyPairs[0].toUpperCase()]; - const price = offer['primaryMarketPrice'] * Math.pow( 10, 8 - currencyRight['precision']); - const amount = offer['primaryMarketAmount'] * Math.pow( 10, 8 - currencyLeft['precision']); - const volume = offer['primaryMarketVolume'] * Math.pow( 10, 8 - currencyRight['precision']); - - return { - offer_id: offer.id, - offer_date: offer.date, - direction: offer.primaryMarketDirection, - min_amount: this.intToBtc(offer.minAmount), - amount: this.intToBtc(amount), - price: this.intToBtc(price), - volume: this.intToBtc(volume), - payment_method: offer.paymentMethod, - offer_fee_txid: null, - }; - } - - private intToBtc(val: number): string { - return (val / 100000000).toFixed(8); - } -} - -export default new BisqMarketsApi(); diff --git a/backend/src/api/bisq/markets.ts b/backend/src/api/bisq/markets.ts deleted file mode 100644 index 08f40d772..000000000 --- a/backend/src/api/bisq/markets.ts +++ /dev/null @@ -1,137 +0,0 @@ -import config from '../../config'; -import * as fs from 'fs'; -import { OffersData as OffersData, TradesData, Currency } from './interfaces'; -import bisqMarket from './markets-api'; -import logger from '../../logger'; - -class Bisq { - private static FOLDER_WATCH_CHANGE_DETECTION_DEBOUNCE = 4000; - private static MARKET_JSON_PATH = config.BISQ.DATA_PATH; - private static MARKET_JSON_FILE_PATHS = { - activeCryptoCurrency: '/active_crypto_currency_list.json', - activeFiatCurrency: '/active_fiat_currency_list.json', - cryptoCurrency: '/crypto_currency_list.json', - fiatCurrency: '/fiat_currency_list.json', - offers: '/offers_statistics.json', - trades: '/trade_statistics.json', - }; - - private cryptoCurrencyLastMtime = new Date('2016-01-01'); - private fiatCurrencyLastMtime = new Date('2016-01-01'); - private offersLastMtime = new Date('2016-01-01'); - private tradesLastMtime = new Date('2016-01-01'); - - private subdirectoryWatcher: fs.FSWatcher | undefined; - - constructor() {} - - startBisqService(): void { - try { - this.checkForBisqDataFolder(); - } catch (e) { - logger.info('Retrying to start bisq service (markets) in 3 minutes'); - setTimeout(this.startBisqService.bind(this), 180000); - return; - } - this.loadBisqDumpFile(); - this.startBisqDirectoryWatcher(); - } - - private checkForBisqDataFolder() { - if (!fs.existsSync(Bisq.MARKET_JSON_PATH + Bisq.MARKET_JSON_FILE_PATHS.cryptoCurrency)) { - logger.err(Bisq.MARKET_JSON_PATH + Bisq.MARKET_JSON_FILE_PATHS.cryptoCurrency + ` doesn't exist. Make sure Bisq is running and the config is correct before starting the server.`); - throw new Error(`Cannot load BISQ ${Bisq.MARKET_JSON_FILE_PATHS.cryptoCurrency} file`); - } - } - - private startBisqDirectoryWatcher() { - if (this.subdirectoryWatcher) { - this.subdirectoryWatcher.close(); - } - if (!fs.existsSync(Bisq.MARKET_JSON_PATH + Bisq.MARKET_JSON_FILE_PATHS.cryptoCurrency)) { - logger.warn(Bisq.MARKET_JSON_PATH + Bisq.MARKET_JSON_FILE_PATHS.cryptoCurrency + ` doesn't exist. Trying to restart sub directory watcher again in 3 minutes.`); - setTimeout(() => this.startBisqDirectoryWatcher(), 180000); - return; - } - let fsWait: NodeJS.Timeout | null = null; - this.subdirectoryWatcher = fs.watch(Bisq.MARKET_JSON_PATH, () => { - if (fsWait) { - clearTimeout(fsWait); - } - fsWait = setTimeout(() => { - logger.debug(`Change detected in the Bisq market data folder.`); - this.loadBisqDumpFile(); - }, Bisq.FOLDER_WATCH_CHANGE_DETECTION_DEBOUNCE); - }); - } - - private async loadBisqDumpFile(): Promise { - const start = new Date().getTime(); - try { - let marketsDataUpdated = false; - const cryptoMtime = this.getFileMtime(Bisq.MARKET_JSON_FILE_PATHS.cryptoCurrency); - const fiatMtime = this.getFileMtime(Bisq.MARKET_JSON_FILE_PATHS.fiatCurrency); - if (cryptoMtime > this.cryptoCurrencyLastMtime || fiatMtime > this.fiatCurrencyLastMtime) { - const cryptoCurrencyData = await this.loadData(Bisq.MARKET_JSON_FILE_PATHS.cryptoCurrency); - const fiatCurrencyData = await this.loadData(Bisq.MARKET_JSON_FILE_PATHS.fiatCurrency); - const activeCryptoCurrencyData = await this.loadData(Bisq.MARKET_JSON_FILE_PATHS.activeCryptoCurrency); - const activeFiatCurrencyData = await this.loadData(Bisq.MARKET_JSON_FILE_PATHS.activeFiatCurrency); - logger.debug('Updating Bisq Market Currency Data'); - bisqMarket.setCurrencyData(cryptoCurrencyData, fiatCurrencyData, activeCryptoCurrencyData, activeFiatCurrencyData); - if (cryptoMtime > this.cryptoCurrencyLastMtime) { - this.cryptoCurrencyLastMtime = cryptoMtime; - } - if (fiatMtime > this.fiatCurrencyLastMtime) { - this.fiatCurrencyLastMtime = fiatMtime; - } - marketsDataUpdated = true; - } - const offersMtime = this.getFileMtime(Bisq.MARKET_JSON_FILE_PATHS.offers); - if (offersMtime > this.offersLastMtime) { - const offersData = await this.loadData(Bisq.MARKET_JSON_FILE_PATHS.offers); - logger.debug('Updating Bisq Market Offers Data'); - bisqMarket.setOffersData(offersData); - this.offersLastMtime = offersMtime; - marketsDataUpdated = true; - } - const tradesMtime = this.getFileMtime(Bisq.MARKET_JSON_FILE_PATHS.trades); - if (tradesMtime > this.tradesLastMtime) { - const tradesData = await this.loadData(Bisq.MARKET_JSON_FILE_PATHS.trades); - logger.debug('Updating Bisq Market Trades Data'); - bisqMarket.setTradesData(tradesData); - this.tradesLastMtime = tradesMtime; - marketsDataUpdated = true; - } - if (marketsDataUpdated) { - bisqMarket.updateCache(); - const time = new Date().getTime() - start; - logger.debug('Bisq market data updated in ' + time + ' ms'); - } - } catch (e) { - logger.err('loadBisqMarketDataDumpFile() error.' + (e instanceof Error ? e.message : e)); - } - } - - private getFileMtime(path: string): Date { - const stats = fs.statSync(Bisq.MARKET_JSON_PATH + path); - return stats.mtime; - } - - private loadData(path: string): Promise { - return new Promise((resolve, reject) => { - fs.readFile(Bisq.MARKET_JSON_PATH + path, 'utf8', (err, data) => { - if (err) { - reject(err); - } - try { - const parsedData = JSON.parse(data); - resolve(parsedData); - } catch (e) { - reject('JSON parse error (' + path + ')'); - } - }); - }); - } -} - -export default new Bisq(); diff --git a/backend/src/api/bisq/strtotime.ts b/backend/src/api/bisq/strtotime.ts deleted file mode 100644 index 912f00ec9..000000000 --- a/backend/src/api/bisq/strtotime.ts +++ /dev/null @@ -1,1375 +0,0 @@ -// @ts-nocheck - -/* -Copyright (c) 2007-2016 Kevin van Zonneveld (https://kvz.io) -and Contributors (https://locutus.io/authors) - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -/* - -https://github.com/locutusjs/locutus/blob/master/src/php/datetime/strtotime.js - -*/ - -const reSpace = '[ \\t]+' -const reSpaceOpt = '[ \\t]*' -const reMeridian = '(?:([ap])\\.?m\\.?([\\t ]|$))' -const reHour24 = '(2[0-4]|[01]?[0-9])' -const reHour24lz = '([01][0-9]|2[0-4])' -const reHour12 = '(0?[1-9]|1[0-2])' -const reMinute = '([0-5]?[0-9])' -const reMinutelz = '([0-5][0-9])' -const reSecond = '(60|[0-5]?[0-9])' -const reSecondlz = '(60|[0-5][0-9])' -const reFrac = '(?:\\.([0-9]+))' - -const reDayfull = 'sunday|monday|tuesday|wednesday|thursday|friday|saturday' -const reDayabbr = 'sun|mon|tue|wed|thu|fri|sat' -const reDaytext = reDayfull + '|' + reDayabbr + '|weekdays?' - -const reReltextnumber = 'first|second|third|fourth|fifth|sixth|seventh|eighth?|ninth|tenth|eleventh|twelfth' -const reReltexttext = 'next|last|previous|this' -const reReltextunit = '(?:second|sec|minute|min|hour|day|fortnight|forthnight|month|year)s?|weeks|' + reDaytext - -const reYear = '([0-9]{1,4})' -const reYear2 = '([0-9]{2})' -const reYear4 = '([0-9]{4})' -const reYear4withSign = '([+-]?[0-9]{4})' -const reMonth = '(1[0-2]|0?[0-9])' -const reMonthlz = '(0[0-9]|1[0-2])' -const reDay = '(?:(3[01]|[0-2]?[0-9])(?:st|nd|rd|th)?)' -const reDaylz = '(0[0-9]|[1-2][0-9]|3[01])' - -const reMonthFull = 'january|february|march|april|may|june|july|august|september|october|november|december' -const reMonthAbbr = 'jan|feb|mar|apr|may|jun|jul|aug|sept?|oct|nov|dec' -const reMonthroman = 'i[vx]|vi{0,3}|xi{0,2}|i{1,3}' -const reMonthText = '(' + reMonthFull + '|' + reMonthAbbr + '|' + reMonthroman + ')' - -const reTzCorrection = '((?:GMT)?([+-])' + reHour24 + ':?' + reMinute + '?)' -const reTzAbbr = '\\(?([a-zA-Z]{1,6})\\)?' -const reDayOfYear = '(00[1-9]|0[1-9][0-9]|[12][0-9][0-9]|3[0-5][0-9]|36[0-6])' -const reWeekOfYear = '(0[1-9]|[1-4][0-9]|5[0-3])' - -const reDateNoYear = reMonthText + '[ .\\t-]*' + reDay + '[,.stndrh\\t ]*' - -function processMeridian (hour, meridian) { - meridian = meridian && meridian.toLowerCase() - - switch (meridian) { - case 'a': - hour += hour === 12 ? -12 : 0 - break - case 'p': - hour += hour !== 12 ? 12 : 0 - break - } - - return hour -} - -function processYear (yearStr) { - let year = +yearStr - - if (yearStr.length < 4 && year < 100) { - year += year < 70 ? 2000 : 1900 - } - - return year -} - -function lookupMonth (monthStr) { - return { - jan: 0, - january: 0, - i: 0, - feb: 1, - february: 1, - ii: 1, - mar: 2, - march: 2, - iii: 2, - apr: 3, - april: 3, - iv: 3, - may: 4, - v: 4, - jun: 5, - june: 5, - vi: 5, - jul: 6, - july: 6, - vii: 6, - aug: 7, - august: 7, - viii: 7, - sep: 8, - sept: 8, - september: 8, - ix: 8, - oct: 9, - october: 9, - x: 9, - nov: 10, - november: 10, - xi: 10, - dec: 11, - december: 11, - xii: 11 - }[monthStr.toLowerCase()] -} - -function lookupWeekday (dayStr, desiredSundayNumber = 0) { - const dayNumbers = { - mon: 1, - monday: 1, - tue: 2, - tuesday: 2, - wed: 3, - wednesday: 3, - thu: 4, - thursday: 4, - fri: 5, - friday: 5, - sat: 6, - saturday: 6, - sun: 0, - sunday: 0 - } - - return dayNumbers[dayStr.toLowerCase()] || desiredSundayNumber -} - -function lookupRelative (relText) { - const relativeNumbers = { - last: -1, - previous: -1, - this: 0, - first: 1, - next: 1, - second: 2, - third: 3, - fourth: 4, - fifth: 5, - sixth: 6, - seventh: 7, - eight: 8, - eighth: 8, - ninth: 9, - tenth: 10, - eleventh: 11, - twelfth: 12 - } - - const relativeBehavior = { - this: 1 - } - - const relTextLower = relText.toLowerCase() - - return { - amount: relativeNumbers[relTextLower], - behavior: relativeBehavior[relTextLower] || 0 - } -} - -function processTzCorrection (tzOffset, oldValue) { - const reTzCorrectionLoose = /(?:GMT)?([+-])(\d+)(:?)(\d{0,2})/i - tzOffset = tzOffset && tzOffset.match(reTzCorrectionLoose) - - if (!tzOffset) { - return oldValue - } - - const sign = tzOffset[1] === '-' ? -1 : 1 - let hours = +tzOffset[2] - let minutes = +tzOffset[4] - - if (!tzOffset[4] && !tzOffset[3]) { - minutes = Math.floor(hours % 100) - hours = Math.floor(hours / 100) - } - - // timezone offset in seconds - return sign * (hours * 60 + minutes) * 60 -} - -// tz abbrevation : tz offset in seconds -const tzAbbrOffsets = { - acdt: 37800, - acst: 34200, - addt: -7200, - adt: -10800, - aedt: 39600, - aest: 36000, - ahdt: -32400, - ahst: -36000, - akdt: -28800, - akst: -32400, - amt: -13840, - apt: -10800, - ast: -14400, - awdt: 32400, - awst: 28800, - awt: -10800, - bdst: 7200, - bdt: -36000, - bmt: -14309, - bst: 3600, - cast: 34200, - cat: 7200, - cddt: -14400, - cdt: -18000, - cemt: 10800, - cest: 7200, - cet: 3600, - cmt: -15408, - cpt: -18000, - cst: -21600, - cwt: -18000, - chst: 36000, - dmt: -1521, - eat: 10800, - eddt: -10800, - edt: -14400, - eest: 10800, - eet: 7200, - emt: -26248, - ept: -14400, - est: -18000, - ewt: -14400, - ffmt: -14660, - fmt: -4056, - gdt: 39600, - gmt: 0, - gst: 36000, - hdt: -34200, - hkst: 32400, - hkt: 28800, - hmt: -19776, - hpt: -34200, - hst: -36000, - hwt: -34200, - iddt: 14400, - idt: 10800, - imt: 25025, - ist: 7200, - jdt: 36000, - jmt: 8440, - jst: 32400, - kdt: 36000, - kmt: 5736, - kst: 30600, - lst: 9394, - mddt: -18000, - mdst: 16279, - mdt: -21600, - mest: 7200, - met: 3600, - mmt: 9017, - mpt: -21600, - msd: 14400, - msk: 10800, - mst: -25200, - mwt: -21600, - nddt: -5400, - ndt: -9052, - npt: -9000, - nst: -12600, - nwt: -9000, - nzdt: 46800, - nzmt: 41400, - nzst: 43200, - pddt: -21600, - pdt: -25200, - pkst: 21600, - pkt: 18000, - plmt: 25590, - pmt: -13236, - ppmt: -17340, - ppt: -25200, - pst: -28800, - pwt: -25200, - qmt: -18840, - rmt: 5794, - sast: 7200, - sdmt: -16800, - sjmt: -20173, - smt: -13884, - sst: -39600, - tbmt: 10751, - tmt: 12344, - uct: 0, - utc: 0, - wast: 7200, - wat: 3600, - wemt: 7200, - west: 3600, - wet: 0, - wib: 25200, - wita: 28800, - wit: 32400, - wmt: 5040, - yddt: -25200, - ydt: -28800, - ypt: -28800, - yst: -32400, - ywt: -28800, - a: 3600, - b: 7200, - c: 10800, - d: 14400, - e: 18000, - f: 21600, - g: 25200, - h: 28800, - i: 32400, - k: 36000, - l: 39600, - m: 43200, - n: -3600, - o: -7200, - p: -10800, - q: -14400, - r: -18000, - s: -21600, - t: -25200, - u: -28800, - v: -32400, - w: -36000, - x: -39600, - y: -43200, - z: 0 -} - -const formats = { - yesterday: { - regex: /^yesterday/i, - name: 'yesterday', - callback () { - this.rd -= 1 - return this.resetTime() - } - }, - - now: { - regex: /^now/i, - name: 'now' - // do nothing - }, - - noon: { - regex: /^noon/i, - name: 'noon', - callback () { - return this.resetTime() && this.time(12, 0, 0, 0) - } - }, - - midnightOrToday: { - regex: /^(midnight|today)/i, - name: 'midnight | today', - callback () { - return this.resetTime() - } - }, - - tomorrow: { - regex: /^tomorrow/i, - name: 'tomorrow', - callback () { - this.rd += 1 - return this.resetTime() - } - }, - - timestamp: { - regex: /^@(-?\d+)/i, - name: 'timestamp', - callback (match, timestamp) { - this.rs += +timestamp - this.y = 1970 - this.m = 0 - this.d = 1 - this.dates = 0 - - return this.resetTime() && this.zone(0) - } - }, - - firstOrLastDay: { - regex: /^(first|last) day of/i, - name: 'firstdayof | lastdayof', - callback (match, day) { - if (day.toLowerCase() === 'first') { - this.firstOrLastDayOfMonth = 1 - } else { - this.firstOrLastDayOfMonth = -1 - } - } - }, - - backOrFrontOf: { - regex: RegExp('^(back|front) of ' + reHour24 + reSpaceOpt + reMeridian + '?', 'i'), - name: 'backof | frontof', - callback (match, side, hours, meridian) { - const back = side.toLowerCase() === 'back' - let hour = +hours - let minute = 15 - - if (!back) { - hour -= 1 - minute = 45 - } - - hour = processMeridian(hour, meridian) - - return this.resetTime() && this.time(hour, minute, 0, 0) - } - }, - - weekdayOf: { - regex: RegExp('^(' + reReltextnumber + '|' + reReltexttext + ')' + reSpace + '(' + reDayfull + '|' + reDayabbr + ')' + reSpace + 'of', 'i'), - name: 'weekdayof' - // todo - }, - - mssqltime: { - regex: RegExp('^' + reHour12 + ':' + reMinutelz + ':' + reSecondlz + '[:.]([0-9]+)' + reMeridian, 'i'), - name: 'mssqltime', - callback (match, hour, minute, second, frac, meridian) { - return this.time(processMeridian(+hour, meridian), +minute, +second, +frac.substr(0, 3)) - } - }, - - timeLong12: { - regex: RegExp('^' + reHour12 + '[:.]' + reMinute + '[:.]' + reSecondlz + reSpaceOpt + reMeridian, 'i'), - name: 'timelong12', - callback (match, hour, minute, second, meridian) { - return this.time(processMeridian(+hour, meridian), +minute, +second, 0) - } - }, - - timeShort12: { - regex: RegExp('^' + reHour12 + '[:.]' + reMinutelz + reSpaceOpt + reMeridian, 'i'), - name: 'timeshort12', - callback (match, hour, minute, meridian) { - return this.time(processMeridian(+hour, meridian), +minute, 0, 0) - } - }, - - timeTiny12: { - regex: RegExp('^' + reHour12 + reSpaceOpt + reMeridian, 'i'), - name: 'timetiny12', - callback (match, hour, meridian) { - return this.time(processMeridian(+hour, meridian), 0, 0, 0) - } - }, - - soap: { - regex: RegExp('^' + reYear4 + '-' + reMonthlz + '-' + reDaylz + 'T' + reHour24lz + ':' + reMinutelz + ':' + reSecondlz + reFrac + reTzCorrection + '?', 'i'), - name: 'soap', - callback (match, year, month, day, hour, minute, second, frac, tzCorrection) { - return this.ymd(+year, month - 1, +day) && - this.time(+hour, +minute, +second, +frac.substr(0, 3)) && - this.zone(processTzCorrection(tzCorrection)) - } - }, - - wddx: { - regex: RegExp('^' + reYear4 + '-' + reMonth + '-' + reDay + 'T' + reHour24 + ':' + reMinute + ':' + reSecond), - name: 'wddx', - callback (match, year, month, day, hour, minute, second) { - return this.ymd(+year, month - 1, +day) && this.time(+hour, +minute, +second, 0) - } - }, - - exif: { - regex: RegExp('^' + reYear4 + ':' + reMonthlz + ':' + reDaylz + ' ' + reHour24lz + ':' + reMinutelz + ':' + reSecondlz, 'i'), - name: 'exif', - callback (match, year, month, day, hour, minute, second) { - return this.ymd(+year, month - 1, +day) && this.time(+hour, +minute, +second, 0) - } - }, - - xmlRpc: { - regex: RegExp('^' + reYear4 + reMonthlz + reDaylz + 'T' + reHour24 + ':' + reMinutelz + ':' + reSecondlz), - name: 'xmlrpc', - callback (match, year, month, day, hour, minute, second) { - return this.ymd(+year, month - 1, +day) && this.time(+hour, +minute, +second, 0) - } - }, - - xmlRpcNoColon: { - regex: RegExp('^' + reYear4 + reMonthlz + reDaylz + '[Tt]' + reHour24 + reMinutelz + reSecondlz), - name: 'xmlrpcnocolon', - callback (match, year, month, day, hour, minute, second) { - return this.ymd(+year, month - 1, +day) && this.time(+hour, +minute, +second, 0) - } - }, - - clf: { - regex: RegExp('^' + reDay + '/(' + reMonthAbbr + ')/' + reYear4 + ':' + reHour24lz + ':' + reMinutelz + ':' + reSecondlz + reSpace + reTzCorrection, 'i'), - name: 'clf', - callback (match, day, month, year, hour, minute, second, tzCorrection) { - return this.ymd(+year, lookupMonth(month), +day) && - this.time(+hour, +minute, +second, 0) && - this.zone(processTzCorrection(tzCorrection)) - } - }, - - iso8601long: { - regex: RegExp('^t?' + reHour24 + '[:.]' + reMinute + '[:.]' + reSecond + reFrac, 'i'), - name: 'iso8601long', - callback (match, hour, minute, second, frac) { - return this.time(+hour, +minute, +second, +frac.substr(0, 3)) - } - }, - - dateTextual: { - regex: RegExp('^' + reMonthText + '[ .\\t-]*' + reDay + '[,.stndrh\\t ]+' + reYear, 'i'), - name: 'datetextual', - callback (match, month, day, year) { - return this.ymd(processYear(year), lookupMonth(month), +day) - } - }, - - pointedDate4: { - regex: RegExp('^' + reDay + '[.\\t-]' + reMonth + '[.-]' + reYear4), - name: 'pointeddate4', - callback (match, day, month, year) { - return this.ymd(+year, month - 1, +day) - } - }, - - pointedDate2: { - regex: RegExp('^' + reDay + '[.\\t]' + reMonth + '\\.' + reYear2), - name: 'pointeddate2', - callback (match, day, month, year) { - return this.ymd(processYear(year), month - 1, +day) - } - }, - - timeLong24: { - regex: RegExp('^t?' + reHour24 + '[:.]' + reMinute + '[:.]' + reSecond), - name: 'timelong24', - callback (match, hour, minute, second) { - return this.time(+hour, +minute, +second, 0) - } - }, - - dateNoColon: { - regex: RegExp('^' + reYear4 + reMonthlz + reDaylz), - name: 'datenocolon', - callback (match, year, month, day) { - return this.ymd(+year, month - 1, +day) - } - }, - - pgydotd: { - regex: RegExp('^' + reYear4 + '\\.?' + reDayOfYear), - name: 'pgydotd', - callback (match, year, day) { - return this.ymd(+year, 0, +day) - } - }, - - timeShort24: { - regex: RegExp('^t?' + reHour24 + '[:.]' + reMinute, 'i'), - name: 'timeshort24', - callback (match, hour, minute) { - return this.time(+hour, +minute, 0, 0) - } - }, - - iso8601noColon: { - regex: RegExp('^t?' + reHour24lz + reMinutelz + reSecondlz, 'i'), - name: 'iso8601nocolon', - callback (match, hour, minute, second) { - return this.time(+hour, +minute, +second, 0) - } - }, - - iso8601dateSlash: { - // eventhough the trailing slash is optional in PHP - // here it's mandatory and inputs without the slash - // are handled by dateslash - regex: RegExp('^' + reYear4 + '/' + reMonthlz + '/' + reDaylz + '/'), - name: 'iso8601dateslash', - callback (match, year, month, day) { - return this.ymd(+year, month - 1, +day) - } - }, - - dateSlash: { - regex: RegExp('^' + reYear4 + '/' + reMonth + '/' + reDay), - name: 'dateslash', - callback (match, year, month, day) { - return this.ymd(+year, month - 1, +day) - } - }, - - american: { - regex: RegExp('^' + reMonth + '/' + reDay + '/' + reYear), - name: 'american', - callback (match, month, day, year) { - return this.ymd(processYear(year), month - 1, +day) - } - }, - - americanShort: { - regex: RegExp('^' + reMonth + '/' + reDay), - name: 'americanshort', - callback (match, month, day) { - return this.ymd(this.y, month - 1, +day) - } - }, - - gnuDateShortOrIso8601date2: { - // iso8601date2 is complete subset of gnudateshort - regex: RegExp('^' + reYear + '-' + reMonth + '-' + reDay), - name: 'gnudateshort | iso8601date2', - callback (match, year, month, day) { - return this.ymd(processYear(year), month - 1, +day) - } - }, - - iso8601date4: { - regex: RegExp('^' + reYear4withSign + '-' + reMonthlz + '-' + reDaylz), - name: 'iso8601date4', - callback (match, year, month, day) { - return this.ymd(+year, month - 1, +day) - } - }, - - gnuNoColon: { - regex: RegExp('^t?' + reHour24lz + reMinutelz, 'i'), - name: 'gnunocolon', - callback (match, hour, minute) { - // this rule is a special case - // if time was already set once by any preceding rule, it sets the captured value as year - switch (this.times) { - case 0: - return this.time(+hour, +minute, 0, this.f) - case 1: - this.y = hour * 100 + +minute - this.times++ - - return true - default: - return false - } - } - }, - - gnuDateShorter: { - regex: RegExp('^' + reYear4 + '-' + reMonth), - name: 'gnudateshorter', - callback (match, year, month) { - return this.ymd(+year, month - 1, 1) - } - }, - - pgTextReverse: { - // note: allowed years are from 32-9999 - // years below 32 should be treated as days in datefull - regex: RegExp('^' + '(\\d{3,4}|[4-9]\\d|3[2-9])-(' + reMonthAbbr + ')-' + reDaylz, 'i'), - name: 'pgtextreverse', - callback (match, year, month, day) { - return this.ymd(processYear(year), lookupMonth(month), +day) - } - }, - - dateFull: { - regex: RegExp('^' + reDay + '[ \\t.-]*' + reMonthText + '[ \\t.-]*' + reYear, 'i'), - name: 'datefull', - callback (match, day, month, year) { - return this.ymd(processYear(year), lookupMonth(month), +day) - } - }, - - dateNoDay: { - regex: RegExp('^' + reMonthText + '[ .\\t-]*' + reYear4, 'i'), - name: 'datenoday', - callback (match, month, year) { - return this.ymd(+year, lookupMonth(month), 1) - } - }, - - dateNoDayRev: { - regex: RegExp('^' + reYear4 + '[ .\\t-]*' + reMonthText, 'i'), - name: 'datenodayrev', - callback (match, year, month) { - return this.ymd(+year, lookupMonth(month), 1) - } - }, - - pgTextShort: { - regex: RegExp('^(' + reMonthAbbr + ')-' + reDaylz + '-' + reYear, 'i'), - name: 'pgtextshort', - callback (match, month, day, year) { - return this.ymd(processYear(year), lookupMonth(month), +day) - } - }, - - dateNoYear: { - regex: RegExp('^' + reDateNoYear, 'i'), - name: 'datenoyear', - callback (match, month, day) { - return this.ymd(this.y, lookupMonth(month), +day) - } - }, - - dateNoYearRev: { - regex: RegExp('^' + reDay + '[ .\\t-]*' + reMonthText, 'i'), - name: 'datenoyearrev', - callback (match, day, month) { - return this.ymd(this.y, lookupMonth(month), +day) - } - }, - - isoWeekDay: { - regex: RegExp('^' + reYear4 + '-?W' + reWeekOfYear + '(?:-?([0-7]))?'), - name: 'isoweekday | isoweek', - callback (match, year, week, day) { - day = day ? +day : 1 - - if (!this.ymd(+year, 0, 1)) { - return false - } - - // get day of week for Jan 1st - let dayOfWeek = new Date(this.y, this.m, this.d).getDay() - - // and use the day to figure out the offset for day 1 of week 1 - dayOfWeek = 0 - (dayOfWeek > 4 ? dayOfWeek - 7 : dayOfWeek) - - this.rd += dayOfWeek + ((week - 1) * 7) + day - } - }, - - relativeText: { - regex: RegExp('^(' + reReltextnumber + '|' + reReltexttext + ')' + reSpace + '(' + reReltextunit + ')', 'i'), - name: 'relativetext', - callback (match, relValue, relUnit) { - // todo: implement handling of 'this time-unit' - // eslint-disable-next-line no-unused-vars - const { amount, behavior } = lookupRelative(relValue) - - switch (relUnit.toLowerCase()) { - case 'sec': - case 'secs': - case 'second': - case 'seconds': - this.rs += amount - break - case 'min': - case 'mins': - case 'minute': - case 'minutes': - this.ri += amount - break - case 'hour': - case 'hours': - this.rh += amount - break - case 'day': - case 'days': - this.rd += amount - break - case 'fortnight': - case 'fortnights': - case 'forthnight': - case 'forthnights': - this.rd += amount * 14 - break - case 'week': - case 'weeks': - this.rd += amount * 7 - break - case 'month': - case 'months': - this.rm += amount - break - case 'year': - case 'years': - this.ry += amount - break - case 'mon': case 'monday': - case 'tue': case 'tuesday': - case 'wed': case 'wednesday': - case 'thu': case 'thursday': - case 'fri': case 'friday': - case 'sat': case 'saturday': - case 'sun': case 'sunday': - this.resetTime() - this.weekday = lookupWeekday(relUnit, 7) - this.weekdayBehavior = 1 - this.rd += (amount > 0 ? amount - 1 : amount) * 7 - break - case 'weekday': - case 'weekdays': - // todo - break - } - } - }, - - relative: { - regex: RegExp('^([+-]*)[ \\t]*(\\d+)' + reSpaceOpt + '(' + reReltextunit + '|week)', 'i'), - name: 'relative', - callback (match, signs, relValue, relUnit) { - const minuses = signs.replace(/[^-]/g, '').length - - const amount = +relValue * Math.pow(-1, minuses) - - switch (relUnit.toLowerCase()) { - case 'sec': - case 'secs': - case 'second': - case 'seconds': - this.rs += amount - break - case 'min': - case 'mins': - case 'minute': - case 'minutes': - this.ri += amount - break - case 'hour': - case 'hours': - this.rh += amount - break - case 'day': - case 'days': - this.rd += amount - break - case 'fortnight': - case 'fortnights': - case 'forthnight': - case 'forthnights': - this.rd += amount * 14 - break - case 'week': - case 'weeks': - this.rd += amount * 7 - break - case 'month': - case 'months': - this.rm += amount - break - case 'year': - case 'years': - this.ry += amount - break - case 'mon': case 'monday': - case 'tue': case 'tuesday': - case 'wed': case 'wednesday': - case 'thu': case 'thursday': - case 'fri': case 'friday': - case 'sat': case 'saturday': - case 'sun': case 'sunday': - this.resetTime() - this.weekday = lookupWeekday(relUnit, 7) - this.weekdayBehavior = 1 - this.rd += (amount > 0 ? amount - 1 : amount) * 7 - break - case 'weekday': - case 'weekdays': - // todo - break - } - } - }, - - dayText: { - regex: RegExp('^(' + reDaytext + ')', 'i'), - name: 'daytext', - callback (match, dayText) { - this.resetTime() - this.weekday = lookupWeekday(dayText, 0) - - if (this.weekdayBehavior !== 2) { - this.weekdayBehavior = 1 - } - } - }, - - relativeTextWeek: { - regex: RegExp('^(' + reReltexttext + ')' + reSpace + 'week', 'i'), - name: 'relativetextweek', - callback (match, relText) { - this.weekdayBehavior = 2 - - switch (relText.toLowerCase()) { - case 'this': - this.rd += 0 - break - case 'next': - this.rd += 7 - break - case 'last': - case 'previous': - this.rd -= 7 - break - } - - if (isNaN(this.weekday)) { - this.weekday = 1 - } - } - }, - - monthFullOrMonthAbbr: { - regex: RegExp('^(' + reMonthFull + '|' + reMonthAbbr + ')', 'i'), - name: 'monthfull | monthabbr', - callback (match, month) { - return this.ymd(this.y, lookupMonth(month), this.d) - } - }, - - tzCorrection: { - regex: RegExp('^' + reTzCorrection, 'i'), - name: 'tzcorrection', - callback (tzCorrection) { - return this.zone(processTzCorrection(tzCorrection)) - } - }, - - tzAbbr: { - regex: RegExp('^' + reTzAbbr), - name: 'tzabbr', - callback (match, abbr) { - const offset = tzAbbrOffsets[abbr.toLowerCase()] - - if (isNaN(offset)) { - return false - } - - return this.zone(offset) - } - }, - - ago: { - regex: /^ago/i, - name: 'ago', - callback () { - this.ry = -this.ry - this.rm = -this.rm - this.rd = -this.rd - this.rh = -this.rh - this.ri = -this.ri - this.rs = -this.rs - this.rf = -this.rf - } - }, - - year4: { - regex: RegExp('^' + reYear4), - name: 'year4', - callback (match, year) { - this.y = +year - return true - } - }, - - whitespace: { - regex: /^[ .,\t]+/, - name: 'whitespace' - // do nothing - }, - - dateShortWithTimeLong: { - regex: RegExp('^' + reDateNoYear + 't?' + reHour24 + '[:.]' + reMinute + '[:.]' + reSecond, 'i'), - name: 'dateshortwithtimelong', - callback (match, month, day, hour, minute, second) { - return this.ymd(this.y, lookupMonth(month), +day) && this.time(+hour, +minute, +second, 0) - } - }, - - dateShortWithTimeLong12: { - regex: RegExp('^' + reDateNoYear + reHour12 + '[:.]' + reMinute + '[:.]' + reSecondlz + reSpaceOpt + reMeridian, 'i'), - name: 'dateshortwithtimelong12', - callback (match, month, day, hour, minute, second, meridian) { - return this.ymd(this.y, lookupMonth(month), +day) && this.time(processMeridian(+hour, meridian), +minute, +second, 0) - } - }, - - dateShortWithTimeShort: { - regex: RegExp('^' + reDateNoYear + 't?' + reHour24 + '[:.]' + reMinute, 'i'), - name: 'dateshortwithtimeshort', - callback (match, month, day, hour, minute) { - return this.ymd(this.y, lookupMonth(month), +day) && this.time(+hour, +minute, 0, 0) - } - }, - - dateShortWithTimeShort12: { - regex: RegExp('^' + reDateNoYear + reHour12 + '[:.]' + reMinutelz + reSpaceOpt + reMeridian, 'i'), - name: 'dateshortwithtimeshort12', - callback (match, month, day, hour, minute, meridian) { - return this.ymd(this.y, lookupMonth(month), +day) && this.time(processMeridian(+hour, meridian), +minute, 0, 0) - } - } -} - -const resultProto = { - // date - y: NaN, - m: NaN, - d: NaN, - // time - h: NaN, - i: NaN, - s: NaN, - f: NaN, - - // relative shifts - ry: 0, - rm: 0, - rd: 0, - rh: 0, - ri: 0, - rs: 0, - rf: 0, - - // weekday related shifts - weekday: NaN, - weekdayBehavior: 0, - - // first or last day of month - // 0 none, 1 first, -1 last - firstOrLastDayOfMonth: 0, - - // timezone correction in minutes - z: NaN, - - // counters - dates: 0, - times: 0, - zones: 0, - - // helper functions - ymd (y, m, d) { - if (this.dates > 0) { - return false - } - - this.dates++ - this.y = y - this.m = m - this.d = d - return true - }, - - time (h, i, s, f) { - if (this.times > 0) { - return false - } - - this.times++ - this.h = h - this.i = i - this.s = s - this.f = f - - return true - }, - - resetTime () { - this.h = 0 - this.i = 0 - this.s = 0 - this.f = 0 - this.times = 0 - - return true - }, - - zone (minutes) { - if (this.zones <= 1) { - this.zones++ - this.z = minutes - return true - } - - return false - }, - - toDate (relativeTo) { - if (this.dates && !this.times) { - this.h = this.i = this.s = this.f = 0 - } - - // fill holes - if (isNaN(this.y)) { - this.y = relativeTo.getFullYear() - } - - if (isNaN(this.m)) { - this.m = relativeTo.getMonth() - } - - if (isNaN(this.d)) { - this.d = relativeTo.getDate() - } - - if (isNaN(this.h)) { - this.h = relativeTo.getHours() - } - - if (isNaN(this.i)) { - this.i = relativeTo.getMinutes() - } - - if (isNaN(this.s)) { - this.s = relativeTo.getSeconds() - } - - if (isNaN(this.f)) { - this.f = relativeTo.getMilliseconds() - } - - // adjust special early - switch (this.firstOrLastDayOfMonth) { - case 1: - this.d = 1 - break - case -1: - this.d = 0 - this.m += 1 - break - } - - if (!isNaN(this.weekday)) { - const date = new Date(relativeTo.getTime()) - date.setFullYear(this.y, this.m, this.d) - date.setHours(this.h, this.i, this.s, this.f) - - const dow = date.getDay() - - if (this.weekdayBehavior === 2) { - // To make "this week" work, where the current day of week is a "sunday" - if (dow === 0 && this.weekday !== 0) { - this.weekday = -6 - } - - // To make "sunday this week" work, where the current day of week is not a "sunday" - if (this.weekday === 0 && dow !== 0) { - this.weekday = 7 - } - - this.d -= dow - this.d += this.weekday - } else { - let diff = this.weekday - dow - - // some PHP magic - if ((this.rd < 0 && diff < 0) || (this.rd >= 0 && diff <= -this.weekdayBehavior)) { - diff += 7 - } - - if (this.weekday >= 0) { - this.d += diff - } else { - this.d -= (7 - (Math.abs(this.weekday) - dow)) - } - - this.weekday = NaN - } - } - - // adjust relative - this.y += this.ry - this.m += this.rm - this.d += this.rd - - this.h += this.rh - this.i += this.ri - this.s += this.rs - this.f += this.rf - - this.ry = this.rm = this.rd = 0 - this.rh = this.ri = this.rs = this.rf = 0 - - const result = new Date(relativeTo.getTime()) - // since Date constructor treats years <= 99 as 1900+ - // it can't be used, thus this weird way - result.setFullYear(this.y, this.m, this.d) - result.setHours(this.h, this.i, this.s, this.f) - - // note: this is done twice in PHP - // early when processing special relatives - // and late - // todo: check if the logic can be reduced - // to just one time action - switch (this.firstOrLastDayOfMonth) { - case 1: - result.setDate(1) - break - case -1: - result.setMonth(result.getMonth() + 1, 0) - break - } - - // adjust timezone - if (!isNaN(this.z) && result.getTimezoneOffset() !== this.z) { - result.setUTCFullYear( - result.getFullYear(), - result.getMonth(), - result.getDate()) - - result.setUTCHours( - result.getHours(), - result.getMinutes(), - result.getSeconds() - this.z, - result.getMilliseconds()) - } - - return result - } -} - -module.exports = function strtotime (str, now) { - // discuss at: https://locutus.io/php/strtotime/ - // original by: Caio Ariede (https://caioariede.com) - // improved by: Kevin van Zonneveld (https://kvz.io) - // improved by: Caio Ariede (https://caioariede.com) - // improved by: A. Matías Quezada (https://amatiasq.com) - // improved by: preuter - // improved by: Brett Zamir (https://brett-zamir.me) - // improved by: Mirko Faber - // input by: David - // bugfixed by: Wagner B. Soares - // bugfixed by: Artur Tchernychev - // bugfixed by: Stephan Bösch-Plepelits (https://github.com/plepe) - // reimplemented by: Rafał Kukawski - // note 1: Examples all have a fixed timestamp to prevent - // note 1: tests to fail because of variable time(zones) - // example 1: strtotime('+1 day', 1129633200) - // returns 1: 1129719600 - // example 2: strtotime('+1 week 2 days 4 hours 2 seconds', 1129633200) - // returns 2: 1130425202 - // example 3: strtotime('last month', 1129633200) - // returns 3: 1127041200 - // example 4: strtotime('2009-05-04 08:30:00+00') - // returns 4: 1241425800 - // example 5: strtotime('2009-05-04 08:30:00+02:00') - // returns 5: 1241418600 - // example 6: strtotime('2009-05-04 08:30:00 YWT') - // returns 6: 1241454600 - - if (now == null) { - now = Math.floor(Date.now() / 1000) - } - - // the rule order is important - // if multiple rules match, the longest match wins - // if multiple rules match the same string, the first match wins - const rules = [ - formats.yesterday, - formats.now, - formats.noon, - formats.midnightOrToday, - formats.tomorrow, - formats.timestamp, - formats.firstOrLastDay, - formats.backOrFrontOf, - // formats.weekdayOf, // not yet implemented - formats.timeTiny12, - formats.timeShort12, - formats.timeLong12, - formats.mssqltime, - formats.timeShort24, - formats.timeLong24, - formats.iso8601long, - formats.gnuNoColon, - formats.iso8601noColon, - formats.americanShort, - formats.american, - formats.iso8601date4, - formats.iso8601dateSlash, - formats.dateSlash, - formats.gnuDateShortOrIso8601date2, - formats.gnuDateShorter, - formats.dateFull, - formats.pointedDate4, - formats.pointedDate2, - formats.dateNoDay, - formats.dateNoDayRev, - formats.dateTextual, - formats.dateNoYear, - formats.dateNoYearRev, - formats.dateNoColon, - formats.xmlRpc, - formats.xmlRpcNoColon, - formats.soap, - formats.wddx, - formats.exif, - formats.pgydotd, - formats.isoWeekDay, - formats.pgTextShort, - formats.pgTextReverse, - formats.clf, - formats.year4, - formats.ago, - formats.dayText, - formats.relativeTextWeek, - formats.relativeText, - formats.monthFullOrMonthAbbr, - formats.tzCorrection, - formats.tzAbbr, - formats.dateShortWithTimeShort12, - formats.dateShortWithTimeLong12, - formats.dateShortWithTimeShort, - formats.dateShortWithTimeLong, - formats.relative, - formats.whitespace - ] - - const result = Object.create(resultProto) - - while (str.length) { - let longestMatch = null - let finalRule = null - - for (let i = 0, l = rules.length; i < l; i++) { - const format = rules[i] - - const match = str.match(format.regex) - - if (match) { - if (!longestMatch || match[0].length > longestMatch[0].length) { - longestMatch = match - finalRule = format - } - } - } - - if (!finalRule || (finalRule.callback && finalRule.callback.apply(result, longestMatch) === false)) { - return false - } - - str = str.substr(longestMatch[0].length) - finalRule = null - longestMatch = null - } - - return Math.floor(result.toDate(new Date(now * 1000)) / 1000) -} \ No newline at end of file diff --git a/backend/src/api/bitcoin/bitcoin.routes.ts b/backend/src/api/bitcoin/bitcoin.routes.ts index c65363315..29c3f7c21 100644 --- a/backend/src/api/bitcoin/bitcoin.routes.ts +++ b/backend/src/api/bitcoin/bitcoin.routes.ts @@ -418,7 +418,7 @@ class BitcoinRoutes { const height = req.params.height === undefined ? undefined : parseInt(req.params.height, 10); res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); res.json(await blocks.$getBlocks(height, 15)); - } else { // Liquid, Bisq + } else { // Liquid return await this.getLegacyBlocks(req, res); } } catch (e) { @@ -428,7 +428,7 @@ class BitcoinRoutes { private async getBlocksByBulk(req: Request, res: Response) { try { - if (['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) === false) { // Liquid, Bisq - Not implemented + if (['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) === false) { // Liquid - Not implemented return res.status(404).send(`This API is only available for Bitcoin networks`); } if (config.MEMPOOL.MAX_BLOCKS_BULK_QUERY <= 0) { diff --git a/backend/src/api/common.ts b/backend/src/api/common.ts index d8cf0d73f..5053d4da3 100644 --- a/backend/src/api/common.ts +++ b/backend/src/api/common.ts @@ -552,6 +552,7 @@ export class Common { value: tx.vout.reduce((acc, vout) => acc + (vout.value ? vout.value : 0), 0), acc: tx.acceleration || undefined, rate: tx.effectiveFeePerVsize, + time: tx.firstSeen || undefined, }; } diff --git a/backend/src/api/database-migration.ts b/backend/src/api/database-migration.ts index cf9acbc53..ae68582af 100644 --- a/backend/src/api/database-migration.ts +++ b/backend/src/api/database-migration.ts @@ -7,7 +7,7 @@ import cpfpRepository from '../repositories/CpfpRepository'; import { RowDataPacket } from 'mysql2'; class DatabaseMigration { - private static currentVersion = 75; + private static currentVersion = 76; private queryTimeout = 3600_000; private statisticsAddedIndexed = false; private uniqueLogs: string[] = []; @@ -654,6 +654,11 @@ class DatabaseMigration { await this.$executeQuery('ALTER TABLE `prices` ADD `ZAR` float DEFAULT "-1"'); await this.updateToSchemaVersion(75); } + + if (databaseSchemaVersion < 76 && isBitcoin === true) { + await this.$executeQuery('ALTER TABLE `blocks_audits` ADD prioritized_txs JSON DEFAULT "[]"'); + await this.updateToSchemaVersion(76); + } } /** diff --git a/backend/src/api/mempool-blocks.ts b/backend/src/api/mempool-blocks.ts index 687fdbef4..3af2a9967 100644 --- a/backend/src/api/mempool-blocks.ts +++ b/backend/src/api/mempool-blocks.ts @@ -598,7 +598,8 @@ class MempoolBlocks { tx.value, Math.round((tx.rate || (tx.fee / tx.vsize)) * 100) / 100, tx.flags, - 1 + tx.time || 0, + 1, ]; } else { return [ @@ -608,6 +609,7 @@ class MempoolBlocks { tx.value, Math.round((tx.rate || (tx.fee / tx.vsize)) * 100) / 100, tx.flags, + tx.time || 0, ]; } } diff --git a/backend/src/api/websocket-handler.ts b/backend/src/api/websocket-handler.ts index a20088128..66f134b2a 100644 --- a/backend/src/api/websocket-handler.ts +++ b/backend/src/api/websocket-handler.ts @@ -83,6 +83,7 @@ class WebsocketHandler { const _blocks = blocks.getBlocks().slice(-config.MEMPOOL.INITIAL_BLOCKS_AMOUNT); const da = difficultyAdjustment.getDifficultyAdjustment(); this.updateSocketDataFields({ + 'backend': config.MEMPOOL.BACKEND, 'mempoolInfo': memPool.getMempoolInfo(), 'vBytesPerSecond': memPool.getVBytesPerSecond(), 'blocks': _blocks, @@ -359,14 +360,6 @@ class WebsocketHandler { client['track-donation'] = parsedMessage['track-donation']; } - if (parsedMessage['track-bisq-market']) { - if (/^[a-z]{3}_[a-z]{3}$/.test(parsedMessage['track-bisq-market'])) { - client['track-bisq-market'] = parsedMessage['track-bisq-market']; - } else { - client['track-bisq-market'] = null; - } - } - if (Object.keys(response).length) { client.send(this.serializeResponse(response)); } @@ -868,7 +861,7 @@ class WebsocketHandler { } if (Common.indexingEnabled()) { - const { censored, added, fresh, sigop, fullrbf, accelerated, score, similarity } = Audit.auditBlock(transactions, projectedBlocks, auditMempool); + const { censored, added, prioritized, fresh, sigop, fullrbf, accelerated, score, similarity } = Audit.auditBlock(transactions, projectedBlocks, auditMempool); const matchRate = Math.round(score * 100 * 100) / 100; const stripped = projectedBlocks[0]?.transactions ? projectedBlocks[0].transactions : []; @@ -894,6 +887,7 @@ class WebsocketHandler { height: block.height, hash: block.id, addedTxs: added, + prioritizedTxs: prioritized, missingTxs: censored, freshTxs: fresh, sigopTxs: sigop, diff --git a/backend/src/config.ts b/backend/src/config.ts index 41dd0aadb..93ac90834 100644 --- a/backend/src/config.ts +++ b/backend/src/config.ts @@ -116,10 +116,6 @@ interface IConfig { ENABLED: boolean; TX_PER_SECOND_SAMPLE_PERIOD: number; }; - BISQ: { - ENABLED: boolean; - DATA_PATH: string; - }; SOCKS5PROXY: { ENABLED: boolean; USE_ONION: boolean; @@ -133,8 +129,6 @@ interface IConfig { MEMPOOL_ONION: string; LIQUID_API: string; LIQUID_ONION: string; - BISQ_URL: string; - BISQ_ONION: string; }; MAXMIND: { ENABLED: boolean; @@ -258,10 +252,6 @@ const defaults: IConfig = { 'ENABLED': true, 'TX_PER_SECOND_SAMPLE_PERIOD': 150 }, - 'BISQ': { - 'ENABLED': false, - 'DATA_PATH': '/bisq/statsnode-data/btc_mainnet/db' - }, 'LIGHTNING': { 'ENABLED': false, 'BACKEND': 'lnd', @@ -293,9 +283,7 @@ const defaults: IConfig = { 'MEMPOOL_API': 'https://mempool.space/api/v1', 'MEMPOOL_ONION': 'http://mempoolhqx4isw62xs7abwphsq7ldayuidyx2v2oethdhhj6mlo2r6ad.onion/api/v1', 'LIQUID_API': 'https://liquid.network/api/v1', - 'LIQUID_ONION': 'http://liquidmom47f6s3m53ebfxn47p76a6tlnxib3wp6deux7wuzotdr6cyd.onion/api/v1', - 'BISQ_URL': 'https://bisq.markets/api', - 'BISQ_ONION': 'http://bisqmktse2cabavbr2xjq7xw3h6g5ottemo5rolfcwt6aly6tp5fdryd.onion/api' + 'LIQUID_ONION': 'http://liquidmom47f6s3m53ebfxn47p76a6tlnxib3wp6deux7wuzotdr6cyd.onion/api/v1' }, 'MAXMIND': { 'ENABLED': false, @@ -333,7 +321,6 @@ class Config implements IConfig { DATABASE: IConfig['DATABASE']; SYSLOG: IConfig['SYSLOG']; STATISTICS: IConfig['STATISTICS']; - BISQ: IConfig['BISQ']; LIGHTNING: IConfig['LIGHTNING']; LND: IConfig['LND']; CLIGHTNING: IConfig['CLIGHTNING']; @@ -355,7 +342,6 @@ class Config implements IConfig { this.DATABASE = configs.DATABASE; this.SYSLOG = configs.SYSLOG; this.STATISTICS = configs.STATISTICS; - this.BISQ = configs.BISQ; this.LIGHTNING = configs.LIGHTNING; this.LND = configs.LND; this.CLIGHTNING = configs.CLIGHTNING; diff --git a/backend/src/index.ts b/backend/src/index.ts index ae5113029..b28e9ccbf 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -11,8 +11,6 @@ import memPool from './api/mempool'; import diskCache from './api/disk-cache'; import statistics from './api/statistics/statistics'; import websocketHandler from './api/websocket-handler'; -import bisq from './api/bisq/bisq'; -import bisqMarkets from './api/bisq/markets'; import logger from './logger'; import backendInfo from './api/backend-info'; import loadingIndicators from './api/loading-indicators'; @@ -32,7 +30,6 @@ import networkSyncService from './tasks/lightning/network-sync.service'; import statisticsRoutes from './api/statistics/statistics.routes'; import pricesRoutes from './api/prices/prices.routes'; import miningRoutes from './api/mining/mining-routes'; -import bisqRoutes from './api/bisq/bisq.routes'; import liquidRoutes from './api/liquid/liquid.routes'; import bitcoinRoutes from './api/bitcoin/bitcoin.routes'; import fundingTxFetcher from './tasks/lightning/sync-tasks/funding-tx-fetcher'; @@ -182,13 +179,6 @@ class Server { setInterval(() => { this.healthCheck(); }, 2500); - if (config.BISQ.ENABLED) { - bisq.startBisqService(); - bisq.setPriceCallbackFunction((price) => websocketHandler.setExtraInitData('bsq-price', price)); - blocks.setNewBlockCallback(bisq.handleNewBitcoinBlock.bind(bisq)); - bisqMarkets.startBisqService(); - } - if (config.LIGHTNING.ENABLED) { this.$runLightningBackend(); } @@ -307,9 +297,6 @@ class Server { if (Common.indexingEnabled() && config.MEMPOOL.ENABLED) { miningRoutes.initRoutes(this.app); } - if (config.BISQ.ENABLED) { - bisqRoutes.initRoutes(this.app); - } if (Common.isLiquid()) { liquidRoutes.initRoutes(this.app); } diff --git a/backend/src/logger.ts b/backend/src/logger.ts index bbd781df6..bce77c63f 100644 --- a/backend/src/logger.ts +++ b/backend/src/logger.ts @@ -86,9 +86,6 @@ class Logger { if (config.LIGHTNING.ENABLED) { return config.MEMPOOL.NETWORK === 'mainnet' ? 'lightning' : `${config.MEMPOOL.NETWORK}-lightning`; } - if (config.BISQ.ENABLED) { - return 'bisq'; - } if (config.MEMPOOL.NETWORK && config.MEMPOOL.NETWORK !== 'mainnet') { return config.MEMPOOL.NETWORK; } diff --git a/backend/src/mempool.interfaces.ts b/backend/src/mempool.interfaces.ts index dd23db8ab..0b4b20e02 100644 --- a/backend/src/mempool.interfaces.ts +++ b/backend/src/mempool.interfaces.ts @@ -37,6 +37,7 @@ export interface BlockAudit { sigopTxs: string[], fullrbfTxs: string[], addedTxs: string[], + prioritizedTxs: string[], acceleratedTxs: string[], matchRate: number, expectedFees?: number, @@ -200,6 +201,7 @@ export interface TransactionStripped { value: number; acc?: boolean; rate?: number; // effective fee rate + time?: number; } export interface TransactionClassified extends TransactionStripped { @@ -207,7 +209,7 @@ export interface TransactionClassified extends TransactionStripped { } // [txid, fee, vsize, value, rate, flags, acceleration?] -export type TransactionCompressed = [string, number, number, number, number, number, 1?]; +export type TransactionCompressed = [string, number, number, number, number, number, number, 1?]; // [txid, rate, flags, acceleration?] export type MempoolDeltaChange = [string, number, number, (1|0)]; @@ -433,7 +435,6 @@ export interface WebsocketResponse { 'track-tx': string; 'track-address': string; 'watch-mempool': boolean; - 'track-bisq-market': string; } export interface VbytesPerSecond { diff --git a/backend/src/replication/AuditReplication.ts b/backend/src/replication/AuditReplication.ts index 503c61613..4ea629839 100644 --- a/backend/src/replication/AuditReplication.ts +++ b/backend/src/replication/AuditReplication.ts @@ -114,6 +114,7 @@ class AuditReplication { time: auditSummary.timestamp || auditSummary.time, missingTxs: auditSummary.missingTxs || [], addedTxs: auditSummary.addedTxs || [], + prioritizedTxs: auditSummary.prioritizedTxs || [], freshTxs: auditSummary.freshTxs || [], sigopTxs: auditSummary.sigopTxs || [], fullrbfTxs: auditSummary.fullrbfTxs || [], diff --git a/backend/src/repositories/BlocksAuditsRepository.ts b/backend/src/repositories/BlocksAuditsRepository.ts index 62f28c56f..daf1ba52d 100644 --- a/backend/src/repositories/BlocksAuditsRepository.ts +++ b/backend/src/repositories/BlocksAuditsRepository.ts @@ -6,9 +6,9 @@ import { BlockAudit, AuditScore } from '../mempool.interfaces'; class BlocksAuditRepositories { public async $saveAudit(audit: BlockAudit): Promise { try { - await DB.query(`INSERT INTO blocks_audits(time, height, hash, missing_txs, added_txs, fresh_txs, sigop_txs, fullrbf_txs, accelerated_txs, match_rate, expected_fees, expected_weight) - VALUE (FROM_UNIXTIME(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [audit.time, audit.height, audit.hash, JSON.stringify(audit.missingTxs), - JSON.stringify(audit.addedTxs), JSON.stringify(audit.freshTxs), JSON.stringify(audit.sigopTxs), JSON.stringify(audit.fullrbfTxs), JSON.stringify(audit.acceleratedTxs), audit.matchRate, audit.expectedFees, audit.expectedWeight]); + await DB.query(`INSERT INTO blocks_audits(time, height, hash, missing_txs, added_txs, prioritized_txs, fresh_txs, sigop_txs, fullrbf_txs, accelerated_txs, match_rate, expected_fees, expected_weight) + VALUE (FROM_UNIXTIME(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [audit.time, audit.height, audit.hash, JSON.stringify(audit.missingTxs), + JSON.stringify(audit.addedTxs), JSON.stringify(audit.prioritizedTxs), JSON.stringify(audit.freshTxs), JSON.stringify(audit.sigopTxs), JSON.stringify(audit.fullrbfTxs), JSON.stringify(audit.acceleratedTxs), audit.matchRate, audit.expectedFees, audit.expectedWeight]); } catch (e: any) { if (e.errno === 1062) { // ER_DUP_ENTRY - This scenario is possible upon node backend restart logger.debug(`Cannot save block audit for block ${audit.hash} because it has already been indexed, ignoring`); @@ -66,6 +66,7 @@ class BlocksAuditRepositories { template, missing_txs as missingTxs, added_txs as addedTxs, + prioritized_txs as prioritizedTxs, fresh_txs as freshTxs, sigop_txs as sigopTxs, fullrbf_txs as fullrbfTxs, @@ -81,6 +82,7 @@ class BlocksAuditRepositories { if (rows.length) { rows[0].missingTxs = JSON.parse(rows[0].missingTxs); rows[0].addedTxs = JSON.parse(rows[0].addedTxs); + rows[0].prioritizedTxs = JSON.parse(rows[0].prioritizedTxs); rows[0].freshTxs = JSON.parse(rows[0].freshTxs); rows[0].sigopTxs = JSON.parse(rows[0].sigopTxs); rows[0].fullrbfTxs = JSON.parse(rows[0].fullrbfTxs); diff --git a/docker/README.md b/docker/README.md index c8ac91d38..32ed6fcde 100644 --- a/docker/README.md +++ b/docker/README.md @@ -323,25 +323,6 @@ Corresponding `docker-compose.yml` overrides:
-`mempool-config.json`: -```json - "BISQ": { - "ENABLED": false, - "DATA_PATH": "/bisq/statsnode-data/btc_mainnet/db" - } -``` - -Corresponding `docker-compose.yml` overrides: -```yaml - api: - environment: - BISQ_ENABLED: "" - BISQ_DATA_PATH: "" - ... -``` - -
- `mempool-config.json`: ```json "SOCKS5PROXY": { diff --git a/docker/backend/Dockerfile b/docker/backend/Dockerfile index 7ab3d605c..d8eada208 100644 --- a/docker/backend/Dockerfile +++ b/docker/backend/Dockerfile @@ -11,10 +11,17 @@ RUN apt-get install -y build-essential python3 pkg-config curl ca-certificates # Install Rust via rustup RUN CPU_ARCH=$(uname -m); if [ "$CPU_ARCH" = "armv7l" ]; then c_rehash; fi -RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain stable +#RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain stable +#Workaround to run on github actions from https://github.com/rust-lang/rustup/issues/2700#issuecomment-1367488985 +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sed 's#/proc/self/exe#\/bin\/sh#g' | sh -s -- -y --default-toolchain stable ENV PATH="/root/.cargo/bin:$PATH" +COPY --from=backend . . +COPY --from=rustgbt . ../rust/ +ENV FD=/build/rust-gbt RUN npm install --omit=dev --omit=optional + +WORKDIR /build RUN npm run package FROM node:20.12.0-buster-slim diff --git a/docker/backend/mempool-config.json b/docker/backend/mempool-config.json index 0d6017788..ea3fb56a1 100644 --- a/docker/backend/mempool-config.json +++ b/docker/backend/mempool-config.json @@ -93,10 +93,6 @@ "ENABLED": __STATISTICS_ENABLED__, "TX_PER_SECOND_SAMPLE_PERIOD": __STATISTICS_TX_PER_SECOND_SAMPLE_PERIOD__ }, - "BISQ": { - "ENABLED": __BISQ_ENABLED__, - "DATA_PATH": "__BISQ_DATA_PATH__" - }, "LIGHTNING": { "ENABLED": __LIGHTNING_ENABLED__, "BACKEND": "__LIGHTNING_BACKEND__", @@ -128,9 +124,7 @@ "MEMPOOL_API": "__EXTERNAL_DATA_SERVER_MEMPOOL_API__", "MEMPOOL_ONION": "__EXTERNAL_DATA_SERVER_MEMPOOL_ONION__", "LIQUID_API": "__EXTERNAL_DATA_SERVER_LIQUID_API__", - "LIQUID_ONION": "__EXTERNAL_DATA_SERVER_LIQUID_ONION__", - "BISQ_URL": "__EXTERNAL_DATA_SERVER_BISQ_URL__", - "BISQ_ONION": "__EXTERNAL_DATA_SERVER_BISQ_ONION__" + "LIQUID_ONION": "__EXTERNAL_DATA_SERVER_LIQUID_ONION__" }, "MAXMIND": { "ENABLED": __MAXMIND_ENABLED__, diff --git a/docker/backend/start.sh b/docker/backend/start.sh index aecdac8f1..401199ede 100755 --- a/docker/backend/start.sh +++ b/docker/backend/start.sh @@ -94,10 +94,6 @@ __SYSLOG_FACILITY__=${SYSLOG_FACILITY:=local7} __STATISTICS_ENABLED__=${STATISTICS_ENABLED:=true} __STATISTICS_TX_PER_SECOND_SAMPLE_PERIOD__=${STATISTICS_TX_PER_SECOND_SAMPLE_PERIOD:=150} -# BISQ -__BISQ_ENABLED__=${BISQ_ENABLED:=false} -__BISQ_DATA_PATH__=${BISQ_DATA_PATH:=/bisq/statsnode-data/btc_mainnet/db} - # SOCKS5PROXY __SOCKS5PROXY_ENABLED__=${SOCKS5PROXY_ENABLED:=false} __SOCKS5PROXY_USE_ONION__=${SOCKS5PROXY_USE_ONION:=true} @@ -111,8 +107,6 @@ __EXTERNAL_DATA_SERVER_MEMPOOL_API__=${EXTERNAL_DATA_SERVER_MEMPOOL_API:=https:/ __EXTERNAL_DATA_SERVER_MEMPOOL_ONION__=${EXTERNAL_DATA_SERVER_MEMPOOL_ONION:=http://mempoolhqx4isw62xs7abwphsq7ldayuidyx2v2oethdhhj6mlo2r6ad.onion/api/v1} __EXTERNAL_DATA_SERVER_LIQUID_API__=${EXTERNAL_DATA_SERVER_LIQUID_API:=https://liquid.network/api/v1} __EXTERNAL_DATA_SERVER_LIQUID_ONION__=${EXTERNAL_DATA_SERVER_LIQUID_ONION:=http://liquidmom47f6s3m53ebfxn47p76a6tlnxib3wp6deux7wuzotdr6cyd.onion/api/v1} -__EXTERNAL_DATA_SERVER_BISQ_URL__=${EXTERNAL_DATA_SERVER_BISQ_URL:=https://bisq.markets/api} -__EXTERNAL_DATA_SERVER_BISQ_ONION__=${EXTERNAL_DATA_SERVER_BISQ_ONION:=http://bisqmktse2cabavbr2xjq7xw3h6g5ottemo5rolfcwt6aly6tp5fdryd.onion/api} # LIGHTNING __LIGHTNING_ENABLED__=${LIGHTNING_ENABLED:=false} @@ -246,9 +240,6 @@ sed -i "s!__SYSLOG_FACILITY__!${__SYSLOG_FACILITY__}!g" mempool-config.json sed -i "s!__STATISTICS_ENABLED__!${__STATISTICS_ENABLED__}!g" mempool-config.json sed -i "s!__STATISTICS_TX_PER_SECOND_SAMPLE_PERIOD__!${__STATISTICS_TX_PER_SECOND_SAMPLE_PERIOD__}!g" mempool-config.json -sed -i "s!__BISQ_ENABLED__!${__BISQ_ENABLED__}!g" mempool-config.json -sed -i "s!__BISQ_DATA_PATH__!${__BISQ_DATA_PATH__}!g" mempool-config.json - sed -i "s!__SOCKS5PROXY_ENABLED__!${__SOCKS5PROXY_ENABLED__}!g" mempool-config.json sed -i "s!__SOCKS5PROXY_USE_ONION__!${__SOCKS5PROXY_USE_ONION__}!g" mempool-config.json sed -i "s!__SOCKS5PROXY_HOST__!${__SOCKS5PROXY_HOST__}!g" mempool-config.json @@ -260,8 +251,6 @@ sed -i "s!__EXTERNAL_DATA_SERVER_MEMPOOL_API__!${__EXTERNAL_DATA_SERVER_MEMPOOL_ sed -i "s!__EXTERNAL_DATA_SERVER_MEMPOOL_ONION__!${__EXTERNAL_DATA_SERVER_MEMPOOL_ONION__}!g" mempool-config.json sed -i "s!__EXTERNAL_DATA_SERVER_LIQUID_API__!${__EXTERNAL_DATA_SERVER_LIQUID_API__}!g" mempool-config.json sed -i "s!__EXTERNAL_DATA_SERVER_LIQUID_ONION__!${__EXTERNAL_DATA_SERVER_LIQUID_ONION__}!g" mempool-config.json -sed -i "s!__EXTERNAL_DATA_SERVER_BISQ_URL__!${__EXTERNAL_DATA_SERVER_BISQ_URL__}!g" mempool-config.json -sed -i "s!__EXTERNAL_DATA_SERVER_BISQ_ONION__!${__EXTERNAL_DATA_SERVER_BISQ_ONION__}!g" mempool-config.json # LIGHTNING sed -i "s!__LIGHTNING_ENABLED__!${__LIGHTNING_ENABLED__}!g" mempool-config.json diff --git a/docker/frontend/entrypoint.sh b/docker/frontend/entrypoint.sh index 8749817bf..a57c599b4 100644 --- a/docker/frontend/entrypoint.sh +++ b/docker/frontend/entrypoint.sh @@ -20,8 +20,6 @@ __TESTNET_ENABLED__=${TESTNET_ENABLED:=false} __SIGNET_ENABLED__=${SIGNET_ENABLED:=false} __LIQUID_ENABLED__=${LIQUID_ENABLED:=false} __LIQUID_TESTNET_ENABLED__=${LIQUID_TESTNET_ENABLED:=false} -__BISQ_ENABLED__=${BISQ_ENABLED:=false} -__BISQ_SEPARATE_BACKEND__=${BISQ_SEPARATE_BACKEND:=false} __ITEMS_PER_PAGE__=${ITEMS_PER_PAGE:=10} __KEEP_BLOCKS_AMOUNT__=${KEEP_BLOCKS_AMOUNT:=8} __NGINX_PROTOCOL__=${NGINX_PROTOCOL:=http} @@ -32,7 +30,6 @@ __MEMPOOL_BLOCKS_AMOUNT__=${MEMPOOL_BLOCKS_AMOUNT:=8} __BASE_MODULE__=${BASE_MODULE:=mempool} __MEMPOOL_WEBSITE_URL__=${MEMPOOL_WEBSITE_URL:=https://mempool.space} __LIQUID_WEBSITE_URL__=${LIQUID_WEBSITE_URL:=https://liquid.network} -__BISQ_WEBSITE_URL__=${BISQ_WEBSITE_URL:=https://bisq.markets} __MINING_DASHBOARD__=${MINING_DASHBOARD:=true} __LIGHTNING__=${LIGHTNING:=false} __AUDIT__=${AUDIT:=false} @@ -40,6 +37,7 @@ __MAINNET_BLOCK_AUDIT_START_HEIGHT__=${MAINNET_BLOCK_AUDIT_START_HEIGHT:=0} __TESTNET_BLOCK_AUDIT_START_HEIGHT__=${TESTNET_BLOCK_AUDIT_START_HEIGHT:=0} __SIGNET_BLOCK_AUDIT_START_HEIGHT__=${SIGNET_BLOCK_AUDIT_START_HEIGHT:=0} __ACCELERATOR__=${ACCELERATOR:=false} +__PUBLIC_ACCELERATIONS__=${PUBLIC_ACCELERATIONS:=false} __HISTORICAL_PRICE__=${HISTORICAL_PRICE:=true} __ADDITIONAL_CURRENCIES__=${ADDITIONAL_CURRENCIES:=false} @@ -48,8 +46,6 @@ export __TESTNET_ENABLED__ export __SIGNET_ENABLED__ export __LIQUID_ENABLED__ export __LIQUID_TESTNET_ENABLED__ -export __BISQ_ENABLED__ -export __BISQ_SEPARATE_BACKEND__ export __ITEMS_PER_PAGE__ export __KEEP_BLOCKS_AMOUNT__ export __NGINX_PROTOCOL__ @@ -60,7 +56,6 @@ export __MEMPOOL_BLOCKS_AMOUNT__ export __BASE_MODULE__ export __MEMPOOL_WEBSITE_URL__ export __LIQUID_WEBSITE_URL__ -export __BISQ_WEBSITE_URL__ export __MINING_DASHBOARD__ export __LIGHTNING__ export __AUDIT__ @@ -68,6 +63,7 @@ export __MAINNET_BLOCK_AUDIT_START_HEIGHT__ export __TESTNET_BLOCK_AUDIT_START_HEIGHT__ export __SIGNET_BLOCK_AUDIT_START_HEIGHT__ export __ACCELERATOR__ +export __PUBLIC_ACCELERATIONS__ export __HISTORICAL_PRICE__ export __ADDITIONAL_CURRENCIES__ diff --git a/frontend/README.md b/frontend/README.md index 8fc77a2b4..069f1d5f0 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -22,14 +22,13 @@ cd mempool/frontend ### 2. Specify Website -The same frontend codebase is used for https://mempool.space, https://liquid.network and https://bisq.markets. +The same frontend codebase is used for https://mempool.space and https://liquid.network. Configure the frontend for the site you want by running the corresponding command: ``` $ npm run config:defaults:mempool $ npm run config:defaults:liquid -$ npm run config:defaults:bisq ``` ### 3. Run the Frontend diff --git a/frontend/cypress/e2e/bisq/bisq.spec.ts b/frontend/cypress/e2e/bisq/bisq.spec.ts deleted file mode 100644 index ac3b747b2..000000000 --- a/frontend/cypress/e2e/bisq/bisq.spec.ts +++ /dev/null @@ -1,164 +0,0 @@ -describe('Bisq', () => { - const baseModule = Cypress.env('BASE_MODULE'); - const basePath = ''; - - beforeEach(() => { - cy.intercept('/sockjs-node/info*').as('socket'); - cy.intercept('/bisq/api/markets/hloc?market=btc_usd&interval=day').as('hloc'); - cy.intercept('/bisq/api/markets/ticker').as('ticker'); - cy.intercept('/bisq/api/markets/markets').as('markets'); - cy.intercept('/bisq/api/markets/volumes/7d').as('7d'); - cy.intercept('/bisq/api/markets/trades?market=all').as('trades'); - cy.intercept('/bisq/api/txs/*/*').as('txs'); - cy.intercept('/bisq/api/blocks/*/*').as('blocks'); - cy.intercept('/bisq/api/stats').as('stats'); - }); - - if (baseModule === 'bisq') { - it('loads the dashboard', () => { - cy.visit(`${basePath}`); - cy.waitForSkeletonGone(); - }); - - describe('transactions', () => { - it('loads the transactions screen', () => { - cy.visit(`${basePath}`); - cy.waitForSkeletonGone(); - cy.get('#btn-transactions').click().then(() => { - cy.get('.table > tr').should('have.length', 50); - }); - }); - - const filters = [ - 'Asset listing fee', 'Blind vote', 'Compensation request', - 'Genesis', 'Irregular', 'Lockup', 'Pay trade fee', 'Proof of burn', - 'Proposal', 'Reimbursement request', 'Transfer BSQ', 'Unlock', 'Vote reveal' - ]; - filters.forEach((filter) => { - it.only(`filters the transaction screen by ${filter}`, () => { - cy.visit(`${basePath}/transactions`); - cy.wait('@txs'); - cy.waitForSkeletonGone(); - cy.get('#filter').click(); - cy.contains(filter).find('input').click(); - cy.wait('@txs'); - cy.wait(500); - cy.get('td:nth-of-type(2)').each(($td) => { - expect($td.text().trim()).to.eq(filter); - }); - }); - }); - - it('filters using multiple criteria', () => { - const filters = ['Proposal', 'Lockup', 'Unlock']; - cy.visit(`${basePath}/transactions`); - cy.waitForSkeletonGone(); - cy.get('#filter').click(); - filters.forEach((filter) => { - cy.contains(filter).find('input').click(); - //TODO: change this waiter - cy.wait(1500); - }); - cy.get('td:nth-of-type(2)').each(($td) => { - const regex = new RegExp(`${filters.join('|')}`, 'g'); - expect($td.text().trim()).to.match(regex); - }); - }); - - const transactions = [ - { type: 'Asset listing fee', txid: '3548aa0c002b015ea700072b7d7d76d45d4f10a3573804d0d2f624c0bb255b6b' }, - { type: 'Blind vote', txid: 'f8fabb95efa1bb81325e4c961b9fc7e3508a9b9ecd4eddf1400e58867eff8d92' }, - { type: 'Compensation request', txid: 'a8cdc65fe6bb8730f5f89f99f779d0469b0a493e1ae570e20eb7afda696a18a9' }, - { type: 'Genesis', txid: '4b5417ec5ab6112bedf539c3b4f5a806ed539542d8b717e1c4470aa3180edce5' }, - { type: 'Irregular', txid: '90b06684a517388fec2237e2362a29810dc82f0e13e019c84747ec27051e6c53' }, - { type: 'Lockup', txid: '365425b3b7487229e2ba598fb8f2a9e359e3351620383e5018548649a28b78c4' }, - { type: 'Pay trade fee', txid: 'a66b30e9777e16572ab36723539df8f45bd5d8130d810b2c3d75b8c02a191eaf' }, - { type: 'Proof of burn', txid: '8325ccb87065fb9243ed9ff1cbb431fc2ac5060a60433bcde474ccbd97b76dcb' }, - { type: 'Proposal', txid: '34e2a20f045c82fbcf7cb191b42dea6fba45641777e1751ffb29d3981c4bf413' }, - { type: 'Reimbursement request', txid: '04c16c79ca6b9ec9978880024b0d0ad3100020f33286b63c85ca7b1a319421ae' }, - { type: 'Transfer BSQ', txid: '64500bd9220675ad30d5ace27de95a341a498d7eda08162ee0ce7feb8c56cb14' }, - { type: 'Unlock', txid: '5a756841bbb11137d15b0082a3fcadbe102791f41a95d661d3bd0c5ad0b3b1a3' }, - { type: 'Vote reveal', txid: 'bd7daae1d4af8837db5e47d7bd9d8b9f83dcfd35d112f85e90728b9be45191f7' } - ]; - - transactions.forEach((transaction) => { - it(`loads a "${transaction.type}" transaction`, () => { - cy.visit(`${basePath}/tx/${transaction.txid}`); - cy.waitForSkeletonGone(); - }); - }); - }); - - describe('blocks', () => { - it('loads the blocks screen', () => { - cy.visit(`${basePath}`); - cy.waitForSkeletonGone(); - cy.get('#btn-blocks').click().then(() => { - cy.wait('@blocks'); - cy.get('tbody tr').should('have.length', 10); - }); - }); - - it('loads a specific block', () => { - cy.visit(`${basePath}/block/0000000000000000000137ef33faa63bc6e809ab30932cf77d454fb36d2bd83a`); - cy.waitForSkeletonGone(); - }); - - }); - - describe('markets', () => { - it('loads the markets screen', () => { - cy.visit(`${basePath}/markets`); - cy.waitForSkeletonGone(); - }); - - it('loads a specific market', () => { - cy.visit(`${basePath}/market/btc_eur`); - cy.waitForSkeletonGone(); - //Buy Offers - cy.get('.row > :nth-child(1) td').should('have.length.at.least', 1); - //Sell offers - cy.get('.row > :nth-child(1) td').should('have.length.at.least', 1); - //Trades - cy.get('app-bisq-trades > .table-container td').should('have.length.at.least', 1); - }); - }); - - it('loads the stats screen', () => { - cy.visit(`${basePath}`); - cy.waitForSkeletonGone(); - cy.get('#btn-stats').click().then(() => { - cy.wait('@stats'); - }); - }); - - it('loads the api screen', () => { - cy.visit(`${basePath}`); - cy.waitForSkeletonGone(); - cy.get('#btn-docs').click().then(() => { - cy.get('.section-header').should('have.length.at.least', 1); - cy.get('.endpoint-container').should('have.length.at.least', 1); - }); - }); - - it('shows blocks pagination with 5 pages (desktop)', () => { - cy.viewport(760, 800); - cy.visit(`${basePath}/blocks`); - cy.waitForSkeletonGone(); - cy.get('tbody tr').should('have.length', 10); - // 5 pages + 4 buttons = 9 buttons - cy.get('.pagination-container ul.pagination').first().children().should('have.length', 9); - }); - - it('shows blocks pagination with 3 pages (mobile)', () => { - cy.viewport(669, 800); - cy.visit(`${basePath}/blocks`); - cy.waitForSkeletonGone(); - cy.get('tbody tr').should('have.length', 10); - // 3 pages + 4 buttons = 7 buttons - cy.get('.pagination-container ul.pagination').first().children().should('have.length', 7); - }); - } else { - it.skip(`Tests cannot be run on the selected BASE_MODULE ${baseModule}`); - } -}); diff --git a/frontend/cypress/e2e/liquid/liquid.spec.ts b/frontend/cypress/e2e/liquid/liquid.spec.ts index 2d1fbd1e9..8548059bb 100644 --- a/frontend/cypress/e2e/liquid/liquid.spec.ts +++ b/frontend/cypress/e2e/liquid/liquid.spec.ts @@ -1,4 +1,4 @@ -describe.skip('Liquid', () => { +describe('Liquid', () => { const baseModule = Cypress.env('BASE_MODULE'); const basePath = ''; @@ -23,6 +23,13 @@ describe.skip('Liquid', () => { cy.get('#mempool-block-0 > .blockLink').should('exist'); }); + it('load first mempool block after skeleton loads', () => { + cy.visit(`${basePath}`); + cy.waitForSkeletonGone(); + cy.get('#mempool-block-0 > .blockLink').click(); + cy.waitForSkeletonGone(); + }); + it('loads the dashboard', () => { cy.visit(`${basePath}`); cy.waitForSkeletonGone(); @@ -84,10 +91,11 @@ describe.skip('Liquid', () => { cy.waitForSkeletonGone(); //TODO: Change to an element id so we don't assert on a string cy.get('.table-tx-vin').should('contain', 'Peg-in'); - cy.get('.table-tx-vin a').click().then(() => { + //Remove the target=_blank attribute so the new url opens in the same tab + cy.get('.table-tx-vin a').invoke('removeAttr', 'target').click().then(() => { cy.waitForSkeletonGone(); if (baseModule === 'liquid') { - cy.url().should('eq', 'https://mempool.space/tx/f148c0d854db4174ea420655235f910543f0ec3680566dcfdf84fb0a1697b592'); + cy.url().should('eq', 'https://mempool.space/tx/f148c0d854db4174ea420655235f910543f0ec3680566dcfdf84fb0a1697b592#vout=0'); } else { //TODO: Use an environment variable to get the hostname cy.url().should('eq', 'http://localhost:4200/tx/f148c0d854db4174ea420655235f910543f0ec3680566dcfdf84fb0a1697b592'); @@ -98,7 +106,8 @@ describe.skip('Liquid', () => { it('loads peg out addresses', () => { cy.visit(`${basePath}/tx/ecf6eba04ffb3946faa172343c87162df76f1a57b07b0d6dc6ad956b13376dc8`); cy.waitForSkeletonGone(); - cy.get('.table-tx-vout a').first().click().then(() => { + //Remove the target=_blank attribute so the new url opens in the same tab + cy.get('.table-tx-vout a').first().invoke('removeAttr', 'target').click().then(() => { cy.waitForSkeletonGone(); if (baseModule === 'liquid') { cy.url().should('eq', 'https://mempool.space/address/1BxoGcMg14oaH3CwHD2hF4gU9VcfgX5yoR'); diff --git a/frontend/cypress/e2e/liquidtestnet/liquidtestnet.spec.ts b/frontend/cypress/e2e/liquidtestnet/liquidtestnet.spec.ts index be27fa183..a96b0700c 100644 --- a/frontend/cypress/e2e/liquidtestnet/liquidtestnet.spec.ts +++ b/frontend/cypress/e2e/liquidtestnet/liquidtestnet.spec.ts @@ -1,4 +1,4 @@ -describe.skip('Liquid Testnet', () => { +describe('Liquid Testnet', () => { const baseModule = Cypress.env('BASE_MODULE'); const basePath = '/testnet'; @@ -28,6 +28,17 @@ describe.skip('Liquid Testnet', () => { cy.waitForSkeletonGone(); }); + it.skip('loads the dashboard with no scrollbars on mobile', () => { + cy.viewport('iphone-xr'); + cy.visit(`${basePath}`); + cy.waitForSkeletonGone(); + cy.window().then(window => { + const htmlWidth = Cypress.$('html')[0].scrollWidth; + const scrollBarWidth = window.innerWidth - htmlWidth; + expect(scrollBarWidth).to.be.eq(0); //check for no horizontal scrollbar + }); + }); + it('loads the blocks page', () => { cy.visit(`${basePath}`) cy.get('#btn-blocks'); @@ -57,17 +68,14 @@ describe.skip('Liquid Testnet', () => { cy.get('.tv-only').should('not.exist'); }); - it.skip('renders unconfidential addresses correctly on mobile', () => { - cy.viewport('iphone-6'); - cy.visit(`${basePath}/address/__TODO__`); + it.skip('renders unconfidential transactions correctly on mobile', () => { + cy.viewport('iphone-xr'); + cy.visit(`${basePath}/tx/b119f338878416781dc285b94c0de52826341dea43566e4de4740d3ebfd1f6dc#blinded=99707,144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49,1377e4ec8eb0c89296e14ffca57e377f4b51ad8f1c881e43364434d8430dbfda,cdd6caae4c3452586cfcb107478dd2b7acaa5f82714a6a966578255e857eee60`); cy.waitForSkeletonGone(); - //TODO: Add proper IDs for these selectors - const firstRowSelector = '.container-xl > :nth-child(3) > div > :nth-child(1) > .table > tbody'; - const thirdRowSelector = '.container-xl > :nth-child(3) > div > :nth-child(3)'; - cy.get(firstRowSelector).invoke('css', 'width').then(firstRowWidth => { - cy.get(thirdRowSelector).invoke('css', 'width').then(thirdRowWidth => { - expect(parseInt(firstRowWidth)).to.be.lessThan(parseInt(thirdRowWidth)); - }); + cy.window().then(window => { + const htmlWidth = Cypress.$('html')[0].scrollWidth; + const scrollBarWidth = window.innerWidth - htmlWidth; + expect(scrollBarWidth).to.be.eq(0); //check for no horizontal scrollbar }); }); diff --git a/frontend/cypress/support/commands.ts b/frontend/cypress/support/commands.ts index 3c32df517..23376e5b2 100644 --- a/frontend/cypress/support/commands.ts +++ b/frontend/cypress/support/commands.ts @@ -72,13 +72,11 @@ Cypress.Commands.add('mockMempoolSocket', () => { mockWebSocket(); }); -Cypress.Commands.add('changeNetwork', (network: "testnet" | "signet" | "liquid" | "bisq" | "mainnet") => { +Cypress.Commands.add('changeNetwork', (network: "testnet" | "signet" | "liquid" | "mainnet") => { cy.get('.dropdown-toggle').click().then(() => { cy.get(`a.${network}`).click().then(() => { cy.waitForPageIdle(); - if (network !== 'bisq') { - cy.waitForSkeletonGone(); - } + cy.waitForSkeletonGone(); }); }); }); diff --git a/frontend/cypress/support/index.d.ts b/frontend/cypress/support/index.d.ts index a6bef9f7c..3cc151b21 100644 --- a/frontend/cypress/support/index.d.ts +++ b/frontend/cypress/support/index.d.ts @@ -5,6 +5,6 @@ declare namespace Cypress { waitForSkeletonGone(): Chainable waitForPageIdle(): Chainable mockMempoolSocket(): Chainable - changeNetwork(network: "testnet"|"signet"|"liquid"|"bisq"|"mainnet"): Chainable + changeNetwork(network: "testnet"|"signet"|"liquid"|"mainnet"): Chainable } } \ No newline at end of file diff --git a/frontend/mempool-frontend-config.sample.json b/frontend/mempool-frontend-config.sample.json index e598f2d03..7f06c8fbc 100644 --- a/frontend/mempool-frontend-config.sample.json +++ b/frontend/mempool-frontend-config.sample.json @@ -3,8 +3,6 @@ "SIGNET_ENABLED": false, "LIQUID_ENABLED": false, "LIQUID_TESTNET_ENABLED": false, - "BISQ_ENABLED": false, - "BISQ_SEPARATE_BACKEND": false, "ITEMS_PER_PAGE": 10, "KEEP_BLOCKS_AMOUNT": 8, "NGINX_PROTOCOL": "http", @@ -15,7 +13,6 @@ "BASE_MODULE": "mempool", "MEMPOOL_WEBSITE_URL": "https://mempool.space", "LIQUID_WEBSITE_URL": "https://liquid.network", - "BISQ_WEBSITE_URL": "https://bisq.markets", "MINING_DASHBOARD": true, "AUDIT": false, "MAINNET_BLOCK_AUDIT_START_HEIGHT": 0, @@ -24,5 +21,6 @@ "LIGHTNING": false, "HISTORICAL_PRICE": true, "ADDITIONAL_CURRENCIES": false, - "ACCELERATOR": false + "ACCELERATOR": false, + "PUBLIC_ACCELERATIONS": false } diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 4beb4413a..5e2cbeada 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -9860,9 +9860,9 @@ "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==" }, "node_modules/express": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.19.1.tgz", - "integrity": "sha512-K4w1/Bp7y8iSiVObmCrtq8Cs79XjJc/RU2YYkZQ7wpUu5ZyZ7MtPHkqoMz4pf+mgXfNvo2qft8D9OnrH2ABk9w==", + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", @@ -25526,9 +25526,9 @@ "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==" }, "express": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.19.1.tgz", - "integrity": "sha512-K4w1/Bp7y8iSiVObmCrtq8Cs79XjJc/RU2YYkZQ7wpUu5ZyZ7MtPHkqoMz4pf+mgXfNvo2qft8D9OnrH2ABk9w==", + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", "requires": { "accepts": "~1.3.8", "array-flatten": "1.1.1", diff --git a/frontend/package.json b/frontend/package.json index d7f2a9e02..5864a2284 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -34,13 +34,12 @@ "start:local-prod": "npm run generate-config && npm run sync-assets-dev && npm run ng -- serve -c local-prod", "start:local-staging": "npm run generate-config && npm run sync-assets-dev && npm run ng -- serve -c local-staging", "start:mixed": "npm run generate-config && npm run sync-assets-dev && npm run ng -- serve -c mixed", - "build": "npm run generate-config && npm run ng -- build --configuration production --localize && npm run sync-assets && npm run build-mempool.js", + "build": "npm run generate-config && npm run ng -- build --configuration production --localize && npm run sync-assets-dev && npm run sync-assets && npm run build-mempool.js", "sync-assets": "rsync -av ./src/resources ./dist/mempool/browser && node sync-assets.js 'dist/mempool/browser/resources/'", "sync-assets-dev": "node sync-assets.js 'src/resources/'", "generate-config": "node generate-config.js", - "build-mempool.js": "npm run build-mempool-js && npm run build-mempool-liquid-js && npm run build-mempool-bisq-js", + "build-mempool.js": "npm run build-mempool-js && npm run build-mempool-liquid-js", "build-mempool-js": "browserify -p tinyify ./node_modules/@mempool/mempool.js/lib/index.js --standalone mempoolJS > ./dist/mempool/browser/en-US/mempool.js", - "build-mempool-bisq-js": "browserify -p tinyify ./node_modules/@mempool/mempool.js/lib/index-bisq.js --standalone bisqJS > ./dist/mempool/browser/en-US/bisq.js", "build-mempool-liquid-js": "browserify -p tinyify ./node_modules/@mempool/mempool.js/lib/index-liquid.js --standalone liquidJS > ./dist/mempool/browser/en-US/liquid.js", "test": "npm run ng -- test", "lint": "./node_modules/.bin/eslint . --ext .ts", @@ -51,17 +50,16 @@ "dev:ssr": "npm run generate-config && ng run mempool:serve-ssr", "serve:ssr": "npm run generate-config && node server.run.js", "build:ssr": "npm run build && ng run mempool:server:production && ./node_modules/typescript/bin/tsc server.run.ts", - "config:defaults:mempool": "node update-config.js TESTNET_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true LIQUID_TESTNET_ENABLED=true BISQ_ENABLED=true ITEMS_PER_PAGE=25 BASE_MODULE=mempool BLOCK_WEIGHT_UNITS=4000000 && npm run generate-config", - "config:defaults:liquid": "node update-config.js TESTNET_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true LIQUID_TESTNET_ENABLED=true BISQ_ENABLED=true ITEMS_PER_PAGE=25 BASE_MODULE=liquid BLOCK_WEIGHT_UNITS=300000 && npm run generate-config", - "config:defaults:bisq": "node update-config.js TESTNET_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true BISQ_ENABLED=true ITEMS_PER_PAGE=25 BASE_MODULE=bisq BLOCK_WEIGHT_UNITS=4000000 && npm run generate-config", + "config:defaults:mempool": "node update-config.js TESTNET_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true LIQUID_TESTNET_ENABLED=true ITEMS_PER_PAGE=25 BASE_MODULE=mempool BLOCK_WEIGHT_UNITS=4000000 && npm run generate-config", + "config:defaults:liquid": "node update-config.js TESTNET_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true LIQUID_TESTNET_ENABLED=true ITEMS_PER_PAGE=25 BASE_MODULE=liquid BLOCK_WEIGHT_UNITS=300000 && npm run generate-config", "prerender": "npm run ng -- run mempool:prerender", "cypress:open": "cypress open", "cypress:run": "cypress run", "cypress:run:record": "cypress run --record", - "cypress:open:ci": "node update-config.js TESTNET_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true BISQ_ENABLED=true ITEMS_PER_PAGE=25 && npm run generate-config && start-server-and-test serve:local-prod 4200 cypress:open", - "cypress:run:ci": "node update-config.js TESTNET_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true BISQ_ENABLED=true ITEMS_PER_PAGE=25 && npm run generate-config && start-server-and-test serve:local-prod 4200 cypress:run:record", - "cypress:open:ci:staging": "node update-config.js TESTNET_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true BISQ_ENABLED=true ITEMS_PER_PAGE=25 && npm run generate-config && start-server-and-test serve:local-staging 4200 cypress:open", - "cypress:run:ci:staging": "node update-config.js TESTNET_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true BISQ_ENABLED=true ITEMS_PER_PAGE=25 && npm run generate-config && start-server-and-test serve:local-staging 4200 cypress:run:record" + "cypress:open:ci": "node update-config.js TESTNET_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true ITEMS_PER_PAGE=25 && npm run generate-config && start-server-and-test serve:local-prod 4200 cypress:open", + "cypress:run:ci": "node update-config.js TESTNET_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true ITEMS_PER_PAGE=25 && npm run generate-config && start-server-and-test serve:local-prod 4200 cypress:run:record", + "cypress:open:ci:staging": "node update-config.js TESTNET_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true ITEMS_PER_PAGE=25 && npm run generate-config && start-server-and-test serve:local-staging 4200 cypress:open", + "cypress:run:ci:staging": "node update-config.js TESTNET_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true ITEMS_PER_PAGE=25 && npm run generate-config && start-server-and-test serve:local-staging 4200 cypress:run:record" }, "dependencies": { "@angular-devkit/build-angular": "^17.3.1", diff --git a/frontend/proxy.conf.js b/frontend/proxy.conf.js index f5384bef0..b63d343e2 100644 --- a/frontend/proxy.conf.js +++ b/frontend/proxy.conf.js @@ -22,7 +22,6 @@ PROXY_CONFIG = [ { context: ['*', '/api/**', '!/api/v1/ws', - '!/bisq', '!/bisq/**', '!/bisq/', '!/liquid', '!/liquid/**', '!/liquid/', '!/liquidtestnet', '!/liquidtestnet/**', '!/liquidtestnet/', '/testnet/api/**', '/signet/api/**' @@ -39,16 +38,6 @@ PROXY_CONFIG = [ secure: false, changeOrigin: true, }, - { - context: ['/api/bisq**', '/bisq/api/**'], - target: "https://bisq.markets", - pathRewrite: { - "^/api/bisq/": "/bisq/api" - }, - ws: true, - secure: false, - changeOrigin: true - }, { context: ['/api/liquid**', '/liquid/api/**'], target: "https://liquid.network", diff --git a/frontend/proxy.conf.local-esplora.js b/frontend/proxy.conf.local-esplora.js index a7137f3bc..0cdc9d459 100644 --- a/frontend/proxy.conf.local-esplora.js +++ b/frontend/proxy.conf.local-esplora.js @@ -67,40 +67,6 @@ if (configContent && configContent.BASE_MODULE === 'liquid') { ]); } - -if (configContent && configContent.BASE_MODULE === 'bisq') { - PROXY_CONFIG.push(...[ - { - context: ['/bisq/api/v1/ws'], - target: `http://127.0.0.1:8999`, - secure: false, - ws: true, - changeOrigin: true, - proxyTimeout: 30000, - pathRewrite: { - "^/bisq": "" - }, - }, - { - context: ['/bisq/api/v1/**'], - target: `http://127.0.0.1:8999`, - secure: false, - changeOrigin: true, - proxyTimeout: 30000, - }, - { - context: ['/bisq/api/**'], - target: `http://127.0.0.1:8999`, - secure: false, - changeOrigin: true, - proxyTimeout: 30000, - pathRewrite: { - "^/bisq/api/": "/api/v1/bisq/" - }, - } - ]); -} - PROXY_CONFIG.push(...[ { context: ['/testnet/api/v1/lightning/**'], diff --git a/frontend/proxy.conf.local.js b/frontend/proxy.conf.local.js index 3a502e0ed..e7bfeee70 100644 --- a/frontend/proxy.conf.local.js +++ b/frontend/proxy.conf.local.js @@ -67,40 +67,6 @@ if (configContent && configContent.BASE_MODULE === 'liquid') { ]); } - -if (configContent && configContent.BASE_MODULE === 'bisq') { - PROXY_CONFIG.push(...[ - { - context: ['/bisq/api/v1/ws'], - target: `http://localhost:8999`, - secure: false, - ws: true, - changeOrigin: true, - proxyTimeout: 30000, - pathRewrite: { - "^/bisq": "" - }, - }, - { - context: ['/bisq/api/v1/**'], - target: `http://localhost:8999`, - secure: false, - changeOrigin: true, - proxyTimeout: 30000, - }, - { - context: ['/bisq/api/**'], - target: `http://localhost:8999`, - secure: false, - changeOrigin: true, - proxyTimeout: 30000, - pathRewrite: { - "^/bisq/api/": "/api/v1/bisq/" - }, - } - ]); -} - PROXY_CONFIG.push(...[ { context: ['/testnet/api/v1/lightning/**'], diff --git a/frontend/proxy.conf.mixed.js b/frontend/proxy.conf.mixed.js index 76bb06607..db8af5d95 100644 --- a/frontend/proxy.conf.mixed.js +++ b/frontend/proxy.conf.mixed.js @@ -61,39 +61,6 @@ if (configContent && configContent.BASE_MODULE === 'liquid') { ]); } -if (configContent && configContent.BASE_MODULE === 'bisq') { - PROXY_CONFIG.push(...[ - { - context: ['/bisq/api/v1/ws'], - target: `http://localhost:8999`, - secure: false, - ws: true, - changeOrigin: true, - proxyTimeout: 30000, - pathRewrite: { - "^/bisq": "" - }, - }, - { - context: ['/bisq/api/v1/**'], - target: `http://localhost:8999`, - secure: false, - changeOrigin: true, - proxyTimeout: 30000, - }, - { - context: ['/bisq/api/**'], - target: `http://localhost:8999`, - secure: false, - changeOrigin: true, - proxyTimeout: 30000, - pathRewrite: { - "^/bisq/api/": "/api/v1/bisq/" - }, - }, - ]); -} - PROXY_CONFIG.push(...[ { context: ['/api/v1/services/**'], diff --git a/frontend/proxy.conf.staging.js b/frontend/proxy.conf.staging.js index 0cf366ca7..e24662038 100644 --- a/frontend/proxy.conf.staging.js +++ b/frontend/proxy.conf.staging.js @@ -5,7 +5,6 @@ let PROXY_CONFIG = require('./proxy.conf'); PROXY_CONFIG.forEach(entry => { entry.target = entry.target.replace("mempool.space", "mempool-staging.fra.mempool.space"); entry.target = entry.target.replace("liquid.network", "liquid-staging.fra.mempool.space"); - entry.target = entry.target.replace("bisq.markets", "bisq-staging.fra.mempool.space"); }); module.exports = PROXY_CONFIG; diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index 4bef1a76f..7ec9a37d3 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -170,13 +170,6 @@ let routes: Routes = [ }, ]; -if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'bisq') { - routes = [{ - path: '', - loadChildren: () => import('./bisq/bisq.module').then(m => m.BisqModule) - }]; -} - if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') { routes = [ { diff --git a/frontend/src/app/bisq/bisq-address/bisq-address.component.html b/frontend/src/app/bisq/bisq-address/bisq-address.component.html deleted file mode 100644 index 6b0d27c2c..000000000 --- a/frontend/src/app/bisq/bisq-address/bisq-address.component.html +++ /dev/null @@ -1,108 +0,0 @@ -
-

Address

- - - - - -
- -
- - -
- -
-
- - - - - - - - - - - - - - - -
Total received{{ totalReceived / 100 | number: '1.2-2' }} BSQ
Total sent{{ totalSent / 100 | number: '1.2-2' }} BSQ
Balance{{ (totalReceived - totalSent) / 100 | number: '1.2-2' }} BSQ
-
-
-
-
- -
-
-
- -
- -
- -

- - {{ i }} transaction - {{ i }} transactions -

- - - -
- - {{ tx.id | shortenString : 16 }} - {{ tx.id }} - -
- ‎{{ tx.time | date:'yyyy-MM-dd HH:mm' }} -
-
-
- - - -
-
- -
- - - -
-
-
- - - - - - - - - - - - -
-
-
-
- -
-
-
- -
- - - - Error loading address data. - - - -
- -
\ No newline at end of file diff --git a/frontend/src/app/bisq/bisq-address/bisq-address.component.scss b/frontend/src/app/bisq/bisq-address/bisq-address.component.scss deleted file mode 100644 index e7d0a9fd0..000000000 --- a/frontend/src/app/bisq/bisq-address/bisq-address.component.scss +++ /dev/null @@ -1,75 +0,0 @@ -.qr-wrapper { - background-color: #FFF; - padding: 10px; - padding-bottom: 5px; - display: inline-block; -} - -.qrcode-col { - text-align: center; -} - -.qrcode-col > div { - margin: 20px auto 5px; - @media (min-width: 768px) { - text-align: center; - margin: auto; - } -} - -.fiat { - display: block; - font-size: 13px; - @media (min-width: 768px) { - display: inline-block; - font-size: 14px; - margin-left: 10px; - } -} -.table { - tr td { - &:last-child { - text-align: right; - @media (min-width: 768px) { - text-align: left; - } - } - } -} - -h1 { - margin: 0px; - padding: 0px; - @media (min-width: 576px) { - float: left; - margin-right: 10px; - } -} - -.address-link { - line-height: 26px; - margin-left: 0px; - top: 14px; - position: relative; - display: flex; - flex-direction: row; - @media (min-width: 768px) { - line-height: 38px; - } -} - -.row{ - flex-direction: column; - @media (min-width: 576px) { - flex-direction: row; - } -} - -@media (max-width: 767.98px) { - .mobile-bottomcol { - margin-top: 15px; - } - .details-table td:first-child { - white-space: pre-wrap; - } -} \ No newline at end of file diff --git a/frontend/src/app/bisq/bisq-address/bisq-address.component.ts b/frontend/src/app/bisq/bisq-address/bisq-address.component.ts deleted file mode 100644 index 1e4f0a178..000000000 --- a/frontend/src/app/bisq/bisq-address/bisq-address.component.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { Component, OnInit, OnDestroy } from '@angular/core'; -import { SeoService } from '../../services/seo.service'; -import { switchMap, filter, catchError } from 'rxjs/operators'; -import { ParamMap, ActivatedRoute } from '@angular/router'; -import { Subscription, of } from 'rxjs'; -import { BisqTransaction } from '../bisq.interfaces'; -import { BisqApiService } from '../bisq-api.service'; -import { WebsocketService } from '../../services/websocket.service'; - -@Component({ - selector: 'app-bisq-address', - templateUrl: './bisq-address.component.html', - styleUrls: ['./bisq-address.component.scss'] -}) -export class BisqAddressComponent implements OnInit, OnDestroy { - transactions: BisqTransaction[]; - addressString: string; - isLoadingAddress = true; - error: any; - mainSubscription: Subscription; - - totalReceived = 0; - totalSent = 0; - - constructor( - private websocketService: WebsocketService, - private route: ActivatedRoute, - private seoService: SeoService, - private bisqApiService: BisqApiService, - ) { } - - ngOnInit() { - this.websocketService.want(['blocks']); - - this.mainSubscription = this.route.paramMap - .pipe( - switchMap((params: ParamMap) => { - this.error = undefined; - this.isLoadingAddress = true; - this.transactions = null; - document.body.scrollTo(0, 0); - this.addressString = params.get('id') || ''; - this.seoService.setTitle($localize`:@@bisq-address.component.browser-title:Address: ${this.addressString}:INTERPOLATION:`); - this.seoService.setDescription($localize`:@@meta.description.bisq.address:See current balance, pending transactions, and history of confirmed transactions for BSQ address ${this.addressString}:INTERPOLATION:.`); - - return this.bisqApiService.getAddress$(this.addressString) - .pipe( - catchError((err) => { - this.isLoadingAddress = false; - this.error = err; - this.seoService.logSoft404(); - console.log(err); - return of(null); - }) - ); - }), - filter((transactions) => transactions !== null) - ) - .subscribe((transactions: BisqTransaction[]) => { - this.transactions = transactions; - this.updateChainStats(); - this.isLoadingAddress = false; - }, - (error) => { - console.log(error); - this.error = error; - this.seoService.logSoft404(); - this.isLoadingAddress = false; - }); - } - - updateChainStats() { - const shortenedAddress = this.addressString.substr(1); - - this.totalSent = this.transactions.reduce((acc, tx) => - acc + tx.inputs - .filter((input) => input.address === shortenedAddress) - .reduce((a, input) => a + input.bsqAmount, 0), 0); - - this.totalReceived = this.transactions.reduce((acc, tx) => - acc + tx.outputs - .filter((output) => output.address === shortenedAddress) - .reduce((a, output) => a + output.bsqAmount, 0), 0); - } - - ngOnDestroy() { - this.mainSubscription.unsubscribe(); - } -} diff --git a/frontend/src/app/bisq/bisq-api.service.ts b/frontend/src/app/bisq/bisq-api.service.ts deleted file mode 100644 index 2d8994d86..000000000 --- a/frontend/src/app/bisq/bisq-api.service.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { Injectable } from '@angular/core'; -import { HttpClient, HttpResponse, HttpParams } from '@angular/common/http'; -import { Observable } from 'rxjs'; -import { BisqTransaction, BisqBlock, BisqStats, MarketVolume, Trade, Markets, Tickers, Offers, Currencies, HighLowOpenClose, SummarizedInterval } from './bisq.interfaces'; - -const API_BASE_URL = '/bisq/api'; - -@Injectable({ - providedIn: 'root' -}) -export class BisqApiService { - apiBaseUrl: string; - - constructor( - private httpClient: HttpClient, - ) { } - - getStats$(): Observable { - return this.httpClient.get(API_BASE_URL + '/stats'); - } - - getTransaction$(txId: string): Observable { - return this.httpClient.get(API_BASE_URL + '/tx/' + txId); - } - - listTransactions$(start: number, length: number, types: string[]): Observable> { - let params = new HttpParams(); - types.forEach((t: string) => { - params = params.append('types[]', t); - }); - return this.httpClient.get(API_BASE_URL + `/txs/${start}/${length}`, { params, observe: 'response' }); - } - - getBlock$(hash: string): Observable { - return this.httpClient.get(API_BASE_URL + '/block/' + hash); - } - - listBlocks$(start: number, length: number): Observable> { - return this.httpClient.get(API_BASE_URL + `/blocks/${start}/${length}`, { observe: 'response' }); - } - - getAddress$(address: string): Observable { - return this.httpClient.get(API_BASE_URL + '/address/' + address); - } - - getMarkets$(): Observable { - return this.httpClient.get(API_BASE_URL + '/markets/markets'); - } - - getMarketsTicker$(): Observable { - return this.httpClient.get(API_BASE_URL + '/markets/ticker'); - } - - getMarketsCurrencies$(): Observable { - return this.httpClient.get(API_BASE_URL + '/markets/currencies'); - } - - getMarketsHloc$(market: string, interval: 'minute' | 'half_hour' | 'hour' | 'half_day' | 'day' - | 'week' | 'month' | 'year' | 'auto'): Observable { - return this.httpClient.get(API_BASE_URL + '/markets/hloc?market=' + market + '&interval=' + interval); - } - - getMarketOffers$(market: string): Observable { - return this.httpClient.get(API_BASE_URL + '/markets/offers?market=' + market); - } - - getMarketTrades$(market: string): Observable { - return this.httpClient.get(API_BASE_URL + '/markets/trades?market=' + market); - } - - getMarketVolumesByTime$(period: string): Observable { - return this.httpClient.get(API_BASE_URL + '/markets/volumes/' + period); - } - - getAllVolumesDay$(): Observable { - return this.httpClient.get(API_BASE_URL + '/markets/volumes?interval=week'); - } -} diff --git a/frontend/src/app/bisq/bisq-block/bisq-block.component.html b/frontend/src/app/bisq/bisq-block/bisq-block.component.html deleted file mode 100644 index 4f79d8838..000000000 --- a/frontend/src/app/bisq/bisq-block/bisq-block.component.html +++ /dev/null @@ -1,114 +0,0 @@ -
- -
-

Block

-
- - {{ blockHeight }} - -
- - - -
-
-
- - - - - - - - - - -
Hash{{ block.hash | shortenString : 13 }}
Timestamp - ‎{{ block.time | date:'yyyy-MM-dd HH:mm' }} -
- () -
-
-
- -
-
- -
- -
- -

- - {{ i }} transaction - {{ i }} transactions -

- - - -
- - {{ tx.id | shortenString : 16 }} - {{ tx.id }} - -
- ‎{{ tx.time | date:'yyyy-MM-dd HH:mm' }} -
-
-
- - - -
-
- -
- - -
-
-
- - - - - - - - - - -
Hash
Timestamp
-
-
- - - - - - -
Previous hash
-
-
-
-
- - -
- -
- Error loading block -
- {{ error.status }}: {{ error.statusText }} -
-
- -
\ No newline at end of file diff --git a/frontend/src/app/bisq/bisq-block/bisq-block.component.scss b/frontend/src/app/bisq/bisq-block/bisq-block.component.scss deleted file mode 100644 index 04e0e1a35..000000000 --- a/frontend/src/app/bisq/bisq-block/bisq-block.component.scss +++ /dev/null @@ -1,44 +0,0 @@ -.td-width { - width: 140px; - @media (min-width: 768px) { - width: 175px; - } -} - -h1 { - margin: 0px; - padding: 0px; - @media (min-width: 576px) { - float: left; - margin-right: 10px; - } -} - -.row{ - flex-direction: column; - @media (min-width: 768px) { - flex-direction: row; - } -} - -.block-container { - .table { - tr td { - &:last-child { - text-align: right; - @media (min-width: 992px) { - text-align: left; - } - } - } - } - .fiat { - display: block; - font-size: 13px; - @media (min-width: 992px) { - display: inline-block; - font-size: 14px; - margin-left: 10px; - } - } -} \ No newline at end of file diff --git a/frontend/src/app/bisq/bisq-block/bisq-block.component.ts b/frontend/src/app/bisq/bisq-block/bisq-block.component.ts deleted file mode 100644 index 59bb16a9e..000000000 --- a/frontend/src/app/bisq/bisq-block/bisq-block.component.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { Component, OnInit, OnDestroy } from '@angular/core'; -import { BisqBlock } from '../../bisq/bisq.interfaces'; -import { Location } from '@angular/common'; -import { BisqApiService } from '../bisq-api.service'; -import { ActivatedRoute, ParamMap, Router } from '@angular/router'; -import { Subscription, of } from 'rxjs'; -import { switchMap, catchError } from 'rxjs/operators'; -import { SeoService } from '../../services/seo.service'; -import { ElectrsApiService } from '../../services/electrs-api.service'; -import { HttpErrorResponse } from '@angular/common/http'; -import { WebsocketService } from '../../services/websocket.service'; - -@Component({ - selector: 'app-bisq-block', - templateUrl: './bisq-block.component.html', - styleUrls: ['./bisq-block.component.scss'] -}) -export class BisqBlockComponent implements OnInit, OnDestroy { - block: BisqBlock; - subscription: Subscription; - blockHash = ''; - blockHeight = 0; - isLoading = true; - error: HttpErrorResponse | null; - - constructor( - private websocketService: WebsocketService, - private bisqApiService: BisqApiService, - private route: ActivatedRoute, - private seoService: SeoService, - private electrsApiService: ElectrsApiService, - private router: Router, - private location: Location, - ) { } - - ngOnInit(): void { - this.websocketService.want(['blocks']); - - this.subscription = this.route.paramMap - .pipe( - switchMap((params: ParamMap) => { - const blockHash = params.get('id') || ''; - document.body.scrollTo(0, 0); - this.isLoading = true; - this.error = null; - if (history.state.data && history.state.data.blockHeight) { - this.blockHeight = history.state.data.blockHeight; - } - if (history.state.data && history.state.data.block) { - this.blockHeight = history.state.data.block.height; - return of(history.state.data.block); - } - - let isBlockHeight = false; - if (/^[0-9]+$/.test(blockHash)) { - isBlockHeight = true; - } else { - this.blockHash = blockHash; - } - - if (isBlockHeight) { - return this.electrsApiService.getBlockHashFromHeight$(parseInt(blockHash, 10)) - .pipe( - switchMap((hash) => { - if (!hash) { - return; - } - this.blockHash = hash; - this.location.replaceState( - this.router.createUrlTree(['/bisq/block/', hash]).toString() - ); - this.seoService.updateCanonical(this.location.path()); - return this.bisqApiService.getBlock$(this.blockHash) - .pipe(catchError(this.caughtHttpError.bind(this))); - }), - catchError(this.caughtHttpError.bind(this)) - ); - } - - return this.bisqApiService.getBlock$(this.blockHash) - .pipe(catchError(this.caughtHttpError.bind(this))); - }) - ) - .subscribe((block: BisqBlock) => { - if (!block) { - this.seoService.logSoft404(); - return; - } - this.isLoading = false; - this.blockHeight = block.height; - this.seoService.setTitle($localize`:@@bisq-block.component.browser-title:Block ${block.height}:BLOCK_HEIGHT:: ${block.hash}:BLOCK_HASH:`); - this.seoService.setDescription($localize`:@@meta.description.bisq.block:See all BSQ transactions in Bitcoin block ${block.height}:BLOCK_HEIGHT: (block hash ${block.hash}:BLOCK_HASH:).`); - this.block = block; - }); - } - - ngOnDestroy() { - this.subscription.unsubscribe(); - } - - caughtHttpError(err: HttpErrorResponse){ - this.error = err; - this.seoService.logSoft404(); - return of(null); - } -} diff --git a/frontend/src/app/bisq/bisq-blocks/bisq-blocks.component.html b/frontend/src/app/bisq/bisq-blocks/bisq-blocks.component.html deleted file mode 100644 index 15f15b258..000000000 --- a/frontend/src/app/bisq/bisq-blocks/bisq-blocks.component.html +++ /dev/null @@ -1,40 +0,0 @@ -
-

BSQ Blocks

-
- -
- - - -
- - - - - - - - - - - - - - - -
HeightConfirmedTotal sentTransactions
{{ block.height }}{{ calculateTotalOutput(block) / 100 | number: '1.2-2' }} BSQ{{ block.txs.length }}
-
- -
- - -
-
-
-
- - - - - - diff --git a/frontend/src/app/bisq/bisq-blocks/bisq-blocks.component.scss b/frontend/src/app/bisq/bisq-blocks/bisq-blocks.component.scss deleted file mode 100644 index e8db46928..000000000 --- a/frontend/src/app/bisq/bisq-blocks/bisq-blocks.component.scss +++ /dev/null @@ -1,6 +0,0 @@ -.pagination-container { - float: none; - @media(min-width: 400px){ - float: right; - } -} diff --git a/frontend/src/app/bisq/bisq-blocks/bisq-blocks.component.ts b/frontend/src/app/bisq/bisq-blocks/bisq-blocks.component.ts deleted file mode 100644 index 7ab742655..000000000 --- a/frontend/src/app/bisq/bisq-blocks/bisq-blocks.component.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core'; -import { BisqApiService } from '../bisq-api.service'; -import { switchMap, map, take, mergeMap, tap } from 'rxjs/operators'; -import { Observable } from 'rxjs'; -import { BisqBlock, BisqOutput, BisqTransaction } from '../bisq.interfaces'; -import { SeoService } from '../../services/seo.service'; -import { ActivatedRoute, Router } from '@angular/router'; -import { WebsocketService } from '../../services/websocket.service'; - -@Component({ - selector: 'app-bisq-blocks', - templateUrl: './bisq-blocks.component.html', - styleUrls: ['./bisq-blocks.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class BisqBlocksComponent implements OnInit { - blocks$: Observable<[BisqBlock[], number]>; - page = 1; - itemsPerPage: number; - contentSpace = window.innerHeight - (165 + 75); - fiveItemsPxSize = 250; - loadingItems: number[]; - isLoading = true; - // @ts-ignore - paginationSize: 'sm' | 'lg' = 'md'; - paginationMaxSize = 5; - - constructor( - private websocketService: WebsocketService, - private bisqApiService: BisqApiService, - private seoService: SeoService, - private route: ActivatedRoute, - private router: Router, - ) { } - - ngOnInit(): void { - this.websocketService.want(['blocks']); - this.seoService.setTitle($localize`:@@8a7b4bd44c0ac71b2e72de0398b303257f7d2f54:Blocks`); - this.seoService.setDescription($localize`:@@meta.description.bisq.blocks:See a list of recent Bitcoin blocks with BSQ transactions, total BSQ sent per block, and more.`); - this.itemsPerPage = Math.max(Math.round(this.contentSpace / this.fiveItemsPxSize) * 5, 10); - this.loadingItems = Array(this.itemsPerPage); - if (document.body.clientWidth < 670) { - this.paginationSize = 'sm'; - this.paginationMaxSize = 3; - } - - this.blocks$ = this.route.queryParams - .pipe( - take(1), - tap((qp) => { - if (qp.page) { - this.page = parseInt(qp.page, 10); - } - }), - mergeMap(() => this.route.queryParams), - map((queryParams) => { - if (queryParams.page) { - const newPage = parseInt(queryParams.page, 10); - this.page = newPage; - return newPage; - } else { - this.page = 1; - } - return 1; - }), - switchMap((page) => this.bisqApiService.listBlocks$((page - 1) * this.itemsPerPage, this.itemsPerPage)), - map((response) => [response.body, parseInt(response.headers.get('x-total-count'), 10)]), - ); - } - - calculateTotalOutput(block: BisqBlock): number { - return block.txs.reduce((a: number, tx: BisqTransaction) => - a + tx.outputs.reduce((acc: number, output: BisqOutput) => acc + output.bsqAmount, 0), 0 - ); - } - - trackByFn(index: number) { - return index; - } - - pageChange(page: number) { - this.router.navigate([], { - queryParams: { page: page }, - queryParamsHandling: 'merge', - }); - } - - onResize(event: any) { - this.paginationMaxSize = event.target.innerWidth < 670 ? 3 : 5; - } -} diff --git a/frontend/src/app/bisq/bisq-dashboard/bisq-dashboard.component.html b/frontend/src/app/bisq/bisq-dashboard/bisq-dashboard.component.html deleted file mode 100644 index 132384fa9..000000000 --- a/frontend/src/app/bisq/bisq-dashboard/bisq-dashboard.component.html +++ /dev/null @@ -1,63 +0,0 @@ -
- -

Bisq Trading Volume

- -
- -
-
-
-
- - - -
- -

- -
-

- Markets - Bitcoin Markets -

- -
- - - - - - - - - - - - - - - -
Currency PriceVolume (7d) Trades (7d)
{{ ticker.name }}) - - - {{ ticker.last | currency: ticker.market.rsymbol }} - - - - {{ ticker.volume?.num_trades ? ticker.volume?.num_trades : 0 }}
-
- -

- -

Latest Trades

- - -
-
-
- - - - - - diff --git a/frontend/src/app/bisq/bisq-dashboard/bisq-dashboard.component.scss b/frontend/src/app/bisq/bisq-dashboard/bisq-dashboard.component.scss deleted file mode 100644 index 6248f9461..000000000 --- a/frontend/src/app/bisq/bisq-dashboard/bisq-dashboard.component.scss +++ /dev/null @@ -1,22 +0,0 @@ -#volumeHolder { - height: 500px; - background-color: #000; - overflow: hidden; - display: flex; - justify-content: center; -} - -.table { - max-width: 100%; - overflow: scroll; -} - -.loadingVolumes { - position: relative; - top: 45%; - z-index: 100; -} - -.container-info{ - overflow-x: scroll; -} \ No newline at end of file diff --git a/frontend/src/app/bisq/bisq-dashboard/bisq-dashboard.component.ts b/frontend/src/app/bisq/bisq-dashboard/bisq-dashboard.component.ts deleted file mode 100644 index 8834d09e9..000000000 --- a/frontend/src/app/bisq/bisq-dashboard/bisq-dashboard.component.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; -import { Observable, combineLatest, BehaviorSubject, of } from 'rxjs'; -import { map, share, switchMap } from 'rxjs/operators'; -import { SeoService } from '../../services/seo.service'; -import { StateService } from '../../services/state.service'; -import { WebsocketService } from '../../services/websocket.service'; -import { BisqApiService } from '../bisq-api.service'; -import { Trade } from '../bisq.interfaces'; - -@Component({ - selector: 'app-bisq-dashboard', - templateUrl: './bisq-dashboard.component.html', - styleUrls: ['./bisq-dashboard.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush -}) -export class BisqDashboardComponent implements OnInit { - tickers$: Observable; - volumes$: Observable; - trades$: Observable; - sort$ = new BehaviorSubject('trades'); - - allowCryptoCoins = ['usdc', 'l-btc', 'bsq']; - - constructor( - private websocketService: WebsocketService, - private bisqApiService: BisqApiService, - public stateService: StateService, - private seoService: SeoService, - ) { } - - ngOnInit(): void { - this.seoService.setTitle($localize`:@@meta.title.bisq.markets:Markets`); - this.seoService.setDescription($localize`:@@meta.description.bisq.markets:Explore the full Bitcoin ecosystem with The Mempool Open Source Project®. See Bisq market prices, trading activity, and more.`); - this.websocketService.want(['blocks']); - - this.volumes$ = this.bisqApiService.getAllVolumesDay$() - .pipe( - map((volumes) => { - const data = volumes.map((volume) => { - return { - time: volume.period_start, - value: volume.volume, - }; - }); - - const linesData = volumes.map((volume) => { - return { - time: volume.period_start, - value: volume.num_trades, - }; - }); - - return { - data: data, - linesData: linesData, - }; - }) - ); - - const getMarkets = this.bisqApiService.getMarkets$().pipe(share()); - - this.tickers$ = combineLatest([ - this.bisqApiService.getMarketsTicker$(), - getMarkets, - this.bisqApiService.getMarketVolumesByTime$('7d'), - ]) - .pipe( - map(([tickers, markets, volumes]) => { - - const newTickers = []; - for (const t in tickers) { - - if (this.stateService.env.BASE_MODULE !== 'bisq') { - const pair = t.split('_'); - if (pair[1] === 'btc' && this.allowCryptoCoins.indexOf(pair[0]) === -1) { - continue; - } - } - - const mappedTicker: any = tickers[t]; - - mappedTicker.pair_url = t; - mappedTicker.pair = t.replace('_', '/').toUpperCase(); - mappedTicker.market = markets[t]; - mappedTicker.volume = volumes[t]; - mappedTicker.name = `${mappedTicker.market.rtype === 'crypto' ? mappedTicker.market.lname : mappedTicker.market.rname} (${mappedTicker.market.rtype === 'crypto' ? mappedTicker.market.lsymbol : mappedTicker.market.rsymbol}`; - newTickers.push(mappedTicker); - } - return newTickers; - }), - switchMap((tickers) => combineLatest([this.sort$, of(tickers)])), - map(([sort, tickers]) => { - if (sort === 'trades') { - tickers.sort((a, b) => (b.volume && b.volume.num_trades || 0) - (a.volume && a.volume.num_trades || 0)); - } else if (sort === 'volumes') { - tickers.sort((a, b) => (b.volume && b.volume.volume || 0) - (a.volume && a.volume.volume || 0)); - } else if (sort === 'name') { - tickers.sort((a, b) => a.name.localeCompare(b.name)); - } - return tickers; - }) - ); - - this.trades$ = combineLatest([ - this.bisqApiService.getMarketTrades$('all'), - getMarkets, - ]) - .pipe( - map(([trades, markets]) => { - if (this.stateService.env.BASE_MODULE !== 'bisq') { - trades = trades.filter((trade) => { - const pair = trade.market.split('_'); - return !(pair[1] === 'btc' && this.allowCryptoCoins.indexOf(pair[0]) === -1); - }); - } - return trades.map((trade => { - trade._market = markets[trade.market]; - return trade; - })); - }) - ); - } - - trackByFn(index: number) { - return index; - } - - sort(by: string) { - this.sort$.next(by); - } - -} diff --git a/frontend/src/app/bisq/bisq-icon/bisq-icon.component.html b/frontend/src/app/bisq/bisq-icon/bisq-icon.component.html deleted file mode 100644 index 5ea603892..000000000 --- a/frontend/src/app/bisq/bisq-icon/bisq-icon.component.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/frontend/src/app/bisq/bisq-icon/bisq-icon.component.scss b/frontend/src/app/bisq/bisq-icon/bisq-icon.component.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/frontend/src/app/bisq/bisq-icon/bisq-icon.component.ts b/frontend/src/app/bisq/bisq-icon/bisq-icon.component.ts deleted file mode 100644 index 6137d5aa6..000000000 --- a/frontend/src/app/bisq/bisq-icon/bisq-icon.component.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { Component, ChangeDetectionStrategy, Input, OnChanges } from '@angular/core'; -import { IconPrefix, IconName } from '@fortawesome/fontawesome-common-types'; - -@Component({ - selector: 'app-bisq-icon', - templateUrl: './bisq-icon.component.html', - styleUrls: ['./bisq-icon.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class BisqIconComponent implements OnChanges { - @Input() txType: string; - - iconProp: [IconPrefix, IconName] = ['fas', 'leaf']; - color: string; - - constructor() { } - - ngOnChanges() { - switch (this.txType) { - case 'UNVERIFIED': - this.iconProp[1] = 'question'; - this.color = 'ffac00'; - break; - case 'INVALID': - this.iconProp[1] = 'exclamation-triangle'; - this.color = 'ff4500'; - break; - case 'GENESIS': - this.iconProp[1] = 'rocket'; - this.color = '25B135'; - break; - case 'TRANSFER_BSQ': - this.iconProp[1] = 'retweet'; - this.color = 'a3a3a3'; - break; - case 'PAY_TRADE_FEE': - this.iconProp[1] = 'leaf'; - this.color = '689f43'; - break; - case 'PROPOSAL': - this.iconProp[1] = 'file-alt'; - this.color = '6c8b3b'; - break; - case 'COMPENSATION_REQUEST': - this.iconProp[1] = 'money-bill'; - this.color = '689f43'; - break; - case 'REIMBURSEMENT_REQUEST': - this.iconProp[1] = 'money-bill'; - this.color = '04a908'; - break; - case 'BLIND_VOTE': - this.iconProp[1] = 'eye-slash'; - this.color = '07579a'; - break; - case 'VOTE_REVEAL': - this.iconProp[1] = 'eye'; - this.color = '4AC5FF'; - break; - case 'LOCKUP': - this.iconProp[1] = 'lock'; - this.color = '0056c4'; - break; - case 'UNLOCK': - this.iconProp[1] = 'lock-open'; - this.color = '1d965f'; - break; - case 'ASSET_LISTING_FEE': - this.iconProp[1] = 'file-alt'; - this.color = '6c8b3b'; - break; - case 'PROOF_OF_BURN': - this.iconProp[1] = 'file-alt'; - this.color = '6c8b3b'; - break; - case 'IRREGULAR': - this.iconProp[1] = 'exclamation-circle'; - this.color = 'ffd700'; - break; - default: - this.iconProp[1] = 'question'; - this.color = 'ffac00'; - } - // @ts-ignore - this.iconProp = this.iconProp.slice(); - } -} diff --git a/frontend/src/app/bisq/bisq-main-dashboard/bisq-main-dashboard.component.html b/frontend/src/app/bisq/bisq-main-dashboard/bisq-main-dashboard.component.html deleted file mode 100644 index cd99d6ed3..000000000 --- a/frontend/src/app/bisq/bisq-main-dashboard/bisq-main-dashboard.component.html +++ /dev/null @@ -1,124 +0,0 @@ -
- -
- -
-
-
-
-
Bisq Price Index
-
- - {{ usdPrice | currency:'USD':'symbol':'1.2-2' }} - -
-
-
-
-
-
-
-
Bisq Market Price
-
- - {{ bisqMarketPrice | currency:'USD':'symbol':'1.2-2' }} - -
-
-
-
-
- -
-
-
-
-
US Dollar - BTC/USD
-
- - - -
-
-
-
-
-
-
-
Bisq Trading Volume
-
- - - -
-
-
-
-
- -
- - -
-
-
-
- Markets - Bitcoin Markets -
- -
- - - - - - - - - - - - - -
Currency PriceTrades (7d)
{{ ticker.name }}) - - - {{ ticker.last | currency: ticker.market.rsymbol }} - - {{ ticker.volume?.num_trades ? ticker.volume?.num_trades : 0 }}
-
- - -
-
-
-
-
-
-
Latest Trades
- - -
-
-
-
-
- -
- - - - - - - - -
-
-
-
- - -
-
diff --git a/frontend/src/app/bisq/bisq-main-dashboard/bisq-main-dashboard.component.scss b/frontend/src/app/bisq/bisq-main-dashboard/bisq-main-dashboard.component.scss deleted file mode 100644 index 4eeeee64e..000000000 --- a/frontend/src/app/bisq/bisq-main-dashboard/bisq-main-dashboard.component.scss +++ /dev/null @@ -1,112 +0,0 @@ -#volumeHolder { - height: 500px; - background-color: #000; - overflow: hidden; - display: flex; - justify-content: center; -} - -.table { - max-width: 100%; - overflow: scroll; -} - -.loadingGraphs { - position: relative; - top: 45%; - z-index: 100; -} - -.table-container { - overflow: scroll; - scrollbar-width: none; - font-size: 13px; - &::-webkit-scrollbar { - display: none; - } - @media(min-width: 576px){ - font-size: 16px; - } - thead th{ - text-align: right; - &:first-child { - text-align: left; - } - &:nth-child(3) { - display: none; - @media(min-width: 1100px){ - display: table-cell; - } - } - } - tr { - td { - text-align: right; - &:first-child { - text-align: left; - } - &:nth-child(3) { - display: none; - @media(min-width: 1100px){ - display: table-cell; - } - } - } - } -} - - -.chart-container { - height: 300px; -} - -.big-fiat { - color: #3bcc49; - font-size: 26px; -} - - - .card { - background-color: #1d1f31; - height: 100%; - } - - .card-title { - color: #4a68b9; - font-size: 1rem; - } - - .info-block { - float: left; - width: 350px; - line-height: 25px; - } - - .progress { - display: inline-flex; - width: 100%; - background-color: #2d3348; - height: 1.1rem; - } - - .bg-warning { - background-color: #b58800 !important; - } - - .skeleton-loader { - max-width: 100%; - &.shorter { - max-width: 150px; - } - } - - .more-padding { - padding: 1.25rem 2rem 1.25rem 2rem; - } - - .graph-card { - height: 100%; - @media (min-width: 992px) { - height: 385px; - } - } diff --git a/frontend/src/app/bisq/bisq-main-dashboard/bisq-main-dashboard.component.ts b/frontend/src/app/bisq/bisq-main-dashboard/bisq-main-dashboard.component.ts deleted file mode 100644 index e7e4471c9..000000000 --- a/frontend/src/app/bisq/bisq-main-dashboard/bisq-main-dashboard.component.ts +++ /dev/null @@ -1,193 +0,0 @@ -import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; -import { Observable, combineLatest, BehaviorSubject, of } from 'rxjs'; -import { map, share, switchMap } from 'rxjs/operators'; -import { SeoService } from '../../services/seo.service'; -import { StateService } from '../../services/state.service'; -import { WebsocketService } from '../../services/websocket.service'; -import { BisqApiService } from '../bisq-api.service'; -import { Trade } from '../bisq.interfaces'; - -@Component({ - selector: 'app-main-bisq-dashboard', - templateUrl: './bisq-main-dashboard.component.html', - styleUrls: ['./bisq-main-dashboard.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush -}) -export class BisqMainDashboardComponent implements OnInit { - tickers$: Observable; - volumes$: Observable; - trades$: Observable; - sort$ = new BehaviorSubject('trades'); - hlocData$: Observable; - usdPrice$: Observable; - isLoadingGraph = true; - bisqMarketPrice = 0; - - allowCryptoCoins = ['usdc', 'l-btc', 'bsq']; - - constructor( - private websocketService: WebsocketService, - private bisqApiService: BisqApiService, - public stateService: StateService, - private seoService: SeoService, - ) { } - - ngOnInit(): void { - this.seoService.resetTitle(); - this.seoService.resetDescription(); - this.websocketService.want(['blocks']); - - this.usdPrice$ = this.stateService.conversions$.asObservable().pipe( - map((conversions) => conversions.USD) - ); - - this.volumes$ = this.bisqApiService.getAllVolumesDay$() - .pipe( - map((volumes) => { - const data = volumes.map((volume) => { - return { - time: volume.period_start, - value: volume.volume, - }; - }); - - const linesData = volumes.map((volume) => { - return { - time: volume.period_start, - value: volume.num_trades, - }; - }); - - return { - data: data, - linesData: linesData, - }; - }) - ); - - const getMarkets = this.bisqApiService.getMarkets$().pipe(share()); - - this.tickers$ = combineLatest([ - this.bisqApiService.getMarketsTicker$(), - getMarkets, - this.bisqApiService.getMarketVolumesByTime$('7d'), - ]) - .pipe( - map(([tickers, markets, volumes]) => { - - const newTickers = []; - for (const t in tickers) { - - if (this.stateService.env.BASE_MODULE !== 'bisq') { - const pair = t.split('_'); - if (pair[1] === 'btc' && this.allowCryptoCoins.indexOf(pair[0]) === -1) { - continue; - } - } - - const mappedTicker: any = tickers[t]; - - mappedTicker.pair_url = t; - mappedTicker.pair = t.replace('_', '/').toUpperCase(); - mappedTicker.market = markets[t]; - mappedTicker.volume = volumes[t]; - mappedTicker.name = `${mappedTicker.market.rtype === 'crypto' ? mappedTicker.market.lname : mappedTicker.market.rname} (${mappedTicker.market.rtype === 'crypto' ? mappedTicker.market.lsymbol : mappedTicker.market.rsymbol}`; - newTickers.push(mappedTicker); - } - return newTickers; - }), - switchMap((tickers) => combineLatest([this.sort$, of(tickers)])), - map(([sort, tickers]) => { - if (sort === 'trades') { - tickers.sort((a, b) => (b.volume && b.volume.num_trades || 0) - (a.volume && a.volume.num_trades || 0)); - } else if (sort === 'volumes') { - tickers.sort((a, b) => (b.volume && b.volume.volume || 0) - (a.volume && a.volume.volume || 0)); - } else if (sort === 'name') { - tickers.sort((a, b) => a.name.localeCompare(b.name)); - } - return tickers.slice(0, 10); - }) - ); - - this.trades$ = combineLatest([ - this.bisqApiService.getMarketTrades$('all'), - getMarkets, - ]) - .pipe( - map(([trades, markets]) => { - if (this.stateService.env.BASE_MODULE !== 'bisq') { - trades = trades.filter((trade) => { - const pair = trade.market.split('_'); - return !(pair[1] === 'btc' && this.allowCryptoCoins.indexOf(pair[0]) === -1); - }); - } - return trades.map((trade => { - trade._market = markets[trade.market]; - return trade; - })).slice(0, 10); - }) - ); - - this.hlocData$ = this.bisqApiService.getMarketsHloc$('btc_usd', 'day') - .pipe( - map((hlocData) => { - this.isLoadingGraph = false; - - hlocData = hlocData.map((h) => { - h.time = h.period_start; - return h; - }); - - const hlocVolume = hlocData.map((h) => { - return { - time: h.time, - value: h.volume_right, - color: h.close > h.avg ? 'rgba(0, 41, 74, 0.7)' : 'rgba(0, 41, 74, 1)', - }; - }); - - // Add whitespace - if (hlocData.length > 1) { - const newHloc = []; - newHloc.push(hlocData[0]); - - const period = 86400; - let periods = 0; - const startingDate = hlocData[0].period_start; - let index = 1; - while (true) { - periods++; - if (hlocData[index].period_start > startingDate + period * periods) { - newHloc.push({ - time: startingDate + period * periods, - }); - } else { - newHloc.push(hlocData[index]); - index++; - if (!hlocData[index]) { - break; - } - } - } - hlocData = newHloc; - } - - this.bisqMarketPrice = hlocData[hlocData.length - 1].close; - - return { - hloc: hlocData, - volume: hlocVolume, - }; - }), - ); - } - - trackByFn(index: number) { - return index; - } - - sort(by: string) { - this.sort$.next(by); - } - -} diff --git a/frontend/src/app/bisq/bisq-market/bisq-market.component.html b/frontend/src/app/bisq/bisq-market/bisq-market.component.html deleted file mode 100644 index bcf890d40..000000000 --- a/frontend/src/app/bisq/bisq-market/bisq-market.component.html +++ /dev/null @@ -1,112 +0,0 @@ -
- - - - -

{{ currency.market.rtype === 'crypto' ? currency.market.lname : currency.market.rname }} - {{ currency.pair }}

-
- {{ hlocData.hloc[hlocData.hloc.length - 1].close | currency: currency.market.rsymbol }} - {{ hlocData.hloc[hlocData.hloc.length - 1].close | number: '1.' + currency.market.rprecision + '-' + currency.market.rprecision }} {{ currency.market.rsymbol }} -
- -
-
- - - - - - - -
-
- -
- -
-
-
-
- -
- -
- - -
- - -
-
- -

- - -

Latest Trades

- - -
- -
-
- -
- - - -
-

- Buy Offers - Sell Offers -

-
- - - - - - - - - - - - - -
Price
- {{ offer.price | currency: market.rsymbol }} - {{ offer.price | number: '1.2-' + market.rprecision }} {{ market.rsymbol }} - - {{ offer.amount | currency: market.rsymbol }} - {{ offer.amount | number: '1.2-' + market.lprecision }} {{ market.lsymbol }} - - {{ offer.volume | currency: market.rsymbol }} - {{ offer.volume | number: '1.2-' + market.rprecision }} {{ market.rsymbol }} -
-
-
-
- - -
-
-
-
-
-
- -Amount ({{ i }}) diff --git a/frontend/src/app/bisq/bisq-market/bisq-market.component.scss b/frontend/src/app/bisq/bisq-market/bisq-market.component.scss deleted file mode 100644 index 232405ff4..000000000 --- a/frontend/src/app/bisq/bisq-market/bisq-market.component.scss +++ /dev/null @@ -1,46 +0,0 @@ -.priceheader { - font-size: 24px; - @media(min-width: 576px){ - float: left; - } -} - -.radio-form { - @media(min-width: 576px){ - float: right; - } -} - -.loadingChart { - z-index: 100; - position: absolute; - top: 50%; - left: 50%; -} - -#graphHolder { - height: 550px; - overflow: hidden; -} - -.col { - &:last-child{ - margin-top: 50px; - @media(min-width: 576px){ - margin-top: 0px; - } - } -} - -.table-container { - overflow: scroll; - -ms-overflow-style: none; - scrollbar-width: none; - font-size: 13px; - @media(min-width: 576px){ - font-size: 16px; - } - &::-webkit-scrollbar { - display: none; - } -} diff --git a/frontend/src/app/bisq/bisq-market/bisq-market.component.ts b/frontend/src/app/bisq/bisq-market/bisq-market.component.ts deleted file mode 100644 index f81b4d891..000000000 --- a/frontend/src/app/bisq/bisq-market/bisq-market.component.ts +++ /dev/null @@ -1,159 +0,0 @@ -import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core'; -import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; -import { ActivatedRoute, Router } from '@angular/router'; -import { combineLatest, merge, Observable, of } from 'rxjs'; -import { map, switchMap } from 'rxjs/operators'; -import { SeoService } from '../../services/seo.service'; -import { WebsocketService } from '../../services/websocket.service'; -import { BisqApiService } from '../bisq-api.service'; -import { OffersMarket, Trade } from '../bisq.interfaces'; - -@Component({ - selector: 'app-bisq-market', - templateUrl: './bisq-market.component.html', - styleUrls: ['./bisq-market.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush -}) -export class BisqMarketComponent implements OnInit, OnDestroy { - hlocData$: Observable; - currency$: Observable; - offers$: Observable; - trades$: Observable; - radioGroupForm: UntypedFormGroup; - defaultInterval = 'day'; - - isLoadingGraph = false; - - constructor( - private websocketService: WebsocketService, - private route: ActivatedRoute, - private bisqApiService: BisqApiService, - private formBuilder: UntypedFormBuilder, - private seoService: SeoService, - private router: Router, - ) { } - - ngOnInit(): void { - this.radioGroupForm = this.formBuilder.group({ - interval: [this.defaultInterval], - }); - - if (['half_hour', 'hour', 'half_day', 'day', 'week', 'month', 'year', 'auto'].indexOf(this.route.snapshot.fragment) > -1) { - this.radioGroupForm.controls.interval.setValue(this.route.snapshot.fragment, { emitEvent: false }); - } - - this.currency$ = this.bisqApiService.getMarkets$() - .pipe( - switchMap((markets) => combineLatest([of(markets), this.route.paramMap])), - map(([markets, routeParams]) => { - const pair = routeParams.get('pair'); - const pairUpperCase = pair.replace('_', '/').toUpperCase(); - this.seoService.setTitle($localize`:@@meta.title.bisq.market:Bisq market: ${pairUpperCase}`); - this.seoService.setDescription($localize`:@@meta.description.bisq.market:See price history, current buy/sell offers, and latest trades for the ${pairUpperCase} market on Bisq.`); - - return { - pair: pairUpperCase, - market: markets[pair], - }; - }) - ); - - this.trades$ = this.route.paramMap - .pipe( - map(routeParams => routeParams.get('pair')), - switchMap((marketPair) => this.bisqApiService.getMarketTrades$(marketPair)), - ); - - this.offers$ = this.route.paramMap - .pipe( - map(routeParams => routeParams.get('pair')), - switchMap((marketPair) => this.bisqApiService.getMarketOffers$(marketPair)), - map((offers) => offers[Object.keys(offers)[0]]) - ); - - this.hlocData$ = combineLatest([ - this.route.paramMap, - merge(this.radioGroupForm.get('interval').valueChanges, of(this.radioGroupForm.get('interval').value)), - ]) - .pipe( - switchMap(([routeParams, interval]) => { - this.isLoadingGraph = true; - const pair = routeParams.get('pair'); - return this.bisqApiService.getMarketsHloc$(pair, interval); - }), - map((hlocData) => { - this.isLoadingGraph = false; - - hlocData = hlocData.map((h) => { - h.time = h.period_start; - return h; - }); - - const hlocVolume = hlocData.map((h) => { - return { - time: h.time, - value: h.volume_right, - color: h.close > h.avg ? 'rgba(0, 41, 74, 0.7)' : 'rgba(0, 41, 74, 1)', - }; - }); - - // Add whitespace - if (hlocData.length > 1) { - const newHloc = []; - newHloc.push(hlocData[0]); - - const period = this.getUnixTimestampFromInterval(this.radioGroupForm.get('interval').value); // temp - let periods = 0; - const startingDate = hlocData[0].period_start; - let index = 1; - while (true) { - periods++; - if (hlocData[index].period_start > startingDate + period * periods) { - newHloc.push({ - time: startingDate + period * periods, - }); - } else { - newHloc.push(hlocData[index]); - index++; - if (!hlocData[index]) { - break; - } - } - } - hlocData = newHloc; - } - - return { - hloc: hlocData, - volume: hlocVolume, - }; - }), - ); - } - - setFragment(fragment: string) { - this.router.navigate([], { - relativeTo: this.route, - queryParamsHandling: 'merge', - fragment: fragment - }); - } - - ngOnDestroy(): void { - this.websocketService.stopTrackingBisqMarket(); - } - - getUnixTimestampFromInterval(interval: string): number { - switch (interval) { - case 'minute': return 60; - case 'half_hour': return 1800; - case 'hour': return 3600; - case 'half_day': return 43200; - case 'day': return 86400; - case 'week': return 604800; - case 'month': return 2592000; - case 'year': return 31579200; - } - } - -} diff --git a/frontend/src/app/bisq/bisq-stats/bisq-stats.component.html b/frontend/src/app/bisq/bisq-stats/bisq-stats.component.html deleted file mode 100644 index 765d24f9f..000000000 --- a/frontend/src/app/bisq/bisq-stats/bisq-stats.component.html +++ /dev/null @@ -1,86 +0,0 @@ -
-

BSQ statistics

-
- -
- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Existing amount{{ (stats.minted - stats.burnt) | number: '1.2-2' }} BSQ
Minted amount{{ stats.minted | number: '1.2-2' }} BSQ
Burnt amount{{ stats.burnt | number: '1.2-2' }} BSQ
Addresses{{ stats.addresses | number }}
Unspent TXOs{{ stats.unspent_txos | number }}
Spent TXOs{{ stats.spent_txos | number }}
Price
Market cap
- -
-
-
-
- - - - - Existing amount - - - - Minted amount - - - - Burnt amount - - - - Addresses - - - - Unspent TXOs - - - - Spent TXOs - - - - Price - - - - Market cap - - - - diff --git a/frontend/src/app/bisq/bisq-stats/bisq-stats.component.scss b/frontend/src/app/bisq/bisq-stats/bisq-stats.component.scss deleted file mode 100644 index 69cf8224e..000000000 --- a/frontend/src/app/bisq/bisq-stats/bisq-stats.component.scss +++ /dev/null @@ -1,18 +0,0 @@ -.td-width { - width: 250px; -} - -@media (max-width: 767.98px) { - .td-width { - width: 175px; - } -} - -.fiat { - display: block; - font-size: 13px; - @media (min-width: 768px) { - font-size: 14px; - display: inline-block; - } -} \ No newline at end of file diff --git a/frontend/src/app/bisq/bisq-stats/bisq-stats.component.ts b/frontend/src/app/bisq/bisq-stats/bisq-stats.component.ts deleted file mode 100644 index 58819d9cf..000000000 --- a/frontend/src/app/bisq/bisq-stats/bisq-stats.component.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { BisqApiService } from '../bisq-api.service'; -import { BisqStats } from '../bisq.interfaces'; -import { SeoService } from '../../services/seo.service'; -import { StateService } from '../../services/state.service'; -import { WebsocketService } from '../../services/websocket.service'; - -@Component({ - selector: 'app-bisq-stats', - templateUrl: './bisq-stats.component.html', - styleUrls: ['./bisq-stats.component.scss'] -}) -export class BisqStatsComponent implements OnInit { - isLoading = true; - stats: BisqStats; - price: number; - - constructor( - private websocketService: WebsocketService, - private bisqApiService: BisqApiService, - private seoService: SeoService, - private stateService: StateService, - ) { } - - ngOnInit() { - this.websocketService.want(['blocks']); - - this.seoService.setTitle($localize`:@@2a30a4cdb123a03facc5ab8c5b3e6d8b8dbbc3d4:BSQ statistics`); - this.seoService.setDescription($localize`:@@meta.description.bisq.stats:See high-level stats on the BSQ economy: supply metrics, number of addresses, BSQ price, market cap, and more.`); - this.stateService.bsqPrice$ - .subscribe((bsqPrice) => { - this.price = bsqPrice; - }); - - this.bisqApiService.getStats$() - .subscribe((stats) => { - this.isLoading = false; - this.stats = stats; - }); - } - -} diff --git a/frontend/src/app/bisq/bisq-trades/bisq-trades.component.html b/frontend/src/app/bisq/bisq-trades/bisq-trades.component.html deleted file mode 100644 index d64fc1ac5..000000000 --- a/frontend/src/app/bisq/bisq-trades/bisq-trades.component.html +++ /dev/null @@ -1,46 +0,0 @@ -
- - - - - - - - - - - - - - - - - - - - - -
DatePrice - - Amount -
- ‎{{ trade.trade_date | date:'yyyy-MM-dd HH:mm' }} - - {{ trade.price | currency: (trade._market || market).rsymbol }} - {{ trade.price | number: '1.2-' + (trade._market || market).rprecision }} {{ (trade._market || market).rsymbol }} - - {{ trade.amount | currency: (trade._market || market).rsymbol }} - {{ trade.amount | number: '1.2-' + (trade._market || market).lprecision }} {{ (trade._market || market).lsymbol }} - - {{ trade.volume | currency: (trade._market || market).rsymbol }} - {{ trade.volume | number: '1.2-' + (trade._market || market).rprecision }} {{ (trade._market || market).rsymbol }} -
-
- - - - - - - -Amount ({{ i }}) diff --git a/frontend/src/app/bisq/bisq-trades/bisq-trades.component.scss b/frontend/src/app/bisq/bisq-trades/bisq-trades.component.scss deleted file mode 100644 index a7c82834d..000000000 --- a/frontend/src/app/bisq/bisq-trades/bisq-trades.component.scss +++ /dev/null @@ -1,38 +0,0 @@ - -.table-container { - overflow: scroll; - scrollbar-width: none; - font-size: 13px; - &::-webkit-scrollbar { - display: none; - } - @media(min-width: 576px){ - font-size: 16px; - } - thead th{ - text-align: right; - &:first-child{ - text-align: left; - } - &:nth-child(2) { - display: none; - @media(min-width: 1100px){ - display: table-cell; - } - } - } - tr { - td { - text-align: right; - &:first-child{ - text-align: left; - } - &:nth-child(2) { - display: none; - @media(min-width: 1100px){ - display: table-cell; - } - } - } - } -} diff --git a/frontend/src/app/bisq/bisq-trades/bisq-trades.component.ts b/frontend/src/app/bisq/bisq-trades/bisq-trades.component.ts deleted file mode 100644 index b984e715c..000000000 --- a/frontend/src/app/bisq/bisq-trades/bisq-trades.component.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { ChangeDetectionStrategy, Component, Input, OnChanges, OnInit } from '@angular/core'; -import { Observable } from 'rxjs'; - -@Component({ - selector: 'app-bisq-trades', - templateUrl: './bisq-trades.component.html', - styleUrls: ['./bisq-trades.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class BisqTradesComponent implements OnChanges { - @Input() trades$: Observable; - @Input() market: any; - @Input() view: 'all' | 'small' = 'all'; - - loadingColumns = [1, 2, 3, 4]; - - ngOnChanges() { - if (this.view === 'small') { - this.loadingColumns = [1, 2, 3]; - } - } -} diff --git a/frontend/src/app/bisq/bisq-transaction-details/bisq-transaction-details.component.html b/frontend/src/app/bisq/bisq-transaction-details/bisq-transaction-details.component.html deleted file mode 100644 index a4033dcda..000000000 --- a/frontend/src/app/bisq/bisq-transaction-details/bisq-transaction-details.component.html +++ /dev/null @@ -1,36 +0,0 @@ -
-
-
- - - - - - - - - - - - - - - -
Inputs{{ totalInput / 100 | number: '1.2-2' }} BSQ
Outputs{{ totalOutput / 100 | number: '1.2-2' }} BSQ
Issued amount{{ totalIssued / 100 | number: '1.2-2' }} BSQ
-
-
- - - - - - - - - - - -
Type {{ tx.txTypeDisplayString }}
Version{{ tx.txVersion }}
-
-
-
\ No newline at end of file diff --git a/frontend/src/app/bisq/bisq-transaction-details/bisq-transaction-details.component.scss b/frontend/src/app/bisq/bisq-transaction-details/bisq-transaction-details.component.scss deleted file mode 100644 index f73dfea53..000000000 --- a/frontend/src/app/bisq/bisq-transaction-details/bisq-transaction-details.component.scss +++ /dev/null @@ -1,22 +0,0 @@ -@media (max-width: 767.98px) { - .td-width { - width: 150px; - } - .mobile-even tr:nth-of-type(even) { - background-color: #181b2d; - } - .mobile-even tr:nth-of-type(odd) { - background-color: inherit; - } -} - -.table { - tr td { - &:last-child{ - text-align: right; - @media(min-width: 768px){ - text-align: left; - } - } - } -} \ No newline at end of file diff --git a/frontend/src/app/bisq/bisq-transaction-details/bisq-transaction-details.component.ts b/frontend/src/app/bisq/bisq-transaction-details/bisq-transaction-details.component.ts deleted file mode 100644 index 9728372c3..000000000 --- a/frontend/src/app/bisq/bisq-transaction-details/bisq-transaction-details.component.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Component, ChangeDetectionStrategy, Input, OnChanges } from '@angular/core'; -import { BisqTransaction } from '../../bisq/bisq.interfaces'; - -@Component({ - selector: 'app-bisq-transaction-details', - templateUrl: './bisq-transaction-details.component.html', - styleUrls: ['./bisq-transaction-details.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class BisqTransactionDetailsComponent implements OnChanges { - @Input() tx: BisqTransaction; - - totalInput: number; - totalOutput: number; - totalIssued: number; - - constructor() { } - - ngOnChanges() { - this.totalInput = this.tx.inputs.filter((input) => input.isVerified).reduce((acc, input) => acc + input.bsqAmount, 0); - this.totalOutput = this.tx.outputs.filter((output) => output.isVerified).reduce((acc, output) => acc + output.bsqAmount, 0); - this.totalIssued = this.tx.outputs - .filter((output) => output.isVerified && output.txOutputType === 'ISSUANCE_CANDIDATE_OUTPUT') - .reduce((acc, output) => acc + output.bsqAmount, 0); - } -} diff --git a/frontend/src/app/bisq/bisq-transaction/bisq-transaction.component.html b/frontend/src/app/bisq/bisq-transaction/bisq-transaction.component.html deleted file mode 100644 index 85abafee0..000000000 --- a/frontend/src/app/bisq/bisq-transaction/bisq-transaction.component.html +++ /dev/null @@ -1,216 +0,0 @@ -
- - -
-
-

Transaction

-
- - - - - - - - - -
-
- -
-
-
- -
- -
-
-
- - - - - - - - - - - - - - - -
Timestamp - ‎{{ bisqTx.time | date:'yyyy-MM-dd HH:mm' }} -
- () -
-
Included in block - {{ bisqTx.blockHeight }} -
Features - - - - -
-
-
- - - - - - - - - - - - - - -
Burnt amount - {{ bisqTx.burntFee / 100 | number: '1.2-2' }} BSQ -
Fee per vByteFee per weight unit - -   - -
-
- -
-
- -
- -
-

Details

-
- - - -
- -
-

Inputs & Outputs

-
- - - -
-
- - - -
- -
-
-

Transaction

-
-
- -
-
-
- - - - - - - - - - - - - - - -
-
-
- - - - - - - - - - - -
-
-
-
- -
- -
-

Details

-
- -
- - - - - - - - - - - - - - -
-
- -
- -
-

Inputs & Outputs

-
- -
-
-
- - - - - - -
-
-
- - - - - - -
-
-
-
- -
- - -
- -
- Error loading Bisq transaction -

- {{ error.status }}: {{ error.statusText }} -
-
- -
diff --git a/frontend/src/app/bisq/bisq-transaction/bisq-transaction.component.ts b/frontend/src/app/bisq/bisq-transaction/bisq-transaction.component.ts deleted file mode 100644 index 7e3456464..000000000 --- a/frontend/src/app/bisq/bisq-transaction/bisq-transaction.component.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { Component, OnInit, OnDestroy } from '@angular/core'; -import { ActivatedRoute, ParamMap, Router } from '@angular/router'; -import { BisqTransaction } from '../../bisq/bisq.interfaces'; -import { switchMap, map, catchError } from 'rxjs/operators'; -import { of, Observable, Subscription } from 'rxjs'; -import { StateService } from '../../services/state.service'; -import { Block, Transaction } from '../../interfaces/electrs.interface'; -import { BisqApiService } from '../bisq-api.service'; -import { SeoService } from '../../services/seo.service'; -import { ElectrsApiService } from '../../services/electrs-api.service'; -import { HttpErrorResponse } from '@angular/common/http'; -import { WebsocketService } from '../../services/websocket.service'; - -@Component({ - selector: 'app-bisq-transaction', - templateUrl: './bisq-transaction.component.html', - styleUrls: ['./../../components/transaction/transaction.component.scss'] -}) -export class BisqTransactionComponent implements OnInit, OnDestroy { - bisqTx: BisqTransaction; - tx: Transaction; - latestBlock$: Observable; - txId: string; - price: number; - isLoading = true; - isLoadingTx = true; - error = null; - subscription: Subscription; - - constructor( - private websocketService: WebsocketService, - private route: ActivatedRoute, - private bisqApiService: BisqApiService, - private electrsApiService: ElectrsApiService, - private stateService: StateService, - private seoService: SeoService, - private router: Router, - ) { } - - ngOnInit(): void { - this.websocketService.want(['blocks']); - - this.subscription = this.route.paramMap.pipe( - switchMap((params: ParamMap) => { - this.isLoading = true; - this.isLoadingTx = true; - this.error = null; - document.body.scrollTo(0, 0); - this.txId = params.get('id') || ''; - this.seoService.setTitle($localize`:@@bisq.transaction.browser-title:Transaction: ${this.txId}:INTERPOLATION:`); - this.seoService.setDescription($localize`:@@meta.description.bisq.transaction:See inputs, outputs, transaction type, burnt amount, and more for transaction with txid ${this.txId}:INTERPOLATION:.`); - if (history.state.data) { - return of(history.state.data); - } - return this.bisqApiService.getTransaction$(this.txId) - .pipe( - catchError((bisqTxError: HttpErrorResponse) => { - if (bisqTxError.status === 404) { - return this.electrsApiService.getTransaction$(this.txId) - .pipe( - map((tx) => { - if (tx.status.confirmed) { - this.error = { - status: 200, - statusText: 'Transaction is confirmed but not available in the Bisq database, please try reloading this page.' - }; - return null; - } - return tx; - }), - catchError((txError: HttpErrorResponse) => { - console.log(txError); - this.error = txError; - this.seoService.logSoft404(); - return of(null); - }) - ); - } - this.error = bisqTxError; - this.seoService.logSoft404(); - return of(null); - }) - ); - }), - switchMap((tx) => { - if (!tx) { - return of(null); - } - - if (tx.version) { - if (this.stateService.env.BASE_MODULE === 'bisq') { - window.location.replace('https://mempool.space/tx/' + this.txId); - } else { - this.router.navigate(['/tx/', this.txId], { state: { data: tx, bsqTx: true }}); - } - return of(null); - } - - this.bisqTx = tx; - this.isLoading = false; - - return this.electrsApiService.getTransaction$(this.txId); - }), - ) - .subscribe((tx) => { - this.isLoadingTx = false; - - if (!tx) { - this.seoService.logSoft404(); - return; - } - - this.tx = tx; - }, - (error) => { - this.error = error; - }); - - this.latestBlock$ = this.stateService.blocks$.pipe(map((blocks) => blocks[0])); - - this.stateService.bsqPrice$ - .subscribe((bsqPrice) => { - this.price = bsqPrice; - }); - } - - ngOnDestroy() { - this.subscription.unsubscribe(); - } -} diff --git a/frontend/src/app/bisq/bisq-transactions/bisq-transactions.component.html b/frontend/src/app/bisq/bisq-transactions/bisq-transactions.component.html deleted file mode 100644 index bc22414ca..000000000 --- a/frontend/src/app/bisq/bisq-transactions/bisq-transactions.component.html +++ /dev/null @@ -1,56 +0,0 @@ -
-

BSQ Transactions

- -
-
- -
-
- -
- -
- - - - - - - - - - - - - - - - - - - - -
TXIDTypeAmountConfirmedHeight
{{ tx.id | slice : 0 : 8 }} - - {{ getStringByTxType(tx.txType) }} - - - - {{ tx.burntFee / 100 | number: '1.2-2' }} BSQ - - - {{ calculateTotalOutput(tx.outputs) / 100 | number: '1.2-2' }} BSQ - - {{ tx.blockHeight }}
- -
- - -
-
- - - - - - diff --git a/frontend/src/app/bisq/bisq-transactions/bisq-transactions.component.scss b/frontend/src/app/bisq/bisq-transactions/bisq-transactions.component.scss deleted file mode 100644 index a42d55e5e..000000000 --- a/frontend/src/app/bisq/bisq-transactions/bisq-transactions.component.scss +++ /dev/null @@ -1,23 +0,0 @@ -label { - padding: 0.25rem 1rem; - white-space: nowrap; -} - -:host ::ng-deep .dropdown-menu { - right: 0px; - left: inherit; -} - -.pagination-container { - float: none; - @media(min-width: 400px){ - float: right; - } -} - -.container-xl { - padding-bottom: 60px; - @media(min-width: 400px){ - padding-bottom: 100px; - } -} \ No newline at end of file diff --git a/frontend/src/app/bisq/bisq-transactions/bisq-transactions.component.ts b/frontend/src/app/bisq/bisq-transactions/bisq-transactions.component.ts deleted file mode 100644 index be5455639..000000000 --- a/frontend/src/app/bisq/bisq-transactions/bisq-transactions.component.ts +++ /dev/null @@ -1,166 +0,0 @@ -import { Component, OnInit, ChangeDetectionStrategy, ChangeDetectorRef, OnDestroy } from '@angular/core'; -import { BisqTransaction, BisqOutput } from '../bisq.interfaces'; - -import { Observable, Subscription } from 'rxjs'; -import { switchMap, map, tap } from 'rxjs/operators'; -import { BisqApiService } from '../bisq-api.service'; -import { SeoService } from '../../services/seo.service'; -import { UntypedFormGroup, UntypedFormBuilder } from '@angular/forms'; -import { Router, ActivatedRoute } from '@angular/router'; -import { IMultiSelectOption, IMultiSelectSettings, IMultiSelectTexts } from '../../components/ngx-bootstrap-multiselect/types' -import { WebsocketService } from '../../services/websocket.service'; - -@Component({ - selector: 'app-bisq-transactions', - templateUrl: './bisq-transactions.component.html', - styleUrls: ['./bisq-transactions.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush -}) -export class BisqTransactionsComponent implements OnInit, OnDestroy { - transactions$: Observable<[BisqTransaction[], number]>; - page = 1; - itemsPerPage = 50; - fiveItemsPxSize = 250; - isLoading = true; - loadingItems: number[]; - radioGroupForm: UntypedFormGroup; - types: string[] = []; - radioGroupSubscription: Subscription; - - txTypeOptions: IMultiSelectOption[] = [ - { id: 1, name: $localize`Asset listing fee` }, - { id: 2, name: $localize`Blind vote` }, - { id: 3, name: $localize`Compensation request` }, - { id: 4, name: $localize`Genesis` }, - { id: 13, name: $localize`Irregular` }, - { id: 5, name: $localize`Lockup` }, - { id: 6, name: $localize`Pay trade fee` }, - { id: 7, name: $localize`Proof of burn` }, - { id: 8, name: $localize`Proposal` }, - { id: 9, name: $localize`Reimbursement request` }, - { id: 10, name: $localize`Transfer BSQ` }, - { id: 11, name: $localize`Unlock` }, - { id: 12, name: $localize`Vote reveal` }, - ]; - txTypesDefaultChecked = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]; - - txTypeDropdownSettings: IMultiSelectSettings = { - buttonClasses: 'btn btn-primary btn-sm', - displayAllSelectedText: true, - showCheckAll: true, - showUncheckAll: true, - maxHeight: '500px', - fixedTitle: true, - }; - - txTypeDropdownTexts: IMultiSelectTexts = { - defaultTitle: $localize`:@@bisq-transactions.filter:Filter`, - checkAll: $localize`:@@bisq-transactions.selectall:Select all`, - uncheckAll: $localize`:@@bisq-transactions.unselectall:Unselect all`, - }; - - // @ts-ignore - paginationSize: 'sm' | 'lg' = 'md'; - paginationMaxSize = 5; - - txTypes = ['ASSET_LISTING_FEE', 'BLIND_VOTE', 'COMPENSATION_REQUEST', 'GENESIS', 'LOCKUP', 'PAY_TRADE_FEE', - 'PROOF_OF_BURN', 'PROPOSAL', 'REIMBURSEMENT_REQUEST', 'TRANSFER_BSQ', 'UNLOCK', 'VOTE_REVEAL', 'IRREGULAR']; - - constructor( - private websocketService: WebsocketService, - private bisqApiService: BisqApiService, - private seoService: SeoService, - private formBuilder: UntypedFormBuilder, - private route: ActivatedRoute, - private router: Router, - private cd: ChangeDetectorRef, - ) { } - - ngOnInit(): void { - this.websocketService.want(['blocks']); - this.seoService.setTitle($localize`:@@add4cd82e3e38a3110fe67b3c7df56e9602644ee:Transactions`); - this.seoService.setDescription($localize`:@@meta.description.bisq.transactions:See recent BSQ transactions: amount, txid, associated Bitcoin block, transaction type, and more.`); - - this.radioGroupForm = this.formBuilder.group({ - txTypes: [this.txTypesDefaultChecked], - }); - - this.loadingItems = Array(this.itemsPerPage); - - if (document.body.clientWidth < 670) { - this.paginationSize = 'sm'; - this.paginationMaxSize = 3; - } - - this.transactions$ = this.route.queryParams - .pipe( - tap((queryParams) => { - if (queryParams.page) { - const newPage = parseInt(queryParams.page, 10); - this.page = newPage; - } else { - this.page = 1; - } - if (queryParams.types) { - const types = queryParams.types.split(',').map((str: string) => parseInt(str, 10)); - this.types = types.map((id: number) => this.txTypes[id - 1]); - this.radioGroupForm.get('txTypes').setValue(types, { emitEvent: false }); - } else { - this.types = []; - this.radioGroupForm.get('txTypes').setValue([], { emitEvent: false }); - } - this.cd.markForCheck(); - }), - switchMap(() => this.bisqApiService.listTransactions$((this.page - 1) * this.itemsPerPage, this.itemsPerPage, this.types)), - map((response) => [response.body, parseInt(response.headers.get('x-total-count'), 10)]) - ); - - this.radioGroupSubscription = this.radioGroupForm.valueChanges - .subscribe((data) => { - this.types = data.txTypes.map((id: number) => this.txTypes[id - 1]); - if (this.types.length === this.txTypes.length) { - this.types = []; - } - this.page = 1; - this.typesChanged(data.txTypes); - this.cd.markForCheck(); - }); - } - - pageChange(page: number) { - this.router.navigate([], { - relativeTo: this.route, - queryParams: { page: page }, - queryParamsHandling: 'merge', - }); - } - - typesChanged(types: number[]) { - this.router.navigate([], { - relativeTo: this.route, - queryParams: { types: types.join(','), page: 1 }, - queryParamsHandling: 'merge', - }); - } - - calculateTotalOutput(outputs: BisqOutput[]): number { - return outputs.reduce((acc: number, output: BisqOutput) => acc + output.bsqAmount, 0); - } - - getStringByTxType(type: string) { - const id = this.txTypes.indexOf(type) + 1; - return this.txTypeOptions.find((type) => id === type.id).name; - } - - trackByFn(index: number) { - return index; - } - - onResize(event: any) { - this.paginationMaxSize = event.target.innerWidth < 670 ? 3 : 5; - } - - ngOnDestroy(): void { - this.radioGroupSubscription.unsubscribe(); - } -} diff --git a/frontend/src/app/bisq/bisq-transfers/bisq-transfers.component.html b/frontend/src/app/bisq/bisq-transfers/bisq-transfers.component.html deleted file mode 100644 index dc4993e90..000000000 --- a/frontend/src/app/bisq/bisq-transfers/bisq-transfers.component.html +++ /dev/null @@ -1,83 +0,0 @@ -
- - -
-
- Burnt amount: {{ tx.burntFee / 100 | number: '1.2-2' }} BSQ -
- -
- - -   - - -
-
-
- -
\ No newline at end of file diff --git a/frontend/src/app/bisq/bisq-transfers/bisq-transfers.component.scss b/frontend/src/app/bisq/bisq-transfers/bisq-transfers.component.scss deleted file mode 100644 index bffd646d8..000000000 --- a/frontend/src/app/bisq/bisq-transfers/bisq-transfers.component.scss +++ /dev/null @@ -1,103 +0,0 @@ - -.arrow-td { - width: 20px; -} -.green, .grey, .red { - font-size: 16px; - top: -2px; - position: relative; - @media( min-width: 576px){ - font-size: 19px; - } -} - -.green { - color:#28a745; -} - -.red { - color:#dc3545; -} - -.grey { - color:#6c757d; -} - -@media (max-width: 767.98px) { - .mobile-bottomcol { - margin-top: 15px; - } - .details-table td:first-child { - white-space: pre-wrap; - } -} - - -.details-table { - margin-top: 5px; -} - -.details-table td { - padding: 0.75rem; -} - -.details-table td:nth-child(2) { - word-break: break-all; - white-space: normal; - font-family: "Courier New", Courier, monospace; - font-size: 12px; -} - -.smaller-text { - font-size: 12px; - @media (min-width: 576px) { - font-size: 14px !important; - } -} - -.longer { - max-width: 100% !important; - width: 200px; - display: inline-block; -} - -.row{ - flex-direction: column; - @media (min-width: 992px) { - flex-direction: row; - } -} - -.extra-info { - display: inline-table; - .fiat { - font-size: 14px; - display: block; - text-align: right; - } -} - -.transaction-fee { - display: block; - margin: 0px auto 5px; - @media (min-width: 576px) { - display: inline-table; - } -} - - -.fiat { - margin-left: 10px; - font-size: 13px; - @media (min-width: 576px) { - font-size: 14px; - } -} - -.btn-container { - text-align: right; - @media (min-width: 576px) { - display: inline-table; - float: right; - } -} \ No newline at end of file diff --git a/frontend/src/app/bisq/bisq-transfers/bisq-transfers.component.ts b/frontend/src/app/bisq/bisq-transfers/bisq-transfers.component.ts deleted file mode 100644 index a46cbf07f..000000000 --- a/frontend/src/app/bisq/bisq-transfers/bisq-transfers.component.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Component, OnInit, ChangeDetectionStrategy, Input, OnChanges } from '@angular/core'; -import { BisqTransaction } from '../../bisq/bisq.interfaces'; -import { StateService } from '../../services/state.service'; -import { map } from 'rxjs/operators'; -import { Observable } from 'rxjs'; -import { Block } from '../../interfaces/electrs.interface'; - -@Component({ - selector: 'app-bisq-transfers', - templateUrl: './bisq-transfers.component.html', - styleUrls: ['./bisq-transfers.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush -}) -export class BisqTransfersComponent implements OnInit, OnChanges { - @Input() tx: BisqTransaction; - @Input() showConfirmations = false; - - totalOutput: number; - latestBlock$: Observable; - - constructor( - private stateService: StateService, - ) { } - - trackByIndexFn(index: number) { - return index; - } - - ngOnInit() { - this.latestBlock$ = this.stateService.blocks$.pipe(map((blocks) => blocks[0])); - } - - ngOnChanges() { - this.totalOutput = this.tx.outputs.filter((output) => output.isVerified).reduce((acc, output) => acc + output.bsqAmount, 0); - } - - switchCurrency() { - const oldvalue = !this.stateService.viewFiat$.value; - this.stateService.viewFiat$.next(oldvalue); - } - -} diff --git a/frontend/src/app/bisq/bisq.interfaces.ts b/frontend/src/app/bisq/bisq.interfaces.ts deleted file mode 100644 index fb04667eb..000000000 --- a/frontend/src/app/bisq/bisq.interfaces.ts +++ /dev/null @@ -1,261 +0,0 @@ - -export interface BisqBlocks { - chainHeight: number; - blocks: BisqBlock[]; -} - -export interface BisqBlock { - height: number; - time: number; - hash: string; - previousBlockHash: string; - txs: BisqTransaction[]; -} - -export interface BisqTransaction { - txVersion: string; - id: string; - blockHeight: number; - blockHash: string; - time: number; - inputs: BisqInput[]; - outputs: BisqOutput[]; - txType: string; - txTypeDisplayString: string; - burntFee: number; - invalidatedBsq: number; - unlockBlockHeight: number; -} - -interface BisqInput { - spendingTxOutputIndex: number; - spendingTxId: string; - bsqAmount: number; - isVerified: boolean; - address: string; - time: number; -} - -export interface BisqOutput { - txVersion: string; - txId: string; - index: number; - bsqAmount: number; - btcAmount: number; - height: number; - isVerified: boolean; - burntFee: number; - invalidatedBsq: number; - address: string; - scriptPubKey: BisqScriptPubKey; - spentInfo?: SpentInfo; - time: any; - txType: string; - txTypeDisplayString: string; - txOutputType: string; - txOutputTypeDisplayString: string; - lockTime: number; - isUnspent: boolean; - opReturn?: string; -} - -export interface BisqStats { - minted: number; - burnt: number; - addresses: number; - unspent_txos: number; - spent_txos: number; -} - -interface BisqScriptPubKey { - addresses: string[]; - asm: string; - hex: string; - reqSigs?: number; - type: string; -} - -interface SpentInfo { - height: number; - inputIndex: number; - txId: string; -} - - -export interface BisqTrade { - direction: string; - price: string; - amount: string; - volume: string; - payment_method: string; - trade_id: string; - trade_date: number; - market?: string; -} - -export interface Currencies { [txid: string]: Currency; } - -export interface Currency { - code: string; - name: string; - precision: number; - - _type: string; -} - -export interface Depth { [market: string]: Market; } - -interface Market { - 'buys': string[]; - 'sells': string[]; -} - -export interface HighLowOpenClose { - period_start: number | string; - open: string; - high: string; - low: string; - close: string; - volume_left: string; - volume_right: string; - avg: string; -} - -export interface Markets { [txid: string]: Pair; } - -interface Pair { - pair: string; - lname: string; - rname: string; - lsymbol: string; - rsymbol: string; - lprecision: number; - rprecision: number; - ltype: string; - rtype: string; - name: string; -} - -export interface Offers { [market: string]: OffersMarket; } - -export interface OffersMarket { - buys: Offer[] | null; - sells: Offer[] | null; -} - -export interface OffersData { - direction: string; - currencyCode: string; - minAmount: number; - amount: number; - price: number; - date: number; - useMarketBasedPrice: boolean; - marketPriceMargin: number; - paymentMethod: string; - id: string; - currencyPair: string; - primaryMarketDirection: string; - priceDisplayString: string; - primaryMarketAmountDisplayString: string; - primaryMarketMinAmountDisplayString: string; - primaryMarketVolumeDisplayString: string; - primaryMarketMinVolumeDisplayString: string; - primaryMarketPrice: number; - primaryMarketAmount: number; - primaryMarketMinAmount: number; - primaryMarketVolume: number; - primaryMarketMinVolume: number; -} - -export interface Offer { - offer_id: string; - offer_date: number; - direction: string; - min_amount: string; - amount: string; - price: string; - volume: string; - payment_method: string; - offer_fee_txid: any; -} - -export interface Tickers { [market: string]: Ticker | null; } - -export interface Ticker { - last: string; - high: string; - low: string; - volume_left: string; - volume_right: string; - buy: string | null; - sell: string | null; -} - -export interface Trade { - market?: string; - price: string; - amount: string; - volume: string; - payment_method: string; - trade_id: string; - trade_date: number; - _market: Pair; -} - -export interface TradesData { - currency: string; - direction: string; - tradePrice: number; - tradeAmount: number; - tradeDate: number; - paymentMethod: string; - offerDate: number; - useMarketBasedPrice: boolean; - marketPriceMargin: number; - offerAmount: number; - offerMinAmount: number; - offerId: string; - depositTxId?: string; - currencyPair: string; - primaryMarketDirection: string; - primaryMarketTradePrice: number; - primaryMarketTradeAmount: number; - primaryMarketTradeVolume: number; - - _market: string; - _tradePriceStr: string; - _tradeAmountStr: string; - _tradeVolumeStr: string; - _offerAmountStr: string; - _tradePrice: number; - _tradeAmount: number; - _tradeVolume: number; - _offerAmount: number; -} - -export interface MarketVolume { - period_start: number; - num_trades: number; - volume: string; -} - -export interface MarketsApiError { - success: number; - error: string; -} - -export type Interval = 'minute' | 'half_hour' | 'hour' | 'half_day' | 'day' | 'week' | 'month' | 'year' | 'auto'; - -export interface SummarizedIntervals { [market: string]: SummarizedInterval; } -export interface SummarizedInterval { - period_start: number; - open: number; - close: number; - high: number; - low: number; - avg: number; - volume_right: number; - volume_left: number; - time?: number; -} diff --git a/frontend/src/app/bisq/bisq.module.ts b/frontend/src/app/bisq/bisq.module.ts deleted file mode 100644 index f7f71156b..000000000 --- a/frontend/src/app/bisq/bisq.module.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { NgModule } from '@angular/core'; -import { BisqRoutingModule } from './bisq.routing.module'; -import { SharedModule } from '../shared/shared.module'; - -import { LightweightChartsComponent } from './lightweight-charts/lightweight-charts.component'; -import { LightweightChartsAreaComponent } from './lightweight-charts-area/lightweight-charts-area.component'; -import { BisqMarketComponent } from './bisq-market/bisq-market.component'; -import { BisqTransactionsComponent } from './bisq-transactions/bisq-transactions.component'; -import { BisqTransactionComponent } from './bisq-transaction/bisq-transaction.component'; -import { BisqBlockComponent } from './bisq-block/bisq-block.component'; -import { BisqDashboardComponent } from './bisq-dashboard/bisq-dashboard.component'; -import { BisqMainDashboardComponent } from './bisq-main-dashboard/bisq-main-dashboard.component'; -import { BisqIconComponent } from './bisq-icon/bisq-icon.component'; -import { BisqTransactionDetailsComponent } from './bisq-transaction-details/bisq-transaction-details.component'; -import { BisqTransfersComponent } from './bisq-transfers/bisq-transfers.component'; -import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome'; -import { faLeaf, faQuestion, faExclamationTriangle, faRocket, faRetweet, faFileAlt, faMoneyBill, - faEye, faEyeSlash, faLock, faLockOpen, faExclamationCircle } from '@fortawesome/free-solid-svg-icons'; -import { BisqBlocksComponent } from './bisq-blocks/bisq-blocks.component'; -import { BisqApiService } from './bisq-api.service'; -import { BisqAddressComponent } from './bisq-address/bisq-address.component'; -import { BisqStatsComponent } from './bisq-stats/bisq-stats.component'; -import { BsqAmountComponent } from './bsq-amount/bsq-amount.component'; -import { BisqTradesComponent } from './bisq-trades/bisq-trades.component'; -import { CommonModule } from '@angular/common'; -import { AutofocusDirective } from '../components/ngx-bootstrap-multiselect/autofocus.directive'; -import { MultiSelectSearchFilter } from '../components/ngx-bootstrap-multiselect/search-filter.pipe'; -import { OffClickDirective } from '../components/ngx-bootstrap-multiselect/off-click.directive'; -import { NgxDropdownMultiselectComponent } from '../components/ngx-bootstrap-multiselect/ngx-bootstrap-multiselect.component'; -import { BisqMasterPageComponent } from '../components/bisq-master-page/bisq-master-page.component'; - -@NgModule({ - declarations: [ - BisqMasterPageComponent, - BisqTransactionsComponent, - BisqTransactionComponent, - BisqBlockComponent, - BisqTransactionComponent, - BisqIconComponent, - BisqTransactionDetailsComponent, - BisqTransfersComponent, - BisqBlocksComponent, - BisqAddressComponent, - BisqStatsComponent, - BsqAmountComponent, - LightweightChartsComponent, - LightweightChartsAreaComponent, - BisqDashboardComponent, - BisqMarketComponent, - BisqTradesComponent, - BisqMainDashboardComponent, - NgxDropdownMultiselectComponent, - AutofocusDirective, - OffClickDirective, - ], - imports: [ - CommonModule, - BisqRoutingModule, - SharedModule, - FontAwesomeModule, - ], - providers: [ - BisqApiService, - MultiSelectSearchFilter, - AutofocusDirective, - OffClickDirective, - ] -}) -export class BisqModule { - constructor(library: FaIconLibrary) { - library.addIcons(faQuestion); - library.addIcons(faExclamationCircle); - library.addIcons(faExclamationTriangle); - library.addIcons(faRocket); - library.addIcons(faRetweet); - library.addIcons(faLeaf); - library.addIcons(faFileAlt); - library.addIcons(faMoneyBill); - library.addIcons(faEye); - library.addIcons(faEyeSlash); - library.addIcons(faLock); - library.addIcons(faLockOpen); - } -} diff --git a/frontend/src/app/bisq/bisq.routing.module.ts b/frontend/src/app/bisq/bisq.routing.module.ts deleted file mode 100644 index 7c6d2ee1b..000000000 --- a/frontend/src/app/bisq/bisq.routing.module.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; -import { BisqMasterPageComponent } from '../components/bisq-master-page/bisq-master-page.component'; -import { BisqTransactionsComponent } from './bisq-transactions/bisq-transactions.component'; -import { BisqTransactionComponent } from './bisq-transaction/bisq-transaction.component'; -import { BisqBlockComponent } from './bisq-block/bisq-block.component'; -import { BisqBlocksComponent } from './bisq-blocks/bisq-blocks.component'; -import { BisqAddressComponent } from './bisq-address/bisq-address.component'; -import { BisqStatsComponent } from './bisq-stats/bisq-stats.component'; -import { BisqDashboardComponent } from './bisq-dashboard/bisq-dashboard.component'; -import { BisqMarketComponent } from './bisq-market/bisq-market.component'; -import { BisqMainDashboardComponent } from './bisq-main-dashboard/bisq-main-dashboard.component'; -import { PushTransactionComponent } from '../components/push-transaction/push-transaction.component'; - -const routes: Routes = [ - { - path: '', - component: BisqMasterPageComponent, - children: [ - { - path: '', - component: BisqMainDashboardComponent, - }, - { - path: 'markets', - data: { networks: ['bisq'] }, - component: BisqDashboardComponent, - }, - { - path: 'transactions', - data: { networks: ['bisq'] }, - component: BisqTransactionsComponent - }, - { - path: 'market/:pair', - data: { networkSpecific: true }, - component: BisqMarketComponent, - }, - { - path: 'tx/push', - component: PushTransactionComponent, - }, - { - path: 'tx/:id', - data: { networkSpecific: true }, - component: BisqTransactionComponent - }, - { - path: 'blocks', - children: [], - component: BisqBlocksComponent - }, - { - path: 'block/:id', - data: { networkSpecific: true }, - component: BisqBlockComponent, - }, - { - path: 'address/:id', - data: { networkSpecific: true }, - component: BisqAddressComponent, - }, - { - path: 'stats', - data: { networks: ['bisq'] }, - component: BisqStatsComponent, - }, - { - path: 'about', - loadChildren: () => import('../components/about/about.module').then(m => m.AboutModule), - }, - { - path: 'docs', - loadChildren: () => import('../docs/docs.module').then(m => m.DocsModule) - }, - { - path: 'api', - loadChildren: () => import('../docs/docs.module').then(m => m.DocsModule) - }, - { - path: 'terms-of-service', - loadChildren: () => import('../components/terms-of-service/terms-of-service.module').then(m => m.TermsOfServiceModule), - }, - { - path: '**', - redirectTo: '' - } - ] - } -]; - -@NgModule({ - imports: [RouterModule.forChild(routes)], -}) -export class BisqRoutingModule { } diff --git a/frontend/src/app/bisq/bsq-amount/bsq-amount.component.html b/frontend/src/app/bisq/bsq-amount/bsq-amount.component.html deleted file mode 100644 index 654adcb47..000000000 --- a/frontend/src/app/bisq/bsq-amount/bsq-amount.component.html +++ /dev/null @@ -1,6 +0,0 @@ - - {{ conversions.USD * bsq / 100 * (bsqPrice$ | async) / 100000000 | currency:'USD':'symbol':'1.2-2' }} - - - {{ bsq / 100 | number : digitsInfo }} BSQ - diff --git a/frontend/src/app/bisq/bsq-amount/bsq-amount.component.scss b/frontend/src/app/bisq/bsq-amount/bsq-amount.component.scss deleted file mode 100644 index 843bd58b6..000000000 --- a/frontend/src/app/bisq/bsq-amount/bsq-amount.component.scss +++ /dev/null @@ -1,3 +0,0 @@ -.green-color { - color: #3bcc49; -} diff --git a/frontend/src/app/bisq/bsq-amount/bsq-amount.component.ts b/frontend/src/app/bisq/bsq-amount/bsq-amount.component.ts deleted file mode 100644 index a3dd10e81..000000000 --- a/frontend/src/app/bisq/bsq-amount/bsq-amount.component.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Component, OnInit, ChangeDetectionStrategy, Input } from '@angular/core'; -import { StateService } from '../../services/state.service'; -import { Observable } from 'rxjs'; - -@Component({ - selector: 'app-bsq-amount', - templateUrl: './bsq-amount.component.html', - styleUrls: ['./bsq-amount.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class BsqAmountComponent implements OnInit { - conversions$: Observable; - viewFiat$: Observable; - bsqPrice$: Observable; - - @Input() bsq: number; - @Input() digitsInfo = '1.2-2'; - @Input() forceFiat = false; - @Input() green = false; - - constructor( - private stateService: StateService, - ) { } - - ngOnInit() { - this.viewFiat$ = this.stateService.viewFiat$.asObservable(); - this.conversions$ = this.stateService.conversions$.asObservable(); - this.bsqPrice$ = this.stateService.bsqPrice$; - } -} diff --git a/frontend/src/app/bisq/lightweight-charts-area/lightweight-charts-area.component.scss b/frontend/src/app/bisq/lightweight-charts-area/lightweight-charts-area.component.scss deleted file mode 100644 index 56fe6ab0e..000000000 --- a/frontend/src/app/bisq/lightweight-charts-area/lightweight-charts-area.component.scss +++ /dev/null @@ -1,25 +0,0 @@ -:host ::ng-deep .floating-tooltip-2 { - width: 160px; - height: 80px; - position: absolute; - display: none; - padding: 8px; - box-sizing: border-box; - font-size: 12px; - color:rgba(255, 255, 255, 1); - background-color: #131722; - text-align: left; - z-index: 1000; - top: 12px; - left: 12px; - pointer-events: none; - border-radius: 2px; -} - -:host ::ng-deep .volumeText { - color: rgba(33, 150, 243, 0.7); -} - -:host ::ng-deep .tradesText { - color: rgba(37, 177, 53, 1); -} diff --git a/frontend/src/app/bisq/lightweight-charts-area/lightweight-charts-area.component.ts b/frontend/src/app/bisq/lightweight-charts-area/lightweight-charts-area.component.ts deleted file mode 100644 index 714d95eec..000000000 --- a/frontend/src/app/bisq/lightweight-charts-area/lightweight-charts-area.component.ts +++ /dev/null @@ -1,151 +0,0 @@ -import { createChart, CrosshairMode, isBusinessDay } from 'lightweight-charts'; -import { ChangeDetectionStrategy, Component, ElementRef, HostListener, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core'; - -@Component({ - selector: 'app-lightweight-charts-area', - template: '', - styleUrls: ['./lightweight-charts-area.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class LightweightChartsAreaComponent implements OnInit, OnChanges, OnDestroy { - @Input() data: any; - @Input() lineData: any; - @Input() precision: number; - @Input() height = 500; - - areaSeries: any; - volumeSeries: any; - chart: any; - lineSeries: any; - container: any; - - width: number; - - constructor( - private element: ElementRef, - ) { } - - @HostListener('window:resize', ['$event']) - resizeCanvas(): void { - this.width = this.element.nativeElement.parentElement.offsetWidth; - this.chart.applyOptions({ - width: this.width, - height: this.height, - }); - } - - ngOnInit() { - this.width = this.element.nativeElement.parentElement.offsetWidth; - this.container = document.createElement('div'); - const chartholder = this.element.nativeElement.appendChild(this.container); - - this.chart = createChart(chartholder, { - width: this.width, - height: this.height, - crosshair: { - mode: CrosshairMode.Normal, - }, - layout: { - backgroundColor: '#000', - textColor: 'rgba(255, 255, 255, 0.8)', - }, - grid: { - vertLines: { - color: 'rgba(255, 255, 255, 0.1)', - }, - horzLines: { - color: 'rgba(255, 255, 255, 0.1)', - }, - }, - rightPriceScale: { - borderColor: 'rgba(255, 255, 255, 0.2)', - }, - timeScale: { - borderColor: 'rgba(255, 255, 255, 0.2)', - }, - }); - - this.lineSeries = this.chart.addLineSeries({ - color: 'rgba(37, 177, 53, 1)', - lineColor: 'rgba(216, 27, 96, 1)', - lineWidth: 2, - }); - - this.areaSeries = this.chart.addAreaSeries({ - topColor: 'rgba(33, 150, 243, 0.7)', - bottomColor: 'rgba(33, 150, 243, 0.1)', - lineColor: 'rgba(33, 150, 243, 0.1)', - lineWidth: 2, - }); - - const toolTip = document.createElement('div'); - toolTip.className = 'floating-tooltip-2'; - chartholder.appendChild(toolTip); - - this.chart.subscribeCrosshairMove((param) => { - if (!param.time || param.point.x < 0 || param.point.x > this.width || param.point.y < 0 || param.point.y > this.height) { - toolTip.style.display = 'none'; - return; - } - - const dateStr = isBusinessDay(param.time) - ? this.businessDayToString(param.time) - : new Date(param.time * 1000).toLocaleDateString(); - - toolTip.style.display = 'block'; - const price = param.seriesPrices.get(this.areaSeries); - const line = param.seriesPrices.get(this.lineSeries); - - const tradesText = $localize`:@@bisq-graph-trades:Trades`; - const volumeText = $localize`:@@bisq-graph-volume:Volume`; - - toolTip.innerHTML = ` - - -
${tradesText}:${Math.round(line * 100) / 100}
${volumeText}:${Math.round(price * 100) / 100} BTC
-
${dateStr}
`; - - const y = param.point.y; - - const toolTipWidth = 100; - const toolTipHeight = 80; - const toolTipMargin = 15; - - let left = param.point.x + toolTipMargin; - if (left > this.width - toolTipWidth) { - left = param.point.x - toolTipMargin - toolTipWidth; - } - - let top = y + toolTipMargin; - if (top > this.height - toolTipHeight) { - top = y - toolTipHeight - toolTipMargin; - } - - toolTip.style.left = left + 'px'; - toolTip.style.top = top + 'px'; - }); - - this.updateData(); - } - - businessDayToString(businessDay) { - return businessDay.year + '-' + businessDay.month + '-' + businessDay.day; - } - - ngOnChanges(changes: SimpleChanges) { - if (!changes.data || changes.data.isFirstChange()){ - return; - } - this.updateData(); - } - - updateData() { - this.areaSeries.setData(this.data); - this.lineSeries.setData(this.lineData); - } - - ngOnDestroy() { - this.chart.remove(); - } - -} diff --git a/frontend/src/app/bisq/lightweight-charts/lightweight-charts.component.scss b/frontend/src/app/bisq/lightweight-charts/lightweight-charts.component.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/frontend/src/app/bisq/lightweight-charts/lightweight-charts.component.ts b/frontend/src/app/bisq/lightweight-charts/lightweight-charts.component.ts deleted file mode 100644 index 620b61782..000000000 --- a/frontend/src/app/bisq/lightweight-charts/lightweight-charts.component.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { createChart, CrosshairMode } from 'lightweight-charts'; -import { ChangeDetectionStrategy, Component, ElementRef, HostListener, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core'; - -@Component({ - selector: 'app-lightweight-charts', - template: '', - styleUrls: ['./lightweight-charts.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class LightweightChartsComponent implements OnInit, OnChanges, OnDestroy { - @Input() data: any; - @Input() volumeData: any; - @Input() precision: number; - @Input() height = 500; - - lineSeries: any; - volumeSeries: any; - chart: any; - - constructor( - private element: ElementRef, - ) { } - - @HostListener('window:resize', ['$event']) - resizeCanvas(): void { - this.chart.applyOptions({ - width: this.element.nativeElement.parentElement.offsetWidth, - height: this.height, - }); - } - - ngOnInit() { - this.chart = createChart(this.element.nativeElement, { - width: this.element.nativeElement.parentElement.offsetWidth, - height: this.height, - layout: { - backgroundColor: '#000000', - textColor: '#d1d4dc', - }, - crosshair: { - mode: CrosshairMode.Normal, - }, - grid: { - vertLines: { - visible: true, - color: 'rgba(42, 46, 57, 0.5)', - }, - horzLines: { - color: 'rgba(42, 46, 57, 0.5)', - }, - }, - }); - this.lineSeries = this.chart.addCandlestickSeries(); - - this.volumeSeries = this.chart.addHistogramSeries({ - color: '#26a69a', - priceFormat: { - type: 'volume', - }, - priceScaleId: '', - scaleMargins: { - top: 0.85, - bottom: 0, - }, - }); - - this.updateData(); - } - - ngOnChanges(changes: SimpleChanges) { - if (!changes.data || changes.data.isFirstChange()){ - return; - } - this.updateData(); - } - - ngOnDestroy() { - this.chart.remove(); - } - - updateData() { - this.lineSeries.setData(this.data); - this.volumeSeries.setData(this.volumeData); - - this.lineSeries.applyOptions({ - priceFormat: { - type: 'price', - precision: this.precision, - minMove: 0.0000001, - }, - }); - } - -} diff --git a/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.ts b/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.ts index d4df57020..1d3603739 100644 --- a/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.ts +++ b/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.ts @@ -44,7 +44,7 @@ export class AcceleratorDashboardComponent implements OnInit { @Inject(PLATFORM_ID) private platformId: Object, ) { this.webGlEnabled = this.stateService.isBrowser && detectWebGL(); - this.seoService.setTitle($localize`:@@a681a4e2011bb28157689dbaa387de0dd0aa0c11:Accelerator Dashboard`); + this.seoService.setTitle($localize`:@@6b867dc61c6a92f3229f1950f9f2d414790cce95:Accelerator Dashboard`); this.ogService.setManualOgImage('accelerator.jpg'); } diff --git a/frontend/src/app/components/address/address-preview.component.ts b/frontend/src/app/components/address/address-preview.component.ts index 4ea857b37..9bc6e967f 100644 --- a/frontend/src/app/components/address/address-preview.component.ts +++ b/frontend/src/app/components/address/address-preview.component.ts @@ -69,7 +69,7 @@ export class AddressPreviewComponent implements OnInit, OnDestroy { this.addressString = this.addressString.toLowerCase(); } this.seoService.setTitle($localize`:@@address.component.browser-title:Address: ${this.addressString}:INTERPOLATION:`); - this.seoService.setDescription($localize`:@@meta.description.bitcoin.address:See mempool transactions, confirmed transactions, balance, and more for ${this.stateService.network==='liquid'||this.stateService.network==='liquidtestnet'?'Liquid':'Bitcoin'} ${seoDescriptionNetwork(this.stateService.network)} address ${this.addressString}:INTERPOLATION:.`); + this.seoService.setDescription($localize`:@@meta.description.bitcoin.address:See mempool transactions, confirmed transactions, balance, and more for ${this.stateService.network==='liquid'||this.stateService.network==='liquidtestnet'?'Liquid':'Bitcoin'}${seoDescriptionNetwork(this.stateService.network)} address ${this.addressString}:INTERPOLATION:.`); return (this.addressString.match(/04[a-fA-F0-9]{128}|(02|03)[a-fA-F0-9]{64}/) ? this.electrsApiService.getPubKeyAddress$(this.addressString) diff --git a/frontend/src/app/components/address/address.component.html b/frontend/src/app/components/address/address.component.html index e4c49d4c5..531b97464 100644 --- a/frontend/src/app/components/address/address.component.html +++ b/frontend/src/app/components/address/address.component.html @@ -51,7 +51,7 @@ - +
diff --git a/frontend/src/app/components/address/address.component.ts b/frontend/src/app/components/address/address.component.ts index 8b325e653..614be930c 100644 --- a/frontend/src/app/components/address/address.component.ts +++ b/frontend/src/app/components/address/address.component.ts @@ -44,7 +44,7 @@ export class AddressComponent implements OnInit, OnDestroy { private route: ActivatedRoute, private electrsApiService: ElectrsApiService, private websocketService: WebsocketService, - private stateService: StateService, + public stateService: StateService, private audioService: AudioService, private apiService: ApiService, private seoService: SeoService, diff --git a/frontend/src/app/components/app/app.component.ts b/frontend/src/app/components/app/app.component.ts index c7ca798ae..ace0122f0 100644 --- a/frontend/src/app/components/app/app.component.ts +++ b/frontend/src/app/components/app/app.component.ts @@ -55,8 +55,6 @@ export class AppComponent implements OnInit { let domain = 'mempool.space'; if (this.stateService.env.BASE_MODULE === 'liquid') { domain = 'liquid.network'; - } else if (this.stateService.env.BASE_MODULE === 'bisq') { - domain = 'bisq.markets'; } this.link.setAttribute('href', 'https://' + domain + this.location.path()); } diff --git a/frontend/src/app/components/asset/asset.component.ts b/frontend/src/app/components/asset/asset.component.ts index 562ebff53..dd09468cc 100644 --- a/frontend/src/app/components/asset/asset.component.ts +++ b/frontend/src/app/components/asset/asset.component.ts @@ -105,6 +105,7 @@ export class AssetComponent implements OnInit, OnDestroy { if (!this.assetContract) { this.assetContract = [null, '?', 'Unknown', 0]; } + this.seoService.setDescription($localize`:@@meta.description.liquid.asset:Browse an overview of the Liquid asset ${this.assetContract[2]}:INTERPOLATION: (${this.assetContract[1]}:INTERPOLATION:): see issued amount, burned amount, circulating amount, related transactions, and more.`); this.blindedIssuance = this.asset.chain_stats.has_blinded_issuances || this.asset.mempool_stats.has_blinded_issuances; this.isNativeAsset = asset.asset_id === this.nativeAssetId; this.updateChainStats(); diff --git a/frontend/src/app/components/bisq-master-page/bisq-master-page.component.html b/frontend/src/app/components/bisq-master-page/bisq-master-page.component.html deleted file mode 100644 index 19c5ab715..000000000 --- a/frontend/src/app/components/bisq-master-page/bisq-master-page.component.html +++ /dev/null @@ -1,91 +0,0 @@ - - -
- -
- -
- - - -
diff --git a/frontend/src/app/components/bisq-master-page/bisq-master-page.component.scss b/frontend/src/app/components/bisq-master-page/bisq-master-page.component.scss deleted file mode 100644 index a7274c165..000000000 --- a/frontend/src/app/components/bisq-master-page/bisq-master-page.component.scss +++ /dev/null @@ -1,172 +0,0 @@ -.sticky-header { - position: sticky; - position: -webkit-sticky; - top: 0; - width: 100%; - z-index: 100; -} - -li.nav-item.active { - background-color: #653b9c; -} - -fa-icon { - font-size: 1.66em; -} - -.navbar { - z-index: 100; - min-height: 64px; -} - -li.nav-item { - margin: auto 5px; - padding-left: 10px; - padding-right: 10px; -} - -@media (max-width: 992px) { - footer > .container-fluid { - padding-bottom: 35px; - } -} - -@media (min-width: 992px) { - .navbar { - padding: 0rem 2rem; - } - fa-icon { - font-size: 1.2em; - } - .dropdown-container { - margin-right: 16px; - } - li.nav-item { - margin: auto 0px; - padding: 10px; - } -} - -.navbar-nav { - background: #212121; - bottom: 0; - box-shadow: 0px 0px 15px 0px #000; - flex-direction: row; - left: 0; - justify-content: center; - position: fixed; - width: 100%; - @media (min-width: 992px) { - background: transparent; - box-shadow: none; - position: relative; - width: auto; - } - a { - font-size: 0.8em; - @media (min-width: 375px) { - font-size: 1em; - } - } -} - - -.navbar-collapse { - flex-basis: auto; - justify-content: flex-end; -} - -@media (min-width: 992px) { - .navbar-collapse { - justify-content: space-between; - } -} - -.navbar-brand { - width: 60%; -} - -@media (min-width: 576px) { - .navbar-brand { - width: 140px; - } -} - -nav { - box-shadow: 0px 0px 15px 0px #000; -} - -.connection-badge { - position: absolute; - top: 13px; - left: 0px; - width: 140px; -} - -.badge { - margin: 0 auto; - display: table; -} - -.mainnet.active { - background-color: #653b9c; -} - -.liquid.active { - background-color: #116761; -} - -.liquidtestnet.active { - background-color: #494a4a; -} - -.testnet.active { - background-color: #1d486f; -} - -.signet.active { - background-color: #6f1d5d; -} - -.dropdown-divider { - border-top: 1px solid #121420; -} - -.dropdown-toggle::after { - vertical-align: 0.1em; -} - -.dropdown-item { - display: flex; - align-items: center; -} - -@media (min-width: 992px) { - .search-form-container { - width: 100%; - max-width: 500px; - padding-left: 15px; - } -} -.navbar-dark .navbar-nav .nav-link { - color: #f1f1f1; -} - -.navbar-brand { - margin-right: 5px; -} - -.current-network-svg { - width: 20px; - height: 20px; - margin-right: 5px; -} - -:host-context(.rtl-layout) { - .current-network-svg { - width: 20px; - height: 20px; - margin-left: 5px; - margin-right: 0px; - } -} \ No newline at end of file diff --git a/frontend/src/app/components/bisq-master-page/bisq-master-page.component.ts b/frontend/src/app/components/bisq-master-page/bisq-master-page.component.ts deleted file mode 100644 index f849998b1..000000000 --- a/frontend/src/app/components/bisq-master-page/bisq-master-page.component.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { Env, StateService } from '../../services/state.service'; -import { Observable } from 'rxjs'; -import { LanguageService } from '../../services/language.service'; -import { EnterpriseService } from '../../services/enterprise.service'; -import { NavigationService } from '../../services/navigation.service'; - -@Component({ - selector: 'app-bisq-master-page', - templateUrl: './bisq-master-page.component.html', - styleUrls: ['./bisq-master-page.component.scss'], -}) -export class BisqMasterPageComponent implements OnInit { - connectionState$: Observable; - navCollapsed = false; - env: Env; - isMobile = window.innerWidth <= 767.98; - urlLanguage: string; - networkPaths: { [network: string]: string }; - footerVisible = true; - - constructor( - private stateService: StateService, - private languageService: LanguageService, - private enterpriseService: EnterpriseService, - private navigationService: NavigationService, - ) { } - - ngOnInit() { - this.env = this.stateService.env; - this.connectionState$ = this.stateService.connectionState$; - this.urlLanguage = this.languageService.getLanguageForUrl(); - this.navigationService.subnetPaths.subscribe((paths) => { - this.networkPaths = paths; - if (paths.mainnet.indexOf('docs') > -1) { - this.footerVisible = false; - } else { - this.footerVisible = true; - } - }); - } - - collapse(): void { - this.navCollapsed = !this.navCollapsed; - } - - onResize(event: any) { - this.isMobile = window.innerWidth <= 767.98; - } -} diff --git a/frontend/src/app/components/block-health-graph/block-health-graph.component.ts b/frontend/src/app/components/block-health-graph/block-health-graph.component.ts index 1e105d5e7..8eef20d51 100644 --- a/frontend/src/app/components/block-health-graph/block-health-graph.component.ts +++ b/frontend/src/app/components/block-health-graph/block-health-graph.component.ts @@ -187,7 +187,7 @@ export class BlockHealthGraphComponent implements OnInit { series: data.length === 0 ? undefined : [ { zlevel: 0, - name: $localize`Health`, + name: $localize`:@@d2bcd3296d2850de762fb943060b7e086a893181:Health`, data: data.map(health => ({ value: health[2], block: health[1], diff --git a/frontend/src/app/components/block-overview-graph/block-overview-graph.component.html b/frontend/src/app/components/block-overview-graph/block-overview-graph.component.html index 702718742..2ef07d12c 100644 --- a/frontend/src/app/components/block-overview-graph/block-overview-graph.component.html +++ b/frontend/src/app/components/block-overview-graph/block-overview-graph.component.html @@ -14,6 +14,7 @@ [blockConversion]="blockConversion" [filterFlags]="activeFilterFlags" [filterMode]="filterMode" + [relativeTime]="relativeTime" >
diff --git a/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts b/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts index f7e9d297f..003531fce 100644 --- a/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts +++ b/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts @@ -1,5 +1,5 @@ import { Component, ElementRef, ViewChild, HostListener, Input, Output, EventEmitter, NgZone, AfterViewInit, OnDestroy, OnChanges } from '@angular/core'; -import { TransactionStripped } from '../../interfaces/websocket.interface'; +import { TransactionStripped } from '../../interfaces/node-api.interface'; import { FastVertexArray } from './fast-vertex-array'; import BlockScene from './block-scene'; import TxSprite from './tx-sprite'; @@ -20,7 +20,7 @@ const unmatchedAuditColors = { censored: setOpacity(defaultAuditColors.censored, unmatchedOpacity), missing: setOpacity(defaultAuditColors.missing, unmatchedOpacity), added: setOpacity(defaultAuditColors.added, unmatchedOpacity), - selected: setOpacity(defaultAuditColors.selected, unmatchedOpacity), + prioritized: setOpacity(defaultAuditColors.prioritized, unmatchedOpacity), accelerated: setOpacity(defaultAuditColors.accelerated, unmatchedOpacity), }; @@ -46,6 +46,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On @Input() excludeFilters: string[] = []; @Input() filterFlags: bigint | null = null; @Input() filterMode: FilterMode = 'and'; + @Input() relativeTime: number | null; @Input() blockConversion: Price; @Input() overrideColors: ((tx: TxView) => Color) | null = null; @Output() txClickEvent = new EventEmitter<{ tx: TransactionStripped, keyModifier: boolean}>(); diff --git a/frontend/src/app/components/block-overview-graph/block-scene.ts b/frontend/src/app/components/block-overview-graph/block-scene.ts index adcf736fc..5d2196f1e 100644 --- a/frontend/src/app/components/block-overview-graph/block-scene.ts +++ b/frontend/src/app/components/block-overview-graph/block-scene.ts @@ -1,6 +1,6 @@ import { FastVertexArray } from './fast-vertex-array'; import TxView from './tx-view'; -import { TransactionStripped } from '../../interfaces/websocket.interface'; +import { TransactionStripped } from '../../interfaces/node-api.interface'; import { Color, Position, Square, ViewUpdateParams } from './sprite-types'; import { defaultColorFunction } from './utils'; diff --git a/frontend/src/app/components/block-overview-graph/tx-view.ts b/frontend/src/app/components/block-overview-graph/tx-view.ts index f9f6eeeb7..742c305f5 100644 --- a/frontend/src/app/components/block-overview-graph/tx-view.ts +++ b/frontend/src/app/components/block-overview-graph/tx-view.ts @@ -32,7 +32,8 @@ export default class TxView implements TransactionStripped { rate?: number; flags: number; bigintFlags?: bigint | null = 0b00000100_00000000_00000000_00000000n; - status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'freshcpfp' | 'added' | 'censored' | 'selected' | 'rbf' | 'accelerated'; + time?: number; + status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'freshcpfp' | 'added' | 'prioritized' | 'censored' | 'selected' | 'rbf' | 'accelerated'; context?: 'projected' | 'actual'; scene?: BlockScene; @@ -53,6 +54,7 @@ export default class TxView implements TransactionStripped { this.scene = scene; this.context = tx.context; this.txid = tx.txid; + this.time = tx.time || 0; this.fee = tx.fee; this.vsize = tx.vsize; this.value = tx.value; diff --git a/frontend/src/app/components/block-overview-graph/utils.ts b/frontend/src/app/components/block-overview-graph/utils.ts index 9c800ad85..b6c8ccf5e 100644 --- a/frontend/src/app/components/block-overview-graph/utils.ts +++ b/frontend/src/app/components/block-overview-graph/utils.ts @@ -45,7 +45,7 @@ export const defaultAuditColors = { censored: hexToColor('f344df'), missing: darken(desaturate(hexToColor('f344df'), 0.3), 0.7), added: hexToColor('0099ff'), - selected: darken(desaturate(hexToColor('0099ff'), 0.3), 0.7), + prioritized: darken(desaturate(hexToColor('0099ff'), 0.3), 0.7), accelerated: hexToColor('8F5FF6'), }; @@ -81,6 +81,8 @@ export function defaultColorFunction( return auditColors.missing; case 'added': return auditColors.added; + case 'prioritized': + return auditColors.prioritized; case 'selected': return marginalFeeColors[feeLevelIndex] || marginalFeeColors[mempoolFeeColors.length - 1]; case 'accelerated': diff --git a/frontend/src/app/components/block-overview-tooltip/block-overview-tooltip.component.html b/frontend/src/app/components/block-overview-tooltip/block-overview-tooltip.component.html index 1ef0d1686..505171f9d 100644 --- a/frontend/src/app/components/block-overview-tooltip/block-overview-tooltip.component.html +++ b/frontend/src/app/components/block-overview-tooltip/block-overview-tooltip.component.html @@ -14,6 +14,26 @@ {{ txid | shortenString : 16}} + + + + First seen + + + + First seen + + + + First seen + + + + Confirmed + + + + Amount @@ -48,14 +68,15 @@ Match - Removed + Removed Marginal fee rate High sigop count Recently broadcasted Recently CPFP'd - Added + Added + Prioritized Marginal fee rate - Conflicting + Conflict Accelerated diff --git a/frontend/src/app/components/block-overview-tooltip/block-overview-tooltip.component.ts b/frontend/src/app/components/block-overview-tooltip/block-overview-tooltip.component.ts index f163e74fc..cdb4c7367 100644 --- a/frontend/src/app/components/block-overview-tooltip/block-overview-tooltip.component.ts +++ b/frontend/src/app/components/block-overview-tooltip/block-overview-tooltip.component.ts @@ -3,6 +3,7 @@ import { Position } from '../../components/block-overview-graph/sprite-types.js' import { Price } from '../../services/price.service'; import { TransactionStripped } from '../../interfaces/node-api.interface.js'; import { Filter, FilterMode, TransactionFlags, toFilters } from '../../shared/filters.utils'; +import { Block } from '../../interfaces/electrs.interface.js'; @Component({ selector: 'app-block-overview-tooltip', @@ -11,6 +12,7 @@ import { Filter, FilterMode, TransactionFlags, toFilters } from '../../shared/fi }) export class BlockOverviewTooltipComponent implements OnChanges { @Input() tx: TransactionStripped | void; + @Input() relativeTime?: number; @Input() cursorPosition: Position; @Input() clickable: boolean; @Input() auditEnabled: boolean = false; @@ -19,6 +21,7 @@ export class BlockOverviewTooltipComponent implements OnChanges { @Input() filterMode: FilterMode = 'and'; txid = ''; + time: number = 0; fee = 0; value = 0; vsize = 1; @@ -26,6 +29,7 @@ export class BlockOverviewTooltipComponent implements OnChanges { effectiveRate; acceleration; hasEffectiveRate: boolean = false; + timeMode: 'mempool' | 'mined' | 'missed' | 'after' = 'mempool'; filters: Filter[] = []; activeFilters: { [key: string]: boolean } = {}; @@ -56,6 +60,7 @@ export class BlockOverviewTooltipComponent implements OnChanges { if (this.tx && (changes.tx || changes.filterFlags || changes.filterMode)) { this.txid = this.tx.txid || ''; + this.time = this.tx.time || 0; this.fee = this.tx.fee || 0; this.value = this.tx.value || 0; this.vsize = this.tx.vsize || 1; @@ -72,6 +77,22 @@ export class BlockOverviewTooltipComponent implements OnChanges { this.activeFilters[filter.key] = true; } } + + if (!this.relativeTime) { + this.timeMode = 'mempool'; + } else { + if (this.tx?.context === 'actual' || this.tx?.status === 'found') { + this.timeMode = 'mined'; + } else { + const time = this.relativeTime || Date.now(); + if (this.time <= time) { + this.timeMode = 'missed'; + } else { + this.timeMode = 'after'; + } + } + } + this.cd.markForCheck(); } } diff --git a/frontend/src/app/components/block-view/block-view.component.html b/frontend/src/app/components/block-view/block-view.component.html index 9a2ddf373..f0dc94e2c 100644 --- a/frontend/src/app/components/block-view/block-view.component.html +++ b/frontend/src/app/components/block-view/block-view.component.html @@ -8,6 +8,7 @@ [orientation]="'top'" [flip]="false" [disableSpinner]="true" + [relativeTime]="block?.timestamp" (txClickEvent)="onTxClick($event)" >
diff --git a/frontend/src/app/components/block/block.component.html b/frontend/src/app/components/block/block.component.html index 0c0655c01..0020f56be 100644 --- a/frontend/src/app/components/block/block.component.html +++ b/frontend/src/app/components/block/block.component.html @@ -39,7 +39,7 @@
- + @@ -117,6 +117,7 @@ [blockConversion]="blockConversion" [showFilters]="true" [excludeFilters]="['replacement']" + [relativeTime]="block?.timestamp" (txClickEvent)="onTxClick($event)" > @@ -126,16 +127,16 @@ - + - + - + - - + @@ -63,8 +63,11 @@ {{ utxo.blocknumber + utxo.timelock - lastReservesBlockUpdate < 0 ? -(utxo.blocknumber + utxo.timelock - lastReservesBlockUpdate) : utxo.blocknumber + utxo.timelock - lastReservesBlockUpdate }} blocks diff --git a/frontend/src/app/components/liquid-reserves-audit/recent-pegs-stats/recent-pegs-stats.component.html b/frontend/src/app/components/liquid-reserves-audit/recent-pegs-stats/recent-pegs-stats.component.html index 2a853ff5d..6f012e84e 100644 --- a/frontend/src/app/components/liquid-reserves-audit/recent-pegs-stats/recent-pegs-stats.component.html +++ b/frontend/src/app/components/liquid-reserves-audit/recent-pegs-stats/recent-pegs-stats.component.html @@ -9,13 +9,13 @@
-
+{{ (+pegsVolume[0].volume) / 100000000 | number: '1.2-2' }} BTC
+
+{{ (+pegsVolume[0].volume) / 100000000 | number: '1.2-2' }} BTC
{{ (+pegsVolume[0].number) }} Peg-Ins
-
{{ (+pegsVolume[1].volume) / 100000000 | number: '1.2-2' }} BTC
+
{{ (+pegsVolume[1].volume) / 100000000 | number: '1.2-2' }} BTC
{{ (+pegsVolume[1].number) }} Peg-Outs
diff --git a/frontend/src/app/components/liquid-reserves-audit/reserves-ratio/reserves-ratio.component.ts b/frontend/src/app/components/liquid-reserves-audit/reserves-ratio/reserves-ratio.component.ts index 2289f5be1..13f948fcd 100644 --- a/frontend/src/app/components/liquid-reserves-audit/reserves-ratio/reserves-ratio.component.ts +++ b/frontend/src/app/components/liquid-reserves-audit/reserves-ratio/reserves-ratio.component.ts @@ -159,7 +159,7 @@ export class ReservesRatioComponent implements OnInit, OnChanges { data: [ { value: value, - name: 'Assets vs Liabilities' + name: $localize`Assets vs Liabilities` } ] } diff --git a/frontend/src/app/components/master-page-preview/master-page-preview.component.html b/frontend/src/app/components/master-page-preview/master-page-preview.component.html index 17182624b..4c436607b 100644 --- a/frontend/src/app/components/master-page-preview/master-page-preview.component.html +++ b/frontend/src/app/components/master-page-preview/master-page-preview.component.html @@ -9,7 +9,6 @@
Signet Testnet - Bisq Mainnet Testnet Mainnet diff --git a/frontend/src/app/components/master-page-preview/preview-title.component.html b/frontend/src/app/components/master-page-preview/preview-title.component.html index 1aebda6c6..a6a9fe5b7 100644 --- a/frontend/src/app/components/master-page-preview/preview-title.component.html +++ b/frontend/src/app/components/master-page-preview/preview-title.component.html @@ -1,7 +1,6 @@

- Bisq Liquid Liquid Bitcoin diff --git a/frontend/src/app/components/master-page/master-page.component.html b/frontend/src/app/components/master-page/master-page.component.html index fd7319b01..0e4d3a5fd 100644 --- a/frontend/src/app/components/master-page/master-page.component.html +++ b/frontend/src/app/components/master-page/master-page.component.html @@ -15,12 +15,31 @@
- - -
- -
-
+ +
+ +
+ +
+
+
+ + + +
+
Offline
+
Reconnecting...
+
+
+
+ + + +
+ +
+
+
@@ -31,7 +50,7 @@
-
-
+
diff --git a/frontend/src/app/components/master-page/master-page.component.scss b/frontend/src/app/components/master-page/master-page.component.scss index 743092f3f..9320e2b38 100644 --- a/frontend/src/app/components/master-page/master-page.component.scss +++ b/frontend/src/app/components/master-page/master-page.component.scss @@ -106,6 +106,8 @@ li.nav-item { .dropdown { .dropdown-toggle { width: 62px; + height: 36px; + margin-top: 5px; } } } @@ -181,19 +183,30 @@ nav { } .subdomain_logo { - max-height: 45px; + height: 35px; + overflow: clip; max-width: 140px; margin: auto; align-self: center; + .rounded { + border-radius: 5px; + } } .subdomain_container { - width: 140px; - margin-right: 15px; + max-width: 140px; text-align: center; align-self: center; } +.vertical-line { + border-left: 1px solid #444; + height: 30px; + margin-left: 20px; + margin-right: 20px; + margin-top: 3px; +} + .logo-holder { display: flex; flex-direction: row; diff --git a/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.ts b/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.ts index 29825491c..48cd6cccb 100644 --- a/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.ts +++ b/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.ts @@ -1,7 +1,8 @@ import { Component, ComponentRef, ViewChild, HostListener, Input, Output, EventEmitter, OnInit, OnDestroy, OnChanges, ChangeDetectionStrategy, ChangeDetectorRef, AfterViewInit } from '@angular/core'; import { StateService } from '../../services/state.service'; -import { MempoolBlockDelta, TransactionStripped } from '../../interfaces/websocket.interface'; +import { MempoolBlockDelta } from '../../interfaces/websocket.interface'; +import { TransactionStripped } from '../../interfaces/node-api.interface'; import { BlockOverviewGraphComponent } from '../../components/block-overview-graph/block-overview-graph.component'; import { Subscription, BehaviorSubject, merge, of, timer } from 'rxjs'; import { switchMap, filter, concatMap, map } from 'rxjs/operators'; diff --git a/frontend/src/app/components/mempool-block/mempool-block.component.ts b/frontend/src/app/components/mempool-block/mempool-block.component.ts index 7f41faffb..430a456ec 100644 --- a/frontend/src/app/components/mempool-block/mempool-block.component.ts +++ b/frontend/src/app/components/mempool-block/mempool-block.component.ts @@ -3,7 +3,8 @@ import { detectWebGL } from '../../shared/graphs.utils'; import { StateService } from '../../services/state.service'; import { ActivatedRoute, ParamMap } from '@angular/router'; import { switchMap, map, tap, filter } from 'rxjs/operators'; -import { MempoolBlock, TransactionStripped } from '../../interfaces/websocket.interface'; +import { MempoolBlock } from '../../interfaces/websocket.interface'; +import { TransactionStripped } from '../../interfaces/node-api.interface'; import { Observable, BehaviorSubject } from 'rxjs'; import { SeoService } from '../../services/seo.service'; import { seoDescriptionNetwork } from '../../shared/common.utils'; diff --git a/frontend/src/app/components/menu/menu.component.html b/frontend/src/app/components/menu/menu.component.html index 54ef9ed22..962b473e2 100644 --- a/frontend/src/app/components/menu/menu.component.html +++ b/frontend/src/app/components/menu/menu.component.html @@ -18,7 +18,7 @@ - Sign in + Sign In diff --git a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.ts b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.ts index 96058e7bf..2b69d4ae0 100644 --- a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.ts +++ b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.ts @@ -20,15 +20,14 @@ export class MiningDashboardComponent implements OnInit, AfterViewInit { private websocketService: WebsocketService, private stateService: StateService, private router: Router - ) { - this.seoService.setTitle($localize`:@@a681a4e2011bb28157689dbaa387de0dd0aa0c11:Mining Dashboard`); - this.seoService.setDescription($localize`:@@meta.description.mining.dashboard:Get real-time Bitcoin mining stats like hashrate, difficulty adjustment, block rewards, pool dominance, and more.`); - this.ogService.setManualOgImage('mining.jpg'); - } + ) { } ngOnInit(): void { this.onResize(); this.websocketService.want(['blocks', 'mempool-blocks', 'stats']); + this.seoService.setTitle($localize`:@@a681a4e2011bb28157689dbaa387de0dd0aa0c11:Mining Dashboard`); + this.seoService.setDescription($localize`:@@meta.description.mining.dashboard:Get real-time Bitcoin mining stats like hashrate, difficulty adjustment, block rewards, pool dominance, and more.`); + this.ogService.setManualOgImage('mining.jpg'); } ngAfterViewInit(): void { diff --git a/frontend/src/app/components/pool/pool.component.ts b/frontend/src/app/components/pool/pool.component.ts index 8274bf441..7c6b5de38 100644 --- a/frontend/src/app/components/pool/pool.component.ts +++ b/frontend/src/app/components/pool/pool.component.ts @@ -121,6 +121,7 @@ export class PoolComponent implements OnInit { ); this.oobFees$ = this.route.params.pipe(map((params) => params.slug)).pipe( + filter(() => this.stateService.env.PUBLIC_ACCELERATIONS === true && this.stateService.network === ''), switchMap(slug => { return combineLatest([ this.apiService.getAccelerationTotals$(this.slug, '1w'), @@ -209,7 +210,7 @@ export class PoolComponent implements OnInit { legend: { data: [ { - name: $localize`:mining.hashrate:Hashrate`, + name: $localize`:@@79a9dc5b1caca3cbeb1733a19515edacc5fc7920:Hashrate`, inactiveColor: 'rgb(110, 112, 121)', textStyle: { color: 'white', @@ -263,7 +264,7 @@ export class PoolComponent implements OnInit { series: hashrate.length <= 1 ? undefined : [ { zlevel: 1, - name: $localize`:mining.hashrate:Hashrate`, + name: $localize`:@@79a9dc5b1caca3cbeb1733a19515edacc5fc7920:Hashrate`, showSymbol: false, symbol: 'none', data: hashrate, diff --git a/frontend/src/app/components/privacy-policy/privacy-policy.component.html b/frontend/src/app/components/privacy-policy/privacy-policy.component.html index d14b12bc3..1250af686 100644 --- a/frontend/src/app/components/privacy-policy/privacy-policy.component.html +++ b/frontend/src/app/components/privacy-policy/privacy-policy.component.html @@ -11,7 +11,7 @@
-

The mempool.space website, the liquid.network website, the bisq.markets website, their associated API services, and related network and server infrastructure (collectively, the "Website") are operated by Mempool Space K.K. in Japan ("Mempool", "We", or "Us") and self-hosted from AS142052.

+

The mempool.space website, the liquid.network website, their associated API services, and related network and server infrastructure (collectively, the "Website") are operated by Mempool Space K.K. in Japan ("Mempool", "We", or "Us") and self-hosted from AS142052.

This website and its API service (collectively, the "Website") are operated by a member of the Bitcoin community ("We" or "Us"). Mempool Space K.K. in Japan ("Mempool") has no affiliation with the operator of this Website, and does not sponsor or endorse the information provided herein.

diff --git a/frontend/src/app/components/rbf-timeline/rbf-timeline-tooltip.component.html b/frontend/src/app/components/rbf-timeline/rbf-timeline-tooltip.component.html index 84e20b75f..46cda0488 100644 --- a/frontend/src/app/components/rbf-timeline/rbf-timeline-tooltip.component.html +++ b/frontend/src/app/components/rbf-timeline/rbf-timeline-tooltip.component.html @@ -33,7 +33,7 @@

diff --git a/frontend/src/app/components/search-form/search-form.component.scss b/frontend/src/app/components/search-form/search-form.component.scss index cec555a4f..b84754397 100644 --- a/frontend/src/app/components/search-form/search-form.component.scss +++ b/frontend/src/app/components/search-form/search-form.component.scss @@ -15,8 +15,6 @@ form { margin-top: 5px; @media (min-width: 564px) { - margin-top: 0px; - margin-left: 5px; margin-right: -5px; } @media (min-width: 992px) { @@ -39,7 +37,7 @@ form { position: relative; width: 100%; @media (min-width: 768px) { - min-width: 400px; + min-width: 300px; } @media (min-width: 992px) { min-width: 142px; diff --git a/frontend/src/app/components/search-form/search-form.component.ts b/frontend/src/app/components/search-form/search-form.component.ts index 3bbe5bccb..8f5e65f96 100644 --- a/frontend/src/app/components/search-form/search-form.component.ts +++ b/frontend/src/app/components/search-form/search-form.component.ts @@ -178,7 +178,7 @@ export class SearchFormComponent implements OnInit { const addressPrefixSearchResults = result[0]; const lightningResults = result[1]; - // Do not show date and timestamp results for liquid and bisq + // Do not show date and timestamp results for liquid const isNetworkBitcoin = this.network === '' || this.network === 'testnet' || this.network === 'signet'; const matchesBlockHeight = this.regexBlockheight.test(searchText) && parseInt(searchText) <= this.stateService.latestBlockHeight; @@ -186,16 +186,10 @@ export class SearchFormComponent implements OnInit { const matchesUnixTimestamp = this.regexUnixTimestamp.test(searchText) && parseInt(searchText) <= Math.floor(Date.now() / 1000) && isNetworkBitcoin; const matchesTxId = this.regexTransaction.test(searchText) && !this.regexBlockhash.test(searchText); const matchesBlockHash = this.regexBlockhash.test(searchText); - let matchesAddress = !matchesTxId && this.regexAddress.test(searchText); + const matchesAddress = !matchesTxId && this.regexAddress.test(searchText); const otherNetworks = findOtherNetworks(searchText, this.network as any || 'mainnet', this.env); const liquidAsset = this.assets ? (this.assets[searchText] || []) : []; - - // Add B prefix to addresses in Bisq network - if (!matchesAddress && this.network === 'bisq' && getRegex('address', 'mainnet').test(searchText)) { - searchText = 'B' + searchText; - matchesAddress = !matchesTxId && this.regexAddress.test(searchText); - } - + if (matchesDateTime && searchText.indexOf('/') !== -1) { searchText = searchText.replace(/\//g, '-'); } @@ -299,7 +293,7 @@ export class SearchFormComponent implements OnInit { navigate(url: string, searchText: string, extras?: any, swapNetwork?: string) { - if (needBaseModuleChange(this.env.BASE_MODULE as 'liquid' | 'bisq' | 'mempool', swapNetwork as Network)) { + if (needBaseModuleChange(this.env.BASE_MODULE as 'liquid' | 'mempool', swapNetwork as Network)) { window.location.href = getTargetUrl(swapNetwork as Network, searchText, this.env); } else { this.router.navigate([this.relativeUrlPipe.transform(url, swapNetwork), searchText], extras); diff --git a/frontend/src/app/components/svg-images/svg-images.component.html b/frontend/src/app/components/svg-images/svg-images.component.html index 11dfe1d79..3fe3f56c8 100644 --- a/frontend/src/app/components/svg-images/svg-images.component.html +++ b/frontend/src/app/components/svg-images/svg-images.component.html @@ -60,9 +60,6 @@ - - - diff --git a/frontend/src/app/components/terms-of-service/terms-of-service.component.html b/frontend/src/app/components/terms-of-service/terms-of-service.component.html index c03f509e9..922ce9434 100644 --- a/frontend/src/app/components/terms-of-service/terms-of-service.component.html +++ b/frontend/src/app/components/terms-of-service/terms-of-service.component.html @@ -11,7 +11,7 @@
-

The mempool.space website, the liquid.network website, the bisq.markets website, their associated API services, and related network and server infrastructure (collectively, the "Website") are operated by Mempool Space K.K. in Japan ("Mempool", "We", or "Us") and self-hosted from AS142052.

+

The mempool.space website, the liquid.network website, their associated API services, and related network and server infrastructure (collectively, the "Website") are operated by Mempool Space K.K. in Japan ("Mempool", "We", or "Us") and self-hosted from AS142052.

This website and its API service (collectively, the "Website") are operated by a member of the Bitcoin community ("We" or "Us"). Mempool Space K.K. in Japan ("Mempool") has no affiliation with the operator of this Website, and does not sponsor or endorse the information provided herein.

@@ -61,7 +61,7 @@

The mempool.space explorer is Bitcoin Only.

-

Unfortunately, the liquid.network and bisq.markets explorers cannot be considered Bitcoin Only, as they may display altcoins as part of their core network functionality. We do not endorse any altcoins and encourage you to stay Bitcoin Only. Please see bitcoin-only.com for more Bitcoin Only resources.

+

Unfortunately, the liquid.network explorer cannot be considered Bitcoin Only, as they may display altcoins as part of their core network functionality. We do not endorse any altcoins and encourage you to stay Bitcoin Only. Please see bitcoin-only.com for more Bitcoin Only resources.


diff --git a/frontend/src/app/components/time/time.component.ts b/frontend/src/app/components/time/time.component.ts index 679604ff5..45070ad67 100644 --- a/frontend/src/app/components/time/time.component.ts +++ b/frontend/src/app/components/time/time.component.ts @@ -23,7 +23,7 @@ export class TimeComponent implements OnInit, OnChanges, OnDestroy { @Input() time: number; @Input() dateString: number; - @Input() kind: 'plain' | 'since' | 'until' | 'span' = 'plain'; + @Input() kind: 'plain' | 'since' | 'until' | 'span' | 'before' = 'plain'; @Input() fastRender = false; @Input() fixedRender = false; @Input() relative = false; @@ -86,7 +86,9 @@ export class TimeComponent implements OnInit, OnChanges, OnDestroy { seconds = Math.floor(this.time); } - if (seconds < 60) { + if (seconds < 1 && this.kind === 'span') { + return $localize`:@@date-base.immediately:Immediately`; + } else if (seconds < 60) { if (this.relative || this.kind === 'since') { return $localize`:@@date-base.just-now:Just now`; } else if (this.kind === 'until') { @@ -206,6 +208,29 @@ export class TimeComponent implements OnInit, OnChanges, OnDestroy { } } break; + case 'before': + if (number === 1) { + switch (unit) { // singular (1 day) + case 'year': return $localize`:@@time-before:${dateStrings.i18nYear}:DATE: before`; break; + case 'month': return $localize`:@@time-before:${dateStrings.i18nMonth}:DATE: before`; break; + case 'week': return $localize`:@@time-before:${dateStrings.i18nWeek}:DATE: before`; break; + case 'day': return $localize`:@@time-before:${dateStrings.i18nDay}:DATE: before`; break; + case 'hour': return $localize`:@@time-before:${dateStrings.i18nHour}:DATE: before`; break; + case 'minute': return $localize`:@@time-before:${dateStrings.i18nMinute}:DATE: before`; break; + case 'second': return $localize`:@@time-before:${dateStrings.i18nSecond}:DATE: before`; break; + } + } else { + switch (unit) { // plural (2 days) + case 'year': return $localize`:@@time-before:${dateStrings.i18nYears}:DATE: before`; break; + case 'month': return $localize`:@@time-before:${dateStrings.i18nMonths}:DATE: before`; break; + case 'week': return $localize`:@@time-before:${dateStrings.i18nWeeks}:DATE: before`; break; + case 'day': return $localize`:@@time-before:${dateStrings.i18nDays}:DATE: before`; break; + case 'hour': return $localize`:@@time-before:${dateStrings.i18nHours}:DATE: before`; break; + case 'minute': return $localize`:@@time-before:${dateStrings.i18nMinutes}:DATE: before`; break; + case 'second': return $localize`:@@time-before:${dateStrings.i18nSeconds}:DATE: before`; break; + } + } + break; default: if (number === 1) { switch (unit) { // singular (1 day) diff --git a/frontend/src/app/components/transaction/transaction.component.html b/frontend/src/app/components/transaction/transaction.component.html index f2a580d07..bcc8ff332 100644 --- a/frontend/src/app/components/transaction/transaction.component.html +++ b/frontend/src/app/components/transaction/transaction.component.html @@ -71,14 +71,15 @@
- + @@ -301,7 +302,7 @@
-

Details

+

Details

@@ -461,7 +462,7 @@
-

Details

+

Details

diff --git a/frontend/src/app/components/transaction/transaction.component.ts b/frontend/src/app/components/transaction/transaction.component.ts index 79f8ebebe..93fb97fac 100644 --- a/frontend/src/app/components/transaction/transaction.component.ts +++ b/frontend/src/app/components/transaction/transaction.component.ts @@ -42,6 +42,7 @@ interface AuditStatus { seen?: boolean; expected?: boolean; added?: boolean; + prioritized?: boolean; delayed?: number; accelerated?: boolean; conflict?: boolean; @@ -317,13 +318,15 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { fetchAudit ? this.apiService.getBlockAudit$(hash).pipe( map(audit => { const isAdded = audit.addedTxs.includes(txid); + const isPrioritized = audit.prioritizedTxs.includes(txid); const isAccelerated = audit.acceleratedTxs.includes(txid); const isConflict = audit.fullrbfTxs.includes(txid); const isExpected = audit.template.some(tx => tx.txid === txid); return { - seen: isExpected || !(isAdded || isConflict), + seen: isExpected || isPrioritized || isAccelerated, expected: isExpected, added: isAdded, + prioritized: isPrioritized, conflict: isConflict, accelerated: isAccelerated, }; diff --git a/frontend/src/app/components/transactions-list/transactions-list.component.ts b/frontend/src/app/components/transactions-list/transactions-list.component.ts index 8f91489c1..b66889f0b 100644 --- a/frontend/src/app/components/transactions-list/transactions-list.component.ts +++ b/frontend/src/app/components/transactions-list/transactions-list.component.ts @@ -154,7 +154,7 @@ export class TransactionsListComponent implements OnInit, OnChanges { }); } else { this.priceService.getBlockPrice$(this.blockTime, true, this.currency).pipe( - tap((price) => this.transactions.forEach((tx) => tx['price'] = price)), + tap((price) => this.transactions?.forEach((tx) => tx['price'] = price)), ).subscribe(); } } @@ -239,7 +239,7 @@ export class TransactionsListComponent implements OnInit, OnChanges { if (this.blockTime && this.transactions?.length && this.currency) { this.priceService.getBlockPrice$(this.blockTime, true, this.currency).pipe( - tap((price) => this.transactions.forEach((tx) => tx['price'] = price)), + tap((price) => this.transactions?.forEach((tx) => tx['price'] = price)), ).subscribe(); } const txIds = this.transactions.filter((tx) => !tx._outspends).map((tx) => tx.txid); diff --git a/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.ts b/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.ts index f59cd1a36..9bbca05dd 100644 --- a/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.ts +++ b/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.ts @@ -85,7 +85,6 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges { gradientColors = { '': ['#9339f4', '#105fb0', '#9339f400'], - bisq: ['#9339f4', '#105fb0', '#9339f400'], // liquid: ['#116761', '#183550'], liquid: ['#09a197', '#0f62af', '#09a19700'], // 'liquidtestnet': ['#494a4a', '#272e46'], diff --git a/frontend/src/app/dashboard/dashboard.component.html b/frontend/src/app/dashboard/dashboard.component.html index 813589313..5df8256ca 100644 --- a/frontend/src/app/dashboard/dashboard.component.html +++ b/frontend/src/app/dashboard/dashboard.component.html @@ -360,7 +360,8 @@
- Audit in progress: Bitcoin block height #{{ auditStatus.lastBlockAudit }} / #{{ auditStatus.bitcoinHeaders }} +
Indexing in progress
+
#{{ auditStatus.lastBlockAudit }} / #{{ auditStatus.bitcoinHeaders }}
\ No newline at end of file diff --git a/frontend/src/app/dashboard/dashboard.component.ts b/frontend/src/app/dashboard/dashboard.component.ts index a3155263e..f396ba6ae 100644 --- a/frontend/src/app/dashboard/dashboard.component.ts +++ b/frontend/src/app/dashboard/dashboard.component.ts @@ -1,8 +1,8 @@ import { AfterViewInit, ChangeDetectionStrategy, Component, HostListener, Inject, OnDestroy, OnInit, PLATFORM_ID } from '@angular/core'; import { combineLatest, EMPTY, fromEvent, interval, merge, Observable, of, Subject, Subscription, timer } from 'rxjs'; import { catchError, delayWhen, distinctUntilChanged, filter, map, scan, share, shareReplay, startWith, switchMap, takeUntil, tap, throttleTime } from 'rxjs/operators'; -import { AuditStatus, BlockExtended, CurrentPegs, FederationAddress, FederationUtxo, OptimizedMempoolStats, PegsVolume, RecentPeg } from '../interfaces/node-api.interface'; -import { MempoolInfo, TransactionStripped, ReplacementInfo } from '../interfaces/websocket.interface'; +import { AuditStatus, BlockExtended, CurrentPegs, FederationAddress, FederationUtxo, OptimizedMempoolStats, PegsVolume, RecentPeg, TransactionStripped } from '../interfaces/node-api.interface'; +import { MempoolInfo, ReplacementInfo } from '../interfaces/websocket.interface'; import { ApiService } from '../services/api.service'; import { StateService } from '../services/state.service'; import { WebsocketService } from '../services/websocket.service'; diff --git a/frontend/src/app/docs/api-docs/api-docs-data.ts b/frontend/src/app/docs/api-docs/api-docs-data.ts index e75254213..39a873f1f 100644 --- a/frontend/src/app/docs/api-docs/api-docs-data.ts +++ b/frontend/src/app/docs/api-docs/api-docs-data.ts @@ -9,8 +9,8 @@ const emptyCodeSample = { response: `` }; -const showJsExamplesDefault = { "": true, "testnet": true, "signet": true, "liquid": true, "liquidtestnet": false, "bisq": true }; -const showJsExamplesDefaultFalse = { "": false, "testnet": false, "signet": false, "liquid": false, "liquidtestnet": false, "bisq": false }; +const showJsExamplesDefault = { "": true, "testnet": true, "signet": true, "liquid": true, "liquidtestnet": false }; +const showJsExamplesDefaultFalse = { "": false, "testnet": false, "signet": false, "liquid": false, "liquidtestnet": false }; export const wsApiDocsData = { showJsExamples: showJsExamplesDefault, @@ -101,7 +101,6 @@ if __name__ == "__main__": codeSampleTestnet: emptyCodeSample, codeSampleSignet: emptyCodeSample, codeSampleLiquid: emptyCodeSample, - codeSampleBisq: emptyCodeSample, }; export const restApiDocsData = [ @@ -110,7 +109,7 @@ export const restApiDocsData = [ category: "general", fragment: "general", title: "General", - showConditions: bitcoinNetworks.concat(["bisq"]) + showConditions: bitcoinNetworks, }, { type: "endpoint", @@ -251,7 +250,6 @@ export const restApiDocsData = [ codeSampleSignet: emptyCodeSample, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -301,530 +299,6 @@ export const restApiDocsData = [ codeSampleSignet: emptyCodeSample, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, - } - } - }, - { - type: "endpoint", - category: "general", - httpRequestMethod: "GET", - fragment: "get-stats", - title: "GET Stats", - description: { - default: "Returns statistics about all Bisq transactions." - }, - urlString: "/stats", - showConditions: ["bisq"], - showJsExamples: showJsExamplesDefault, - codeExample: { - default: { - codeTemplate: { - commonJS: ` - const { %{0}: { statistics } } = mempoolJS(); - - const stats = await statistics.getStats(); - - document.getElementById("result").textContent = JSON.stringify(stats, undefined, 2); - `, - esModule: ` - const { %{0}: { statistics } } = mempoolJS(); - - const stats = await statistics.getStats(); - console.log(stats); - `, - curl: `/api/stats`, - }, - codeSampleMainnet: emptyCodeSample, - codeSampleTestnet: emptyCodeSample, - codeSampleSignet: emptyCodeSample, - codeSampleLiquid: emptyCodeSample, - codeSampleBisq: { - esModule: [], - commonJS: [], - curl: [], - response: `{ - addresses: 213825, - minted: 6148323.75, - burnt: 1830262.66, - spent_txos: 215705, - unspent_txos: 2572 -}` - }, - } - } - }, - { - type: "category", - category: "markets", - fragment: "markets", - title: "Markets", - showConditions: ["bisq"] - }, - { - type: "endpoint", - category: "markets", - httpRequestMethod: "GET", - fragment: "get-market-currencies", - title: "GET Market Currencies", - description: { - default: "Provides list of available currencies for a given base currency." - }, - urlString: "/currencies", - showConditions: ["bisq"], - showJsExamples: showJsExamplesDefault, - codeExample: { - default: { - codeTemplate: { - curl: `/api/currencies`, - commonJS: ` - const { %{0}: { markets } } = mempoolJS(); - - const currencies = await markets.getCurrencies(); - - document.getElementById("result").textContent = JSON.stringify(currencies, undefined, 2); - `, - esModule: ` - const { %{0}: { markets } } = mempoolJS(); - - const currencies = await markets.getCurrencies(); - console.log(currencies); - `, - }, - codeSampleMainnet: emptyCodeSample, - codeSampleTestnet: emptyCodeSample, - codeSampleSignet: emptyCodeSample, - codeSampleLiquid: emptyCodeSample, - codeSampleBisq: { - esModule: [], - commonJS: [], - curl: [], - response: `{ - BTC: { - code: 'BTC', - name: 'Bitcoin', - precision: 8, - _type: 'crypto' - } - ... -}`, - }, - } - } - }, - { - type: "endpoint", - category: "markets", - httpRequestMethod: "GET", - fragment: "get-market-depth", - title: "GET Market Depth", - description: { - default: "Provides list of open offer prices for a single market." - }, - urlString: "/depth?market=[:market]", - showConditions: ["bisq"], - showJsExamples: showJsExamplesDefault, - codeExample: { - default: { - codeTemplate: { - curl: `/api/depth?market=%{1}`, - commonJS: ` - const { %{0}: { markets } } = mempoolJS(); - - const market = "%{1}"; - - const depth = await markets.getDepth({ market }); - - document.getElementById("result").textContent = JSON.stringify(depth, undefined, 2); - `, - esModule: ` - const { %{0}: { markets } } = mempoolJS(); - - const market = "%{1}"; - - const depth = await markets.getDepth({ market }); - console.log(depth); - `, - }, - codeSampleMainnet: emptyCodeSample, - codeSampleTestnet: emptyCodeSample, - codeSampleSignet: emptyCodeSample, - codeSampleLiquid: emptyCodeSample, - codeSampleBisq: { - esModule: ['BTC_USD'], - commonJS: ['BTC_USD'], - curl: ['BTC_USD'], - response: `{ - btc_usd: { - buys: [ - '4.56941560', - ... - ], - sells: [ - '4.54668218', - ... - ] - } -}`, - }, - } - } - }, - { - type: "endpoint", - category: "markets", - httpRequestMethod: "GET", - fragment: "get-market-hloc", - title: "GET Market HLOC", - description: { - default: "Provides hi/low/open/close data for a given market. This can be used to generate a candlestick chart." - }, - urlString: "/hloc?market=[:market]", - showConditions: ["bisq"], - showJsExamples: showJsExamplesDefault, - codeExample: { - default: { - codeTemplate: { - curl: `/api/hloc?market=%{1}`, - commonJS: ` - const { %{0}: { markets } } = mempoolJS(); - - const market = "%{1}"; - - const hloc = await markets.getHloc({ market }); - - document.getElementById("result").textContent = JSON.stringify(hloc, undefined, 2); - `, - esModule: ` - const { %{0}: { markets } } = mempoolJS(); - - const market = "%{1}"; - - const hloc = await markets.getHloc({ market }); - console.log(hloc); - `, - }, - codeSampleMainnet: emptyCodeSample, - codeSampleTestnet: emptyCodeSample, - codeSampleSignet: emptyCodeSample, - codeSampleLiquid: emptyCodeSample, - codeSampleBisq: { - esModule: ['BTC_USD'], - commonJS: ['BTC_USD'], - curl: ['BTC_USD'], - response: `[ - { - period_start: 1609459200, - open: '30448.18510000', - close: '45717.81750000', - high: '77700.00000000', - low: '27500.00000000', - avg: '44613.01158471', - volume_right: '4923536.57150000', - volume_left: '110.36100000' - } - ... -]`, - }, - } - } - }, - { - type: "endpoint", - category: "markets", - httpRequestMethod: "GET", - fragment: "get-markets", - title: "GET Markets", - description: { - default: "Provides list of available markets." - }, - urlString: "/markets", - showConditions: ["bisq"], - showJsExamples: showJsExamplesDefault, - codeExample: { - default: { - codeTemplate: { - curl: `/api/markets`, - commonJS: ` - const { %{0}: { markets } } = mempoolJS(); - - const allMarkets = await markets.getMarkets(); - - document.getElementById("result").textContent = JSON.stringify(allMarkets, undefined, 2); - `, - esModule: ` - const { %{0}: { markets } } = mempoolJS(); - - const allMarkets = await markets.getMarkets(); - console.log(allMarkets); - `, - }, - codeSampleMainnet: emptyCodeSample, - codeSampleTestnet: emptyCodeSample, - codeSampleSignet: emptyCodeSample, - codeSampleLiquid: emptyCodeSample, - codeSampleBisq: { - esModule: ['BTC_USD'], - commonJS: ['BTC_USD'], - curl: ['BTC_USD'], - response: `{ - btc_brl: { - pair: 'btc_brl', - lname: 'Bitcoin', - rname: 'Brazilian Real', - lsymbol: 'BTC', - rsymbol: 'BRL', - lprecision: 8, - rprecision: 2, - ltype: 'crypto', - rtype: 'fiat', - name: 'Bitcoin/Brazilian Real' - }, - ... -}`, - }, - } - } - }, - { - type: "endpoint", - category: "markets", - httpRequestMethod: "GET", - fragment: "get-market-offers", - title: "GET Market Offers", - description: { - default: "Provides list of open offer details for a single market." - }, - urlString: "/offers?market=[:market]", - showConditions: ["bisq"], - showJsExamples: showJsExamplesDefault, - codeExample: { - default: { - codeTemplate: { - curl: `/api/offers?market=%{1}`, - commonJS: ` - const { %{0}: { markets } } = mempoolJS(); - - const market = "%{1}"; - - const offers = await markets.getOffers({ market }); - - document.getElementById("result").textContent = JSON.stringify(offers, undefined, 2); - `, - esModule: ` - const { %{0}: { markets } } = mempoolJS(); - - const market = "%{1}"; - - const offers = await markets.getOffers({ market }); - console.log(offers); - `, - }, - codeSampleMainnet: emptyCodeSample, - codeSampleTestnet: emptyCodeSample, - codeSampleSignet: emptyCodeSample, - codeSampleLiquid: emptyCodeSample, - codeSampleBisq: { - esModule: ['BTC_USD'], - commonJS: ['BTC_USD'], - curl: ['BTC_USD'], - response: `{ - btc_usd: { - buys: [ - { - offer_id: "ORHL1BE-0c193d04-be60-4657-ba42-cc172bb4ae5d-172", - offer_date: 1630207815462, - direction: "BUY", - min_amount: "0.00500000", - amount: "0.01500000", - price: "50030.24770000", - volume: "750.45370000", - payment_method: "AMAZON_GIFT_CARD", - offer_fee_txid: null - }, - ... - ], - sells: [ - { - offer_id: "nswiwkre-7676d5e6-e808-4c47-9c51-d5708e465ad5-172", - offer_date: 1630320354509, - direction: "SELL", - min_amount: "0.04170000", - amount: "0.04170000", - price: "49534.89880000", - volume: "2065.60520000", - payment_method: "CASH_DEPOSIT", - offer_fee_txid: null - }, - ... - ] - } -}`, - }, - } - } - }, - { - type: "endpoint", - category: "markets", - httpRequestMethod: "GET", - fragment: "get-market-ticker", - title: "GET Market Ticker", - description: { - default: "Provides 24-hour price ticker for single market or all markets." - }, - urlString: "/ticker?market=[:market]", - showConditions: ["bisq"], - showJsExamples: showJsExamplesDefault, - codeExample: { - default: { - codeTemplate: { - curl: `/api/ticker?market=%{1}`, - commonJS: ` - const { %{0}: { markets } } = mempoolJS(); - - const market = "%{1}"; - - const ticker = await markets.getTicker({ market }); - - document.getElementById("result").textContent = JSON.stringify(ticker, undefined, 2); - `, - esModule: ` - const { %{0}: { markets } } = mempoolJS(); - - const market = "%{1}"; - - const ticker = await markets.getTicker({ market }); - console.log(ticker); - `, - }, - codeSampleMainnet: emptyCodeSample, - codeSampleTestnet: emptyCodeSample, - codeSampleSignet: emptyCodeSample, - codeSampleLiquid: emptyCodeSample, - codeSampleBisq: { - esModule: ['BTC_USD'], - commonJS: ['BTC_USD'], - curl: ['BTC_USD'], - response: `{ - last: "53923.20570000", - high: "53923.20570000", - low: "48137.67410000", - volume_left: "0.27160000", - volume_right: "13593.92070000", - buy: "48118.52400000", - sell: "49555.63750000" -}`, - }, - } - } - }, - { - type: "endpoint", - category: "markets", - httpRequestMethod: "GET", - fragment: "get-market-trades", - title: "GET Market Trades", - description: { - default: "Provides list of completed trades for a single market." - }, - urlString: "/trades?market=[:market]&limit=[:limit]", - showConditions: ["bisq"], - showJsExamples: showJsExamplesDefault, - codeExample: { - default: { - codeTemplate: { - curl: `/api/trades?market=%{1}&limit=%{2}`, - commonJS: ` - const { %{0}: { markets } } = mempoolJS(); - - const market = "%{1}"; - - const trades = await markets.getTrades({ market, limit: %{2} }); - - document.getElementById("result").textContent = JSON.stringify(trades, undefined, 2); - `, - esModule: ` - const { %{0}: { markets } } = mempoolJS(); - - const market = "%{1}"; - - const trades = await markets.getTrades({ market, limit: %{2} }); - console.log(trades); - `, - }, - codeSampleMainnet: emptyCodeSample, - codeSampleTestnet: emptyCodeSample, - codeSampleSignet: emptyCodeSample, - codeSampleLiquid: emptyCodeSample, - codeSampleBisq: { - esModule: ['BTC_USD', '1'], - commonJS: ['BTC_USD', '1'], - curl: ['BTC_USD', '1'], - response: `[ - { - price: "53923.20570000", - amount: "0.00500000", - volume: "269.61600000", - payment_method: "CLEAR_X_CHANGE", - trade_date: 1630646161647 - } -]`, - }, - } - } - }, - { - type: "endpoint", - category: "markets", - httpRequestMethod: "GET", - fragment: "get-market-volumes", - title: "GET Market Volumes", - description: { - default: "Provides periodic volume data in terms of base currency for one or all markets." - }, - urlString: "/volumes?basecurrency=[:basecurrency]", - showConditions: ["bisq"], - showJsExamples: showJsExamplesDefault, - codeExample: { - default: { - codeTemplate: { - curl: `/api/volumes?markets=%{1}`, - commonJS: ` - const { %{0}: { markets } } = mempoolJS(); - - const market = "%{1}"; - - const volumes = await markets.getVolumes({ market }); - - document.getElementById("result").textContent = JSON.stringify(volumes, undefined, 2); - `, - esModule: ` - const { %{0}: { markets } } = mempoolJS(); - - const market = "%{1}"; - - const volumes = await markets.getVolumes({ market }); - console.log(volumes); - `, - }, - codeSampleMainnet: emptyCodeSample, - codeSampleTestnet: emptyCodeSample, - codeSampleSignet: emptyCodeSample, - codeSampleLiquid: emptyCodeSample, - codeSampleBisq: { - esModule: ['BTC_USD', 'BTC'], - commonJS: ['BTC_USD', 'BTC'], - curl: ['BTC_USD', 'BTC'], - response: `[ - { - period_start: 1451606400, - num_trades: 1923, - volume: "1095.22050000" - }, - ... -]`, - }, } } }, @@ -833,7 +307,7 @@ export const restApiDocsData = [ category: "addresses", fragment: "addresses", title: "Addresses", - showConditions: bitcoinNetworks.concat(liquidNetworks).concat(["bisq"]) + showConditions: bitcoinNetworks.concat(liquidNetworks) }, { type: "endpoint", @@ -845,7 +319,7 @@ export const restApiDocsData = [ default: "Returns details about an address. Available fields: address, chain_stats, and mempool_stats. chain_stats and mempool_stats each contain an object with tx_count, funded_txo_count, funded_txo_sum, spent_txo_count, and spent_txo_sum." }, urlString: "/address/:address", - showConditions: bitcoinNetworks.concat(liquidNetworks).concat(["bisq"]), + showConditions: bitcoinNetworks.concat(liquidNetworks), showJsExamples: showJsExamplesDefault, codeExample: { default: { @@ -969,28 +443,6 @@ export const restApiDocsData = [ } }` }, - codeSampleBisq: { - esModule: [`B1DgwRN92rdQ9xpEVCdXRfgeqGw9X4YtrZz`], - commonJS: [`B1DgwRN92rdQ9xpEVCdXRfgeqGw9X4YtrZz`], - curl: [`B1DgwRN92rdQ9xpEVCdXRfgeqGw9X4YtrZz`], - response: `[ - { - "txVersion": "1", - "id": "d6f0a6fd191ac907ff88fc51af91cae8d50e596a846952ffa0ad0cea84eedc9a", - "blockHeight": 679129, - "blockHash": "00000000000000000001328850b0482312325f7f4abd5457e45d37cad664675d", - "time": 1618369311000, - "inputs": [ ... ], - "outputs": [ ... ], - "txType": "PAY_TRADE_FEE", - "txTypeDisplayString": "Pay trade fee", - "burntFee": 6, - "invalidatedBsq": 0, - "unlockBlockHeight": 0 - }, - ... -]` - }, } } }, @@ -1144,7 +596,6 @@ export const restApiDocsData = [ ... ]` }, - codeSampleBisq: emptyCodeSample, } } }, @@ -1298,12 +749,6 @@ export const restApiDocsData = [ ... ]` }, - codeSampleBisq: { - esModule: [], - commonJS: [], - curl: [], - response: '' - }, } } }, @@ -1429,7 +874,6 @@ export const restApiDocsData = [ } ]` }, - codeSampleBisq: emptyCodeSample, } } }, @@ -1562,7 +1006,6 @@ export const restApiDocsData = [ } ]`, }, - codeSampleBisq: emptyCodeSample, } } }, @@ -1621,7 +1064,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -1729,7 +1171,6 @@ export const restApiDocsData = [ "ticker": "LCAD" }`, }, - codeSampleBisq: emptyCodeSample, } } }, @@ -1816,7 +1257,6 @@ export const restApiDocsData = [ ... ]`, }, - codeSampleBisq: emptyCodeSample, } } }, @@ -1867,7 +1307,6 @@ export const restApiDocsData = [ curl: [`05aa9f02a06da37f2a0a572c49ac381499a16a643ad7c70c51ac94560778c92e`], response: `1000`, }, - codeSampleBisq: emptyCodeSample, } } }, @@ -1951,7 +1390,7 @@ export const restApiDocsData = [ category: "blocks", fragment: "blocks", title: "Blocks", - showConditions: bitcoinNetworks.concat(liquidNetworks).concat(["bisq"]) + showConditions: bitcoinNetworks.concat(liquidNetworks) }, { type: "endpoint", @@ -1964,7 +1403,7 @@ export const restApiDocsData = [ liquid: "Returns details about a block. Available fields: id, height, version, timestamp, bits, nonce, merkle_root, tx_count, size, weight,proof, and previousblockhash." }, urlString: "/block/:hash", - showConditions: bitcoinNetworks.concat(liquidNetworks).concat(["bisq"]), + showConditions: bitcoinNetworks.concat(liquidNetworks), showJsExamples: showJsExamplesDefault, codeExample: { default: { @@ -2120,18 +1559,6 @@ export const restApiDocsData = [ ext: {...} }`, }, - codeSampleBisq: { - esModule: ['0000000000000000000b24f70ed27da8b282b050f38e20831923211a1f7266d5'], - commonJS: ['0000000000000000000b24f70ed27da8b282b050f38e20831923211a1f7266d5'], - curl: ['0000000000000000000b24f70ed27da8b282b050f38e20831923211a1f7266d5'], - response: `{ - height: 698746, - time: 1630621494000, - hash: "0000000000000000000b24f70ed27da8b282b050f38e20831923211a1f7266d5", - previousBlockHash: "000000000000000000039cd226a99c125ee3004e9d585b04e2ccceccddef7547", - txs: [] -}` - }, } } }, @@ -2197,7 +1624,6 @@ export const restApiDocsData = [ curl: [`8f7cb70f32e2069724212c986f34462fc40180eabf189b44486faf6989824f9a`], response: `000000a0263542a60466e252dbc301001f2f87cdd232106344209d6c252bbda572fd4527b4b9a8412c0ecaca405241beaa6779e74d505a481941a873be74b0b34511cce7d806d261515c020001220020e9e4117540f7f23b3edd7c2cad660a17fb33c7959b8c37cf61d92b189133929a96000000fbee9cea00d8efdc49cfbec328537e0d7032194de6ebf3cf42e5c05bb89a08b10003004730440220303a6fc365e016422bd5d714e403db237964c9e53c244310a4a03f432583290202206951e82c2ffa028f88d64d9bb4ec7789ced137046bb38a02816617b554efd42b012551210217e403ddb181872c32a0cd468c710040b2f53d8cac69f18dad07985ee37e9a7151ae`, }, - codeSampleBisq: emptyCodeSample, } } }, @@ -2260,7 +1686,6 @@ export const restApiDocsData = [ curl: [`150000`], response: `67d5eb1aee63c6c2058a088985503ff0626fd3f7f8022bdc74fab36a359164db`, }, - codeSampleBisq: emptyCodeSample, } } }, @@ -2315,7 +1740,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -2381,7 +1805,6 @@ export const restApiDocsData = [ curl: [`67d5eb1aee63c6c2058a088985503ff0626fd3f7f8022bdc74fab36a359164db`], response: '', }, - codeSampleBisq: emptyCodeSample, } } }, @@ -2467,7 +1890,6 @@ export const restApiDocsData = [ next_best: "2f24f3d94c006971b86fe2c9cdc92a7ed0aa7ec3b0643a836b8d8b5a54103bab" }`, }, - codeSampleBisq: emptyCodeSample, } } }, @@ -2481,7 +1903,7 @@ export const restApiDocsData = [ default: "Returns the height of the last block." }, urlString: "/blocks/tip/height", - showConditions: bitcoinNetworks.concat(liquidNetworks).concat(["bisq"]), + showConditions: bitcoinNetworks.concat(liquidNetworks), showJsExamples: showJsExamplesDefault, codeExample: { default: { @@ -2531,12 +1953,6 @@ export const restApiDocsData = [ curl: [''], response: `162495`, }, - codeSampleBisq: { - esModule: [''], - commonJS: [''], - curl: [''], - response: `698765` - }, } } }, @@ -2600,7 +2016,6 @@ export const restApiDocsData = [ curl: [''], response: `ff643a1e102b555103d8feb20b296ee5cf3b4a202fa284e5d6ce82945b738ae7`, }, - codeSampleBisq: emptyCodeSample, } } }, @@ -2666,7 +2081,6 @@ export const restApiDocsData = [ curl: ['b6b4aeefa220c6a17da116bda666e869b3146967d2479656448a8bce1e799b8f', '1'], response: `41493aa0eec8b6d359c2defc90e2fafb42fb5b8633456648553467a4d3a16c4a` }, - codeSampleBisq: emptyCodeSample, } } }, @@ -2761,7 +2175,6 @@ export const restApiDocsData = [ "fa6b8dda9037f8284a659627005ad32dbb81e22b102c1d3d8a9bab0893ce2ab7" ]` }, - codeSampleBisq: emptyCodeSample, } } }, @@ -2917,7 +2330,6 @@ export const restApiDocsData = [ ... ]` }, - codeSampleBisq: emptyCodeSample, } } }, @@ -3121,7 +2533,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -3334,7 +2745,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -3409,87 +2819,6 @@ export const restApiDocsData = [ mediantime: 1640871614 }, ... -]` - }, - codeSampleBisq: emptyCodeSample, - } - } - }, - { - type: "endpoint", - category: "blocks", - httpRequestMethod: "GET", - fragment: "get-blocks", - title: "GET Blocks", - description: { - default: "

Returns the past n blocks with BSQ transactions starting m blocks ago.

Assume a block height of 700,000. Query /blocks/0/10 for the past 10 blocks before 700,000 with BSQ transactions. Query /blocks/1000/10 for the past 10 blocks before 699,000 with BSQ transactions." - }, - urlString: "/blocks/:m/:n", - showConditions: ["bisq"], - showJsExamples: showJsExamplesDefault, - codeExample: { - default: { - codeTemplate: { - curl: `/api/blocks/%{1}/%{2}`, - commonJS: ` - const { %{0}: { blocks } } = mempoolJS(); - - const getBlocks = await blocks.getBlocks({ index: %{1}, length: %{2} }); - - document.getElementById("result").textContent = JSON.stringify(getBlocks, undefined, 2); - `, - esModule: ` - const { %{0}: { blocks } } = mempoolJS(); - - const getBlocks = await blocks.getBlocks({ index: %{1}, length: %{2} }); - console.log(getBlocks); - `, - }, - codeSampleMainnet: emptyCodeSample, - codeSampleTestnet: emptyCodeSample, - codeSampleSignet: emptyCodeSample, - codeSampleLiquid: emptyCodeSample, - codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: { - esModule: ['0', '5'], - commonJS: ['0', '5'], - curl: ['0', '5'], - response: `[ - { - "height": 739030, - "time": 1654203258000, - "hash": "000000000000000000036bc04416ddeec264cbb977a9cd9e454897acb547b601", - "previousBlockHash": "00000000000000000000f49261617b589d76e5e70529ea1d4c16f3e19ddcb8ef", - "txs": [ ... ], - }, - { - "height": 739029, - "time": 1654203236000, - "hash": "00000000000000000000f49261617b589d76e5e70529ea1d4c16f3e19ddcb8ef", - "previousBlockHash": "00000000000000000008dd87e9486cd0d71c5d84e452432bab33c2a0cbaa31ce", - "txs": [ ... ], - }, - { - "height": 739025, - "time": 1654199569000, - "hash": "000000000000000000021e9ce82dec208af75807f92a9b1d9dae91f2b4d40e24", - "previousBlockHash": "00000000000000000002db644c025a76464b466d25900402452b07213b30c40b", - "txs": [ ... ] - }, - { - "height": 739023, - "time": 1654198597000, - "hash": "0000000000000000000702ce10250a46bea4155ca7acb869f3ea92c1e3a68bc5", - "previousBlockHash": "00000000000000000002b3d6c1adc5676262ded84181982f88dbd357b9f9d1ec", - "txs": [ ... ] - }, - { - "height": 739020, - "time": 1654197263000, - "hash": "000000000000000000046eb46ad941028381d3534c35658f9c80de0641dbbb31", - "previousBlockHash": "000000000000000000073f1c49b4c4895f3fa6b866d1e21ab8b22f3f9318b42f", - "txs": [ ... ] - } ]` }, } @@ -3597,7 +2926,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -3690,7 +3018,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -3797,7 +3124,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -3918,7 +3244,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -4128,7 +3453,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -4248,7 +3572,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -4343,7 +3666,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -4404,7 +3726,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -4503,7 +3824,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -4597,7 +3917,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -4724,7 +4043,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -4868,7 +4186,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -4977,7 +4294,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -5035,7 +4351,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -5120,7 +4435,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -5269,7 +4583,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -5430,7 +4743,6 @@ export const restApiDocsData = [ } ]` }, - codeSampleBisq: emptyCodeSample, } } }, @@ -5524,7 +4836,6 @@ export const restApiDocsData = [ minimumFee: 0.1 }` }, - codeSampleBisq: emptyCodeSample, } } }, @@ -5635,7 +4946,6 @@ export const restApiDocsData = [ ] }` }, - codeSampleBisq: emptyCodeSample, } } }, @@ -5722,7 +5032,6 @@ export const restApiDocsData = [ "dfbe66e6e71e775c9529a822c14286de0ee1066c2760a53552615d05e17006f3" ]` }, - codeSampleBisq: emptyCodeSample, } } }, @@ -5829,7 +5138,6 @@ export const restApiDocsData = [ ... ]` }, - codeSampleBisq: emptyCodeSample, } } }, @@ -5960,7 +5268,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -6090,7 +5397,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -6099,7 +5405,7 @@ export const restApiDocsData = [ category: "transactions", fragment: "transactions", title: "Transactions", - showConditions: bitcoinNetworks.concat(liquidNetworks).concat(["bisq"]) + showConditions: bitcoinNetworks.concat(liquidNetworks) }, { type: "endpoint", @@ -6163,7 +5469,6 @@ export const restApiDocsData = [ curl: ['txid'], response: `` }, - codeSampleBisq: emptyCodeSample, } } }, @@ -6177,7 +5482,7 @@ export const restApiDocsData = [ default: "Returns details about a transaction. Available fields: txid, version, locktime, size, weight, fee, vin, vout, and status." }, urlString: "/tx/:txid", - showConditions: bitcoinNetworks.concat(liquidNetworks).concat(["bisq"]), + showConditions: bitcoinNetworks.concat(liquidNetworks), showJsExamples: showJsExamplesDefault, codeExample: { default: { @@ -6302,27 +5607,6 @@ export const restApiDocsData = [ block_hash: "05a51089255650a16c17b4b3f3977376bc7ebe90a35584578f12916c3eaba59e", block_time: 1642000444 } -}`, - }, - codeSampleBisq: { - esModule: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - commonJS: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - curl: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - response: `{ - txid: "98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e", - version: 1, - locktime: 0, - vin: [], - vout: [], - size: 402, - weight: 957, - fee: 2390, - status: { - confirmed: true, - block_height: 698788, - block_hash: "00000000000000000005bfe17b41395bed53565022e0c98965b15ec1d00b1f31", - block_time: 1630645738 - } }`, }, } @@ -6390,12 +5674,6 @@ export const restApiDocsData = [ curl: [`59dd7a0bce4f3310272ff352402291bc555f141149812d8f573f62e7fdc19cc4`], response: `020000000102fa567669f73a314138aa6dbe74e3935612895df273d20ccbbedbecd44a04d3ce0000000000fdffffff8412fed07b8316dd4304df90af6f20292d3b2950133711c0ee43eb94fe12cc4f0100000000fdffffff040b801035010192095b8d9316f28450e98a85c915994c3f80ecc493adf505d73e9609a51e48bc0f35e34f88c482654d659fa779dcbf0457dc71053f3edcf76bd3667f03821ffcc4fc4ae5c2668685fec678e4...`, }, - codeSampleBisq: { - esModule: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - commonJS: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - curl: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - response: `0100000000010222ae3642a9300262f6e730e8bfb7979b15852c8836f3835beef9cd58c464e5f70000000000ffffffff22ae3642a9300262f6e730e8bfb7979b15852c8836f3835beef9cd58c464e5f70200000000ffffffff03de0900000000000016001490f9ee145d7b1c9352b793350741da97f3e4d795aca80500000000001600144168859b4b74a09277969fb8152115aea9d33a159c960600000000001600146534b1859209d8ae8f1a8...`, - }, } }, }, @@ -6451,12 +5729,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: { - esModule: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - commonJS: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - curl: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - response: `04000020e05c5f176bdb7966b44388ca223bef6e548fb390a9f202000000000000000000f10d5017f8e98200ea6e9d9a90d48e8078a49f2ee1da2cae9f80f48a0badfdaaeaad3161a0fa0f174d163a5daa0400000c77d2b87749e72de52feacaab57134c40172ae247c9de1f8f180736a8ef64a024542ab6b22b2c1fc961eae3d7d7d6c5f...`, - }, } } }, @@ -6572,28 +5844,6 @@ export const restApiDocsData = [ "1dbe7041197b78f73c0d4a3810c47080c252bc928f041b787acaad3fa76ba7a0" ], pos: 1 -}`, - }, - codeSampleBisq: { - esModule: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - commonJS: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - curl: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - response: `{ - block_height: 698788, - merkle: [ - "455eb8942edf5444f0130194353185705e891fb328b47fd5c43c0f5260de8121", - "98d18cb3470a3ee27a1d083e8f7baf76eaed19b5c972af33a335acdb3374dc75", - "1bf53a838bef7d64c2f7207bdb054df7dcc58e335ba9bd43803d00a24b4aec1b", - "19033925798e6e09f385dd7b5afbd76136f910b21b75ca03a2692ee804e0860e", - "5b4bd0b3cbbd5b73ae36d00bd144ee8db0966ff1f78c4483a4a8d601dc0b2ded", - "485a0c2af1687efe5433d4621c5dd222f6c5d6d7d7e3ea61c91f2c2bb2b62a54", - "1af9cbc4539b66e44c9bd6d07c5720301ba4694088e06b7f5978686b7a94aa62", - "b7a27d5a849f30cdf82c19b3d84902a146a0723a0798e46f91028f412af0d14d", - "b3925e68565674c54b3f0beb9f5f3820f4cd35cd15683b119cd232f52024a997", - "24a064efa83607188f1fdec947e22a17404c1357abcaea2fe52de74977b8d277", - "bd818bb2791a0d536097163c0d4dfb4dc3657cbd169617ca286dcc828ddf444d" - ], - pos: 546 }`, }, } @@ -6715,22 +5965,6 @@ export const restApiDocsData = [ block_hash: "3b10cdce761c4a2ec3e1239648c7d034922b34608a66f894e2f707307dae6b18", block_time: 1642002136 } -}`, - }, - codeSampleBisq: { - esModule: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`, '1'], - commonJS: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`, '1'], - curl: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`, '1'], - response: `{ - spent: true, - txid: "455eb8942edf5444f0130194353185705e891fb328b47fd5c43c0f5260de8121", - vin: 0, - status: { - confirmed: true, - block_height: 698788, - block_hash: "00000000000000000005bfe17b41395bed53565022e0c98965b15ec1d00b1f31", - block_time: 1630645738 - } }`, }, } @@ -6867,30 +6101,6 @@ export const restApiDocsData = [ { spent: false } -]`, - }, - codeSampleBisq: { - esModule: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - commonJS: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - curl: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - response: `[ - { - spent: false - }, - { - spent: true, - txid: "455eb8942edf5444f0130194353185705e891fb328b47fd5c43c0f5260de8121", - vin: 0, - status: { - confirmed: true, - block_height: 698788, - block_hash: "00000000000000000005bfe17b41395bed53565022e0c98965b15ec1d00b1f31", - block_time: 1630645738 - } - }, - { - spent: false - } ]`, }, } @@ -6958,12 +6168,6 @@ export const restApiDocsData = [ curl: ['59dd7a0bce4f3310272ff352402291bc555f141149812d8f573f62e7fdc19cc4'], response: ``, }, - codeSampleBisq: { - esModule: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - commonJS: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - curl: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - response: ``, - }, } } }, @@ -7105,7 +6309,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -7191,17 +6394,6 @@ export const restApiDocsData = [ block_height: 168765, block_hash: "05a51089255650a16c17b4b3f3977376bc7ebe90a35584578f12916c3eaba59e", block_time: 1642000444 -}`, - }, - codeSampleBisq: { - esModule: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - commonJS: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - curl: [`98a598aeea121ea061dc713d1547363358974191c257d3b563bbf2a1706ff44e`], - response: `{ - confirmed: true, - block_height: 698788, - block_hash: "00000000000000000005bfe17b41395bed53565022e0c98965b15ec1d00b1f31", - block_time: 1630645738 }`, }, } @@ -7249,64 +6441,6 @@ export const restApiDocsData = [ } } }, - { - type: "endpoint", - category: "transactions", - httpRequestMethod: "GET", - fragment: "get-transactions", - title: "GET Transactions", - description: { - default: "Returns :length of latest Bisq transactions, starting from :index." - }, - urlString: "/txs/:index/:length", - showConditions: ["bisq"], - showJsExamples: showJsExamplesDefault, - codeExample: { - default: { - codeTemplate: { - curl: `/api/txs/%{1}/%{2}`, - commonJS: ` - const { %{0}: { transactions } } = mempoolJS(); - - const txs = await transactions.getTxs({ index: %{1}, length: %{2} }); - - document.getElementById("result").textContent = JSON.stringify(txs, undefined, 2); - `, - esModule: ` - const { %{0}: { transactions } } = mempoolJS(); - - const txs = await transactions.getTxs({ index: %{1}, length: %{2} }); - console.log(txStatus); - `, - }, - codeSampleMainnet: emptyCodeSample, - codeSampleTestnet: emptyCodeSample, - codeSampleSignet: emptyCodeSample, - codeSampleLiquid: emptyCodeSample, - codeSampleBisq: { - esModule: [`0`, '1'], - commonJS: [`0`, '1'], - curl: [`0`, '1'], - response: `[ - { - txVersion: "1", - id: "be1b2932155c012bec79bbd0f7cf7db32a4a35859dcb7b70f5d35fea581ac30a", - blockHeight: 698808, - blockHash: "0000000000000000000bf9461e8e0b8e077bcc0e8fe0f55483a7fd5d0860336c", - time: 1630658066000, - inputs: [], - outputs: [], - txType: "PAY_TRADE_FEE", - txTypeDisplayString: "Pay trade fee", - burntFee: 609, - invalidatedBsq: 0, - unlockBlockHeight: 0 - } -]`, - }, - } - } - }, { type: "endpoint", category: "transactions", @@ -7371,7 +6505,6 @@ export const restApiDocsData = [ curl: [`0200000001fd5b5fcd1cb066c27cfc9fda5428b9be850b81ac440ea51f1ddba2f987189ac1010000008a4730440220686a40e9d2dbffeab4ca1ff66341d06a17806767f12a1fc4f55740a7af24c6b5022049dd3c9a85ac6c51fecd5f4baff7782a518781bbdd94453c8383755e24ba755c01410436d554adf4a3eb03a317c77aa4020a7bba62999df633bba0ea8f83f48b9e01b0861d3b3c796840f982ee6b14c3c4b7ad04fcfcc3774f81bff9aaf52a15751fedfdffffff02416c00000000000017a914bc791b2afdfe1e1b5650864a9297b20d74c61f4787d71d0000000000001976a9140a59837ccd4df25adc31cdad39be6a8d97557ed688ac00000000`], response: ``, }, - codeSampleBisq: emptyCodeSample, } } }, @@ -7475,7 +6608,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -7561,7 +6693,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -7784,7 +6915,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -7929,7 +7059,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -8049,7 +7178,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -8162,7 +7290,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -8286,7 +7413,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -8484,7 +7610,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -8681,7 +7806,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -8869,7 +7993,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -9034,7 +8157,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -9133,7 +8255,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -9299,7 +8420,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -9501,7 +8621,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -9638,7 +8757,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -9747,7 +8865,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -9856,7 +8973,6 @@ export const restApiDocsData = [ }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, - codeSampleBisq: emptyCodeSample, } } }, @@ -10036,18 +9152,18 @@ export const restApiDocsData = [ type: "endpoint", category: "accelerator-private", httpRequestMethod: "GET", - fragment: "accelerator-deposit-history", - title: "GET Deposit History", + fragment: "accelerator-top-up-history", + title: "GET Top Up History", description: { - default: "

Returns a list of deposits the user has made as prepayment for the accelerator service.

" + default: "

Returns a list of top ups the user has made as prepayment for the accelerator service.

" }, - urlString: "/v1/services/accelerator/deposit-history", + urlString: "/v1/services/accelerator/top-up-history", showConditions: [""], showJsExamples: showJsExamplesDefaultFalse, codeExample: { default: { codeTemplate: { - curl: `/api/v1/services/accelerator/deposit-history`, + curl: `/api/v1/services/accelerator/top-up-history`, commonJS: ``, esModule: `` }, diff --git a/frontend/src/app/docs/api-docs/api-docs.component.html b/frontend/src/app/docs/api-docs/api-docs.component.html index aef36603e..95c86693e 100644 --- a/frontend/src/app/docs/api-docs/api-docs.component.html +++ b/frontend/src/app/docs/api-docs/api-docs.component.html @@ -108,7 +108,7 @@
-
+
diff --git a/frontend/src/app/docs/api-docs/api-docs.component.ts b/frontend/src/app/docs/api-docs/api-docs.component.ts index 3198d80c6..dd2681ca8 100644 --- a/frontend/src/app/docs/api-docs/api-docs.component.ts +++ b/frontend/src/app/docs/api-docs/api-docs.component.ts @@ -179,10 +179,6 @@ export class ApiDocsComponent implements OnInit, AfterViewInit { if (network === 'liquidtestnet') { curlResponse = code.codeSampleLiquidTestnet.curl; } - if (network === 'bisq') { - curlResponse = code.codeSampleBisq.curl; - } - let curlNetwork = ''; if (this.env.BASE_MODULE === 'mempool') { if (!['', 'mainnet'].includes(network)) { diff --git a/frontend/src/app/docs/code-template/code-template.component.ts b/frontend/src/app/docs/code-template/code-template.component.ts index 6b91f5a9d..395f6a297 100644 --- a/frontend/src/app/docs/code-template/code-template.component.ts +++ b/frontend/src/app/docs/code-template/code-template.component.ts @@ -36,9 +36,6 @@ export class CodeTemplateComponent implements OnInit { npmGithubLink(){ let npmLink = `https://github.com/mempool/mempool.js`; - if (this.network === 'bisq') { - npmLink = `https://github.com/mempool/mempool.js/tree/main/npm-bisq-js`; - } if (this.network === 'liquid' || this.network === 'liquidtestnet') { npmLink = `https://github.com/mempool/mempool.js/tree/main/npm-liquid-js`; } @@ -47,9 +44,6 @@ export class CodeTemplateComponent implements OnInit { npmModuleLink() { let npmLink = `https://www.npmjs.org/package/@mempool/mempool.js`; - if (this.network === 'bisq') { - npmLink = `https://www.npmjs.org/package/@mempool/bisq.js`; - } if (this.network === 'liquid' || this.network === 'liquidtestnet') { npmLink = `https://www.npmjs.org/package/@mempool/liquid.js`; } @@ -58,12 +52,12 @@ export class CodeTemplateComponent implements OnInit { normalizeHostsESModule(codeText: string) { if (this.env.BASE_MODULE === 'mempool') { - if (['liquid', 'bisq'].includes(this.network)) { + if (['liquid'].includes(this.network)) { codeText = codeText.replace('%{0}', this.network); } else { codeText = codeText.replace('%{0}', 'bitcoin'); } - if(['', 'main', 'liquid', 'bisq', 'liquidtestnet'].includes(this.network)) { + if(['', 'main', 'liquid', 'liquidtestnet'].includes(this.network)) { codeText = codeText.replace('mempoolJS();', `mempoolJS({ hostname: '${document.location.hostname}' });`); @@ -75,11 +69,6 @@ export class CodeTemplateComponent implements OnInit { } } - if (this.env.BASE_MODULE === 'bisq') { - codeText = codeText.replace('} = mempoolJS();', ` = bisqJS();`); - codeText = codeText.replace('{ %{0}: ', ''); - } - if (this.env.BASE_MODULE === 'liquid') { codeText = codeText.replace('} = mempoolJS();', ` = liquidJS();`); codeText = codeText.replace('{ %{0}: ', ''); @@ -89,12 +78,12 @@ export class CodeTemplateComponent implements OnInit { normalizeHostsCommonJS(codeText: string) { if (this.env.BASE_MODULE === 'mempool') { - if (['liquid', 'bisq'].includes(this.network)) { + if (['liquid'].includes(this.network)) { codeText = codeText.replace('%{0}', this.network); } else { codeText = codeText.replace('%{0}', 'bitcoin'); } - if(['', 'main', 'liquid', 'bisq'].includes(this.network)) { + if(['', 'main', 'liquid'].includes(this.network)) { codeText = codeText.replace('mempoolJS();', `mempoolJS({ hostname: '${document.location.hostname}' });`); @@ -106,11 +95,6 @@ export class CodeTemplateComponent implements OnInit { } } - if (this.env.BASE_MODULE === 'bisq') { - codeText = codeText.replace('} = mempoolJS();', ` = bisqJS();`); - codeText = codeText.replace('{ %{0}: ', ''); - } - if (this.env.BASE_MODULE === 'liquid') { codeText = codeText.replace('} = mempoolJS();', ` = liquidJS();`); codeText = codeText.replace('{ %{0}: ', ''); @@ -135,14 +119,8 @@ export class CodeTemplateComponent implements OnInit { if (this.network === 'liquid' || this.network === 'liquidtestnet') { codeText = this.replaceJSPlaceholder(codeText, code.codeSampleLiquid.esModule); } - if (this.network === 'bisq') { - codeText = this.replaceJSPlaceholder(codeText, code.codeSampleBisq.esModule); - } let importText = `import mempoolJS from "@mempool/mempool.js";`; - if (this.env.BASE_MODULE === 'bisq') { - importText = `import bisqJS from "@mempool/bisq.js";`; - } if (this.env.BASE_MODULE === 'liquid') { importText = `import liquidJS from "@mempool/liquid.js";`; } @@ -174,18 +152,12 @@ init();`; if (this.network === 'liquid' || this.network === 'liquidtestnet') { codeText = this.replaceJSPlaceholder(codeText, code.codeSampleLiquid.esModule); } - if (this.network === 'bisq') { - codeText = this.replaceJSPlaceholder(codeText, code.codeSampleBisq.esModule); - } if (code.noWrap) { return codeText; } let importText = ``; - if (this.env.BASE_MODULE === 'bisq') { - importText = ``; - } if (this.env.BASE_MODULE === 'liquid') { importText = ``; } @@ -224,14 +196,6 @@ npm install @mempool/mempool.js --save # yarn yarn add @mempool/mempool.js`; - if (this.env.BASE_MODULE === 'bisq') { - importTemplate = `# npm -npm install @mempool/bisq.js --save - -# yarn -yarn add @mempool/bisq.js`; - } - if (this.env.BASE_MODULE === 'liquid') { importTemplate = `# npm npm install @mempool/liquid.js --save @@ -257,9 +221,6 @@ yarn add @mempool/liquid.js`; if (this.network === 'liquidtestnet') { return this.replaceCurlPlaceholder(code.codeTemplate.curl, code.codeSampleLiquidTestnet); } - if (this.network === 'bisq') { - return this.replaceCurlPlaceholder(code.codeTemplate.curl, code.codeSampleBisq); - } if (this.network === '' || this.network === 'main') { return this.replaceCurlPlaceholder(code.codeTemplate.curl, code.codeSampleMainnet); } @@ -282,9 +243,6 @@ yarn add @mempool/liquid.js`; if (this.network === 'liquidtestnet') { return code.codeSampleLiquidTestnet.response; } - if (this.network === 'bisq') { - return code.codeSampleBisq.response; - } return code.codeSampleMainnet.response; } diff --git a/frontend/src/app/docs/docs.routing.module.ts b/frontend/src/app/docs/docs.routing.module.ts index 1babb1dcf..3c0eb961d 100644 --- a/frontend/src/app/docs/docs.routing.module.ts +++ b/frontend/src/app/docs/docs.routing.module.ts @@ -8,7 +8,7 @@ const browserWindowEnv = browserWindow.__env || {}; let routes: Routes = []; -if (browserWindowEnv.BASE_MODULE && (browserWindowEnv.BASE_MODULE === 'bisq' || browserWindowEnv.BASE_MODULE === 'liquid')) { +if (browserWindowEnv.BASE_MODULE && browserWindowEnv.BASE_MODULE === 'liquid') { routes = [ { path: '', diff --git a/frontend/src/app/docs/docs/docs.component.ts b/frontend/src/app/docs/docs/docs.component.ts index e3737b545..35080a19f 100644 --- a/frontend/src/app/docs/docs/docs.component.ts +++ b/frontend/src/app/docs/docs/docs.component.ts @@ -31,9 +31,8 @@ export class DocsComponent implements OnInit { ngOnInit(): void { this.websocket.want(['blocks']); this.env = this.stateService.env; - this.showWebSocketTab = ( ! ( ( this.stateService.network === "bisq" ) || ( this.stateService.network === "liquidtestnet" ) ) ); this.showFaqTab = ( this.env.BASE_MODULE === 'mempool' ) ? true : false; - this.showElectrsTab = this.stateService.env.OFFICIAL_MEMPOOL_SPACE && ( this.stateService.network !== "bisq" ); + this.showElectrsTab = this.stateService.env.OFFICIAL_MEMPOOL_SPACE; document.querySelector( "html" ).style.scrollBehavior = "smooth"; } @@ -50,10 +49,8 @@ export class DocsComponent implements OnInit { } else if( url[1].path === "rest" ) { this.activeTab = 1; this.seoService.setTitle($localize`:@@meta.title.docs.rest:REST API`); - if( this.stateService.network === 'liquid' || this.stateService.network === 'liquidtestnet' ) { + if (this.stateService.network === 'liquid' || this.stateService.network === 'liquidtestnet' ) { this.seoService.setDescription($localize`:@@meta.description.docs.rest-liquid:Documentation for the liquid.network REST API service: get info on addresses, transactions, assets, blocks, and more.`); - } else if( this.stateService.network === 'bisq' ) { - this.seoService.setDescription($localize`:@@meta.description.docs.rest-bisq:Documentation for the bisq.markets REST API service: get info on recent trades, current offers, transactions, network state, and more.`); } else { this.seoService.setDescription($localize`:@@meta.description.docs.rest-bitcoin:Documentation for the mempool.space REST API service: get info on addresses, transactions, blocks, fees, mining, the Lightning network, and more.`); } diff --git a/frontend/src/app/interfaces/node-api.interface.ts b/frontend/src/app/interfaces/node-api.interface.ts index f8057fda5..8441acc14 100644 --- a/frontend/src/app/interfaces/node-api.interface.ts +++ b/frontend/src/app/interfaces/node-api.interface.ts @@ -208,6 +208,7 @@ export interface BlockExtended extends Block { export interface BlockAudit extends BlockExtended { missingTxs: string[], addedTxs: string[], + prioritizedTxs: string[], freshTxs: string[], sigopTxs: string[], fullrbfTxs: string[], @@ -230,7 +231,8 @@ export interface TransactionStripped { rate?: number; // effective fee rate acc?: boolean; flags?: number | null; - status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'freshcpfp' | 'added' | 'censored' | 'selected' | 'rbf' | 'accelerated'; + time?: number; + status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'freshcpfp' | 'added' | 'prioritized' | 'censored' | 'selected' | 'rbf' | 'accelerated'; context?: 'projected' | 'actual'; } diff --git a/frontend/src/app/interfaces/websocket.interface.ts b/frontend/src/app/interfaces/websocket.interface.ts index 9553fef02..daf06603f 100644 --- a/frontend/src/app/interfaces/websocket.interface.ts +++ b/frontend/src/app/interfaces/websocket.interface.ts @@ -1,9 +1,10 @@ import { SafeResourceUrl } from '@angular/platform-browser'; import { ILoadingIndicators } from '../services/state.service'; import { Transaction } from './electrs.interface'; -import { BlockExtended, DifficultyAdjustment, RbfTree } from './node-api.interface'; +import { BlockExtended, DifficultyAdjustment, RbfTree, TransactionStripped } from './node-api.interface'; export interface WebsocketResponse { + backend?: 'esplora' | 'electrum' | 'none'; block?: BlockExtended; blocks?: BlockExtended[]; conversions?: any; @@ -35,7 +36,6 @@ export interface WebsocketResponse { 'track-rbf'?: string; 'track-rbf-summary'?: boolean; 'watch-mempool'?: boolean; - 'track-bisq-market'?: string; 'refresh-blocks'?: boolean; } @@ -92,20 +92,8 @@ export interface MempoolInfo { minrelaytxfee: number; // (numeric) Current minimum relay fee for transactions } -export interface TransactionStripped { - txid: string; - fee: number; - vsize: number; - value: number; - acc?: boolean; // is accelerated? - rate?: number; // effective fee rate - flags?: number; - status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'freshcpfp' | 'added' | 'censored' | 'selected' | 'rbf' | 'accelerated'; - context?: 'projected' | 'actual'; -} - // [txid, fee, vsize, value, rate, flags, acceleration?] -export type TransactionCompressed = [string, number, number, number, number, number, 1?]; +export type TransactionCompressed = [string, number, number, number, number, number, number, 1?]; // [txid, rate, flags, acceleration?] export type MempoolDeltaChange = [string, number, number, (1|0)]; diff --git a/frontend/src/app/lightning/lightning-api.service.ts b/frontend/src/app/lightning/lightning-api.service.ts index 4be878a18..736c12c13 100644 --- a/frontend/src/app/lightning/lightning-api.service.ts +++ b/frontend/src/app/lightning/lightning-api.service.ts @@ -23,9 +23,6 @@ export class LightningApiService { } this.apiBasePath = ''; // assume mainnet by default this.stateService.networkChanged$.subscribe((network) => { - if (network === 'bisq' && !this.stateService.env.BISQ_SEPARATE_BACKEND) { - network = ''; - } this.apiBasePath = network ? '/' + network : ''; }); } diff --git a/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.ts b/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.ts index ddcfb81ad..5599bc255 100644 --- a/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.ts +++ b/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.ts @@ -51,6 +51,7 @@ export class NodesPerISPChartComponent implements OnInit { ngOnInit(): void { if (!this.widget) { this.seoService.setTitle($localize`:@@8573a1576789bd2c4faeaed23037c4917812c6cf:Lightning Nodes Per ISP`); + this.seoService.setDescription($localize`:@@meta.description.lightning.nodes-per-isp:Browse the top 100 ISPs hosting Lightning nodes along with stats like total number of nodes per ISP, aggregate BTC capacity per ISP, and more`); } this.nodesPerAsObservable$ = combineLatest([ @@ -106,7 +107,7 @@ export class NodesPerISPChartComponent implements OnInit { ); if (this.widget) { - this.sortBySubject.next(false); + this.sortBySubject.next(false); } } diff --git a/frontend/src/app/services/api.service.ts b/frontend/src/app/services/api.service.ts index cd4e71385..9879255c1 100644 --- a/frontend/src/app/services/api.service.ts +++ b/frontend/src/app/services/api.service.ts @@ -29,9 +29,6 @@ export class ApiService { } this.apiBasePath = ''; // assume mainnet by default this.stateService.networkChanged$.subscribe((network) => { - if (network === 'bisq' && !this.stateService.env.BISQ_SEPARATE_BACKEND) { - network = ''; - } this.apiBasePath = network ? '/' + network : ''; }); } diff --git a/frontend/src/app/services/electrs-api.service.ts b/frontend/src/app/services/electrs-api.service.ts index 748194a21..57dcbb762 100644 --- a/frontend/src/app/services/electrs-api.service.ts +++ b/frontend/src/app/services/electrs-api.service.ts @@ -25,9 +25,6 @@ export class ElectrsApiService { } this.apiBasePath = ''; // assume mainnet by default this.stateService.networkChanged$.subscribe((network) => { - if (network === 'bisq') { - network = ''; - } this.apiBasePath = network ? '/' + network : ''; }); } diff --git a/frontend/src/app/services/enterprise.service.ts b/frontend/src/app/services/enterprise.service.ts index ba17668ef..4ad31bd9f 100644 --- a/frontend/src/app/services/enterprise.service.ts +++ b/frontend/src/app/services/enterprise.service.ts @@ -44,7 +44,6 @@ export class EnterpriseService { this.stateService.env.LIQUID_ENABLED = false; this.stateService.env.LIQUID_TESTNET_ENABLED = false; this.stateService.env.SIGNET_ENABLED = false; - this.stateService.env.BISQ_ENABLED = false; } fetchSubdomainInfo(): void { @@ -81,14 +80,6 @@ export class EnterpriseService { siteId = 10; statsUrl = '//stats.liquid.network/'; break; - case 'bisq.markets': - siteId = 7; - statsUrl = '//stats.bisq.markets/'; - break; - case 'bisq.ninja': - statsUrl = '//stats.bisq.markets/'; - siteId = 11; - break; default: return; } diff --git a/frontend/src/app/services/navigation.service.ts b/frontend/src/app/services/navigation.service.ts index 661a8c38f..2db3f6025 100644 --- a/frontend/src/app/services/navigation.service.ts +++ b/frontend/src/app/services/navigation.service.ts @@ -17,12 +17,7 @@ const networkModules = { { name: 'liquid', path: '' }, { name: 'liquidtestnet', path: '/testnet' }, ], - }, - bisq: { - subnets: [ - { name: 'bisq', path: '' }, - ], - }, + } }; const networks = Object.keys(networkModules); @@ -44,7 +39,7 @@ export class NavigationService { }); } - // For each network (bitcoin/liquid/bisq), find and save the longest url path compatible with the current route + // For each network (bitcoin/liquid), find and save the longest url path compatible with the current route updateSubnetPaths(root: ActivatedRouteSnapshot): void { let path = ''; const networkPaths = {}; diff --git a/frontend/src/app/services/seo.service.ts b/frontend/src/app/services/seo.service.ts index cb9a321d6..45d62ebdd 100644 --- a/frontend/src/app/services/seo.service.ts +++ b/frontend/src/app/services/seo.service.ts @@ -71,8 +71,6 @@ export class SeoService { let domain = 'mempool.space'; if (this.stateService.env.BASE_MODULE === 'liquid') { domain = 'liquid.network'; - } else if (this.stateService.env.BASE_MODULE === 'bisq') { - domain = 'bisq.markets'; } this.canonicalLink.setAttribute('href', 'https://' + domain + path); } @@ -86,8 +84,6 @@ export class SeoService { return this.baseTitle + ' - Liquid Network'; if (this.network === 'liquidtestnet') return this.baseTitle + ' - Liquid Testnet'; - if (this.network === 'bisq') - return this.baseTitle + ' - Bisq Markets'; return this.baseTitle + ' - ' + (this.network ? this.ucfirst(this.network) : 'Bitcoin') + ' Explorer'; } @@ -96,8 +92,6 @@ export class SeoService { return this.baseDescription + ' See the real-time status of your transactions, browse network stats, and more.'; if ( (this.network === 'liquid') || (this.network === 'liquidtestnet') ) return this.baseDescription + ' See Liquid transactions & assets, get network info, and more.'; - if (this.network === 'bisq') - return this.baseDescription + ' See Bisq market prices, trading activity, and more.'; } ucfirst(str: string) { diff --git a/frontend/src/app/services/services-api.service.ts b/frontend/src/app/services/services-api.service.ts index 9fcd9ea11..0caa06168 100644 --- a/frontend/src/app/services/services-api.service.ts +++ b/frontend/src/app/services/services-api.service.ts @@ -52,9 +52,6 @@ export class ServicesApiServices { } this.apiBasePath = ''; // assume mainnet by default this.stateService.networkChanged$.subscribe((network) => { - if (network === 'bisq' && !this.stateService.env.BISQ_SEPARATE_BACKEND) { - network = ''; - } this.apiBasePath = network ? '/' + network : ''; }); diff --git a/frontend/src/app/services/state.service.ts b/frontend/src/app/services/state.service.ts index f9de61b69..12caf9f53 100644 --- a/frontend/src/app/services/state.service.ts +++ b/frontend/src/app/services/state.service.ts @@ -1,8 +1,8 @@ import { Inject, Injectable, PLATFORM_ID, LOCALE_ID } from '@angular/core'; import { ReplaySubject, BehaviorSubject, Subject, fromEvent, Observable, merge } from 'rxjs'; import { Transaction } from '../interfaces/electrs.interface'; -import { HealthCheckHost, IBackendInfo, MempoolBlock, MempoolBlockDelta, MempoolInfo, Recommendedfees, ReplacedTransaction, ReplacementInfo, TransactionStripped } from '../interfaces/websocket.interface'; -import { BlockExtended, CpfpInfo, DifficultyAdjustment, MempoolPosition, OptimizedMempoolStats, RbfTree } from '../interfaces/node-api.interface'; +import { HealthCheckHost, IBackendInfo, MempoolBlock, MempoolBlockDelta, MempoolInfo, Recommendedfees, ReplacedTransaction, ReplacementInfo } from '../interfaces/websocket.interface'; +import { BlockExtended, CpfpInfo, DifficultyAdjustment, MempoolPosition, OptimizedMempoolStats, RbfTree, TransactionStripped } from '../interfaces/node-api.interface'; import { Router, NavigationStart } from '@angular/router'; import { isPlatformBrowser } from '@angular/common'; import { filter, map, scan, shareReplay } from 'rxjs/operators'; @@ -25,8 +25,6 @@ export interface Env { SIGNET_ENABLED: boolean; LIQUID_ENABLED: boolean; LIQUID_TESTNET_ENABLED: boolean; - BISQ_ENABLED: boolean; - BISQ_SEPARATE_BACKEND: boolean; ITEMS_PER_PAGE: number; KEEP_BLOCKS_AMOUNT: number; OFFICIAL_MEMPOOL_SPACE: boolean; @@ -40,7 +38,6 @@ export interface Env { PACKAGE_JSON_VERSION: string; MEMPOOL_WEBSITE_URL: string; LIQUID_WEBSITE_URL: string; - BISQ_WEBSITE_URL: string; MINING_DASHBOARD: boolean; LIGHTNING: boolean; AUDIT: boolean; @@ -49,6 +46,7 @@ export interface Env { SIGNET_BLOCK_AUDIT_START_HEIGHT: number; HISTORICAL_PRICE: boolean; ACCELERATOR: boolean; + PUBLIC_ACCELERATIONS: boolean; ADDITIONAL_CURRENCIES: boolean; GIT_COMMIT_HASH_MEMPOOL_SPACE?: string; PACKAGE_JSON_VERSION_MEMPOOL_SPACE?: string; @@ -60,8 +58,6 @@ const defaultEnv: Env = { 'LIQUID_ENABLED': false, 'LIQUID_TESTNET_ENABLED': false, 'BASE_MODULE': 'mempool', - 'BISQ_ENABLED': false, - 'BISQ_SEPARATE_BACKEND': false, 'ITEMS_PER_PAGE': 10, 'KEEP_BLOCKS_AMOUNT': 8, 'OFFICIAL_MEMPOOL_SPACE': false, @@ -74,7 +70,6 @@ const defaultEnv: Env = { 'PACKAGE_JSON_VERSION': '', 'MEMPOOL_WEBSITE_URL': 'https://mempool.space', 'LIQUID_WEBSITE_URL': 'https://liquid.network', - 'BISQ_WEBSITE_URL': 'https://bisq.markets', 'MINING_DASHBOARD': true, 'LIGHTNING': false, 'AUDIT': false, @@ -83,6 +78,7 @@ const defaultEnv: Env = { 'SIGNET_BLOCK_AUDIT_START_HEIGHT': 0, 'HISTORICAL_PRICE': true, 'ACCELERATOR': false, + 'PUBLIC_ACCELERATIONS': false, 'ADDITIONAL_CURRENCIES': false, }; @@ -92,6 +88,7 @@ const defaultEnv: Env = { export class StateService { isBrowser: boolean = isPlatformBrowser(this.platformId); isMempoolSpaceBuild = window['isMempoolSpaceBuild'] ?? false; + backend: 'esplora' | 'electrum' | 'none' = 'esplora'; network = ''; lightning = false; blockVSize: number; @@ -99,6 +96,7 @@ export class StateService { latestBlockHeight = -1; blocks: BlockExtended[] = []; + backend$ = new BehaviorSubject<'esplora' | 'electrum' | 'none'>('esplora'); networkChanged$ = new ReplaySubject(1); lightningChanged$ = new ReplaySubject(1); blocksSubject$ = new BehaviorSubject([]); @@ -214,11 +212,6 @@ export class StateService { } }, {})); - if (this.env.BASE_MODULE === 'bisq') { - this.network = this.env.BASE_MODULE; - this.networkChanged$.next(this.env.BASE_MODULE); - } - this.networkChanged$.subscribe((network) => { this.transactions$ = new BehaviorSubject(null); this.blocksSubject$.next([]); @@ -257,6 +250,10 @@ export class StateService { const rateUnitPreference = this.storageService.getValue('rate-unit-preference'); this.rateUnits$ = new BehaviorSubject(rateUnitPreference || 'vb'); + + this.backend$.subscribe(backend => { + this.backend = backend; + }); } setNetworkBasedonUrl(url: string) { @@ -267,22 +264,10 @@ export class StateService { // /^\/ starts with a forward slash... // (?:[a-z]{2}(?:-[A-Z]{2})?\/)? optional locale prefix (non-capturing) // (?:preview\/)? optional "preview" prefix (non-capturing) - // (bisq|testnet|liquidtestnet|liquid|signet)/ network string (captured as networkMatches[1]) + // (testnet|signet)/ network string (captured as networkMatches[1]) // ($|\/) network string must end or end with a slash - const networkMatches = url.match(/^\/(?:[a-z]{2}(?:-[A-Z]{2})?\/)?(?:preview\/)?(bisq|testnet|liquidtestnet|liquid|signet)($|\/)/); + const networkMatches = url.match(/^\/(?:[a-z]{2}(?:-[A-Z]{2})?\/)?(?:preview\/)?(testnet|signet)($|\/)/); switch (networkMatches && networkMatches[1]) { - case 'liquid': - if (this.network !== 'liquid') { - this.network = 'liquid'; - this.networkChanged$.next('liquid'); - } - return; - case 'liquidtestnet': - if (this.network !== 'liquidtestnet') { - this.network = 'liquidtestnet'; - this.networkChanged$.next('liquidtestnet'); - } - return; case 'signet': if (this.network !== 'signet') { this.network = 'signet'; @@ -290,7 +275,7 @@ export class StateService { } return; case 'testnet': - if (this.network !== 'testnet') { + if (this.network !== 'testnet' && this.network !== 'liquidtestnet') { if (this.env.BASE_MODULE === 'liquid') { this.network = 'liquidtestnet'; this.networkChanged$.next('liquidtestnet'); @@ -300,12 +285,6 @@ export class StateService { } } return; - case 'bisq': - if (this.network !== 'bisq') { - this.network = 'bisq'; - this.networkChanged$.next('bisq'); - } - return; default: if (this.env.BASE_MODULE !== 'mempool') { if (this.network !== this.env.BASE_MODULE) { diff --git a/frontend/src/app/services/websocket.service.ts b/frontend/src/app/services/websocket.service.ts index 61ac15fd3..de5170cbe 100644 --- a/frontend/src/app/services/websocket.service.ts +++ b/frontend/src/app/services/websocket.service.ts @@ -54,7 +54,7 @@ export class WebsocketService { .pipe(take(1)) .subscribe((response) => this.handleResponse(response)); } else { - this.network = this.stateService.network === 'bisq' && !this.stateService.env.BISQ_SEPARATE_BACKEND ? '' : this.stateService.network; + this.network = this.stateService.network; this.websocketSubject = webSocket(this.webSocketUrl.replace('{network}', this.network ? '/' + this.network : '')); const { response: theInitData } = this.transferState.get(initData, null) || {}; @@ -62,6 +62,7 @@ export class WebsocketService { if (theInitData.body.blocks) { theInitData.body.blocks = theInitData.body.blocks.reverse(); } + this.stateService.backend$.next(theInitData.backend); this.stateService.isLoadingWebSocket$.next(false); this.handleResponse(theInitData.body); this.startSubscription(false, true); @@ -70,9 +71,6 @@ export class WebsocketService { } this.stateService.networkChanged$.subscribe((network) => { - if (network === 'bisq' && !this.stateService.env.BISQ_SEPARATE_BACKEND) { - network = ''; - } if (network === this.network) { return; } @@ -237,14 +235,6 @@ export class WebsocketService { this.isTrackingRbfSummary = false; } - startTrackBisqMarket(market: string) { - this.websocketSubject.next({ 'track-bisq-market': market }); - } - - stopTrackingBisqMarket() { - this.websocketSubject.next({ 'track-bisq-market': 'stop' }); - } - fetchStatistics(historicalDate: string) { this.websocketSubject.next({ historicalDate }); } @@ -290,6 +280,10 @@ export class WebsocketService { handleResponse(response: WebsocketResponse) { let reinitBlocks = false; + if (response.backend) { + this.stateService.backend$.next(response.backend); + } + if (response.blocks && response.blocks.length) { const blocks = response.blocks; this.stateService.resetBlocks(blocks); diff --git a/frontend/src/app/shared/common.utils.ts b/frontend/src/app/shared/common.utils.ts index 18a330fab..be1e32c73 100644 --- a/frontend/src/app/shared/common.utils.ts +++ b/frontend/src/app/shared/common.utils.ts @@ -1,4 +1,5 @@ -import { MempoolBlockDelta, MempoolBlockDeltaCompressed, MempoolDeltaChange, TransactionCompressed, TransactionStripped } from "../interfaces/websocket.interface"; +import { MempoolBlockDelta, MempoolBlockDeltaCompressed, MempoolDeltaChange, TransactionCompressed } from "../interfaces/websocket.interface"; +import { TransactionStripped } from "../interfaces/node-api.interface"; export function isMobile(): boolean { return (window.innerWidth <= 767.98); @@ -164,7 +165,8 @@ export function uncompressTx(tx: TransactionCompressed): TransactionStripped { value: tx[3], rate: tx[4], flags: tx[5], - acc: !!tx[6], + time: tx[6], + acc: !!tx[7], }; } diff --git a/frontend/src/app/shared/components/global-footer/global-footer.component.html b/frontend/src/app/shared/components/global-footer/global-footer.component.html index b92e8a339..cc9cb3538 100644 --- a/frontend/src/app/shared/components/global-footer/global-footer.component.html +++ b/frontend/src/app/shared/components/global-footer/global-footer.component.html @@ -62,7 +62,6 @@

Signet Explorer

Liquid Testnet Explorer

Liquid Explorer

-

Bisq Explorer

Hash {{ block.id | shortenString : 13 }}
Fee span - -
Median fee~ + ~ - @@ -232,7 +233,7 @@ + [showFilters]="true" [excludeFilters]="['replacement']" [relativeTime]="block?.timestamp"> @@ -247,7 +248,7 @@ + [showFilters]="true" [excludeFilters]="['replacement']" [relativeTime]="block?.timestamp"> @@ -259,7 +260,7 @@ - +

diff --git a/frontend/src/app/components/block/block.component.ts b/frontend/src/app/components/block/block.component.ts index 2602c5ecd..13b0ecd76 100644 --- a/frontend/src/app/components/block/block.component.ts +++ b/frontend/src/app/components/block/block.component.ts @@ -2,7 +2,7 @@ import { Component, OnInit, OnDestroy, ViewChildren, QueryList, Inject, PLATFORM import { Location } from '@angular/common'; import { ActivatedRoute, ParamMap, Router } from '@angular/router'; import { ElectrsApiService } from '../../services/electrs-api.service'; -import { switchMap, tap, throttleTime, catchError, map, shareReplay, startWith } from 'rxjs/operators'; +import { switchMap, tap, throttleTime, catchError, map, shareReplay, startWith, filter } from 'rxjs/operators'; import { Transaction, Vout } from '../../interfaces/electrs.interface'; import { Observable, of, Subscription, asyncScheduler, EMPTY, combineLatest, forkJoin } from 'rxjs'; import { StateService } from '../../services/state.service'; @@ -371,6 +371,7 @@ export class BlockComponent implements OnInit, OnDestroy { const inTemplate = {}; const inBlock = {}; const isAdded = {}; + const isPrioritized = {}; const isCensored = {}; const isMissing = {}; const isSelected = {}; @@ -394,6 +395,9 @@ export class BlockComponent implements OnInit, OnDestroy { for (const txid of blockAudit.addedTxs) { isAdded[txid] = true; } + for (const txid of blockAudit.prioritizedTxs || []) { + isPrioritized[txid] = true; + } for (const txid of blockAudit.missingTxs) { isCensored[txid] = true; } @@ -443,6 +447,8 @@ export class BlockComponent implements OnInit, OnDestroy { tx.status = null; } else if (isAdded[tx.txid]) { tx.status = 'added'; + } else if (isPrioritized[tx.txid]) { + tx.status = 'prioritized'; } else if (inTemplate[tx.txid]) { tx.status = 'found'; } else if (isRbf[tx.txid]) { @@ -460,9 +466,9 @@ export class BlockComponent implements OnInit, OnDestroy { inBlock[tx.txid] = true; } - blockAudit.feeDelta = blockAudit.expectedFees > 0 ? (blockAudit.expectedFees - (this.block.extras.totalFees + this.oobFees)) / blockAudit.expectedFees : 0; - blockAudit.weightDelta = blockAudit.expectedWeight > 0 ? (blockAudit.expectedWeight - this.block.weight) / blockAudit.expectedWeight : 0; - blockAudit.txDelta = blockAudit.template.length > 0 ? (blockAudit.template.length - this.block.tx_count) / blockAudit.template.length : 0; + blockAudit.feeDelta = blockAudit.expectedFees > 0 ? (blockAudit.expectedFees - (this.block?.extras.totalFees + this.oobFees)) / blockAudit.expectedFees : 0; + blockAudit.weightDelta = blockAudit.expectedWeight > 0 ? (blockAudit.expectedWeight - this.block?.weight) / blockAudit.expectedWeight : 0; + blockAudit.txDelta = blockAudit.template.length > 0 ? (blockAudit.template.length - this.block?.tx_count) / blockAudit.template.length : 0; this.blockAudit = blockAudit; this.setAuditAvailable(true); } else { @@ -478,6 +484,7 @@ export class BlockComponent implements OnInit, OnDestroy { }); this.oobSubscription = block$.pipe( + filter(() => this.stateService.env.PUBLIC_ACCELERATIONS === true && this.stateService.network === ''), switchMap((block) => this.apiService.getAccelerationsByHeight$(block.height) .pipe( map(accelerations => { diff --git a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts index 02591f657..5141f4de9 100644 --- a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts +++ b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts @@ -64,7 +64,6 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy { gradientColors = { '': ['#9339f4', '#105fb0'], - bisq: ['#9339f4', '#105fb0'], liquid: ['#116761', '#183550'], 'liquidtestnet': ['#494a4a', '#272e46'], testnet: ['#1d486f', '#183550'], diff --git a/frontend/src/app/components/blocks-list/blocks-list.component.ts b/frontend/src/app/components/blocks-list/blocks-list.component.ts index 94afb6509..29b23e608 100644 --- a/frontend/src/app/components/blocks-list/blocks-list.component.ts +++ b/frontend/src/app/components/blocks-list/blocks-list.component.ts @@ -64,6 +64,15 @@ export class BlocksList implements OnInit { if (!this.widget) { this.websocketService.want(['blocks']); + + this.seoService.setTitle($localize`:@@8a7b4bd44c0ac71b2e72de0398b303257f7d2f54:Blocks`); + this.ogService.setManualOgImage('recent-blocks.jpg'); + if( this.stateService.network==='liquid'||this.stateService.network==='liquidtestnet' ) { + this.seoService.setDescription($localize`:@@meta.description.liquid.blocks:See the most recent Liquid${seoDescriptionNetwork(this.stateService.network)} blocks along with basic stats such as block height, block size, and more.`); + } else { + this.seoService.setDescription($localize`:@@meta.description.bitcoin.blocks:See the most recent Bitcoin${seoDescriptionNetwork(this.stateService.network)} blocks along with basic stats such as block height, block reward, block size, and more.`); + } + this.blocksCountInitializedSubscription = combineLatest([this.blocksCountInitialized$, this.route.queryParams]).pipe( filter(([blocksCountInitialized, _]) => blocksCountInitialized), tap(([_, params]) => { @@ -96,18 +105,7 @@ export class BlocksList implements OnInit { this.skeletonLines = this.widget === true ? [...Array(6).keys()] : [...Array(15).keys()]; this.paginationMaxSize = window.matchMedia('(max-width: 670px)').matches ? 3 : 5; - - if (!this.widget) { - this.seoService.setTitle($localize`:@@m8a7b4bd44c0ac71b2e72de0398b303257f7d2f54:Blocks`); - this.ogService.setManualOgImage('recent-blocks.jpg'); - } - if( this.stateService.network==='liquid'||this.stateService.network==='liquidtestnet' ) { - this.seoService.setDescription($localize`:@@meta.description.liquid.blocks:See the most recent Liquid${seoDescriptionNetwork(this.stateService.network)} blocks along with basic stats such as block height, block size, and more.`); - } else { - this.seoService.setDescription($localize`:@@meta.description.bitcoin.blocks:See the most recent Bitcoin${seoDescriptionNetwork(this.stateService.network)} blocks along with basic stats such as block height, block reward, block size, and more.`); - } - - + this.blocks$ = combineLatest([ this.fromHeightSubject.pipe( filter(fromBlockHeight => fromBlockHeight !== this.lastBlockHeightFetched), diff --git a/frontend/src/app/components/clock/clock.component.ts b/frontend/src/app/components/clock/clock.component.ts index f3f778e89..94ff3e810 100644 --- a/frontend/src/app/components/clock/clock.component.ts +++ b/frontend/src/app/components/clock/clock.component.ts @@ -33,7 +33,6 @@ export class ClockComponent implements OnInit { gradientColors = { '': ['#9339f4', '#105fb0'], - bisq: ['#9339f4', '#105fb0'], liquid: ['#116761', '#183550'], 'liquidtestnet': ['#494a4a', '#272e46'], testnet: ['#1d486f', '#183550'], diff --git a/frontend/src/app/components/eight-blocks/eight-blocks.component.html b/frontend/src/app/components/eight-blocks/eight-blocks.component.html index 59390c953..414a693d3 100644 --- a/frontend/src/app/components/eight-blocks/eight-blocks.component.html +++ b/frontend/src/app/components/eight-blocks/eight-blocks.component.html @@ -12,6 +12,7 @@ [animationDuration]="animationDuration" [animationOffset]="animationOffset" [disableSpinner]="true" + [relativeTime]="blockInfo[i]?.timestamp" (txClickEvent)="onTxClick($event)" >
diff --git a/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.ts b/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.ts index d7a63710f..ca5b3f452 100644 --- a/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.ts +++ b/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.ts @@ -1,6 +1,6 @@ import { HostListener, OnChanges, OnDestroy } from '@angular/core'; import { Component, Input, OnInit, ChangeDetectionStrategy } from '@angular/core'; -import { TransactionStripped } from '../../interfaces/websocket.interface'; +import { TransactionStripped } from '../../interfaces/node-api.interface'; import { StateService } from '../../services/state.service'; import { VbytesPipe } from '../../shared/pipes/bytes-pipe/vbytes.pipe'; import { selectPowerOfTen } from '../../bitcoin.utils'; diff --git a/frontend/src/app/components/fiat-selector/fiat-selector.component.ts b/frontend/src/app/components/fiat-selector/fiat-selector.component.ts index f2538fec9..732c6e862 100644 --- a/frontend/src/app/components/fiat-selector/fiat-selector.component.ts +++ b/frontend/src/app/components/fiat-selector/fiat-selector.component.ts @@ -13,10 +13,10 @@ import { StateService } from '../../services/state.service'; export class FiatSelectorComponent implements OnInit { fiatForm: UntypedFormGroup; currencies = Object.entries(fiatCurrencies).sort((a: any, b: any) => { - if (a[1].name < b[1].name) { + if (a[1].code < b[1].code) { return -1; } - if (a[1].name > b[1].name) { + if (a[1].code > b[1].code) { return 1; } return 0; diff --git a/frontend/src/app/components/footer/footer.component.html b/frontend/src/app/components/footer/footer.component.html index f89e780ff..a0967b7cb 100644 --- a/frontend/src/app/components/footer/footer.component.html +++ b/frontend/src/app/components/footer/footer.component.html @@ -2,7 +2,7 @@
- Incoming transactions  + Incoming Transactions   Backend is synchronizing ({{ mempoolLoadingStatus$ | async }}%) diff --git a/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.ts b/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.ts index c69fce2d1..d938baf15 100644 --- a/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.ts +++ b/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.ts @@ -72,6 +72,7 @@ export class HashrateChartPoolsComponent implements OnInit { let firstRun = true; this.seoService.setTitle($localize`:@@mining.pools-historical-dominance:Pools Historical Dominance`); + this.seoService.setDescription($localize`:@@meta.descriptions.bitcoin.graphs.hashrate-pools:See Bitcoin mining pool dominance visualized over time: see how top mining pools' share of total hashrate has fluctuated over time.`); this.miningWindowPreference = this.miningService.getDefaultTimespan('6m'); this.radioGroupForm = this.formBuilder.group({ dateSpan: this.miningWindowPreference }); this.radioGroupForm.controls.dateSpan.setValue(this.miningWindowPreference); diff --git a/frontend/src/app/components/liquid-master-page/liquid-master-page.component.html b/frontend/src/app/components/liquid-master-page/liquid-master-page.component.html index dd07645bf..d1d474074 100644 --- a/frontend/src/app/components/liquid-master-page/liquid-master-page.component.html +++ b/frontend/src/app/components/liquid-master-page/liquid-master-page.component.html @@ -44,7 +44,7 @@ -
Date Expires in Expired sinceIs DustDust
-
Yes
-
No
+ @if (utxo.isDust) { + ✔ + } @else { + ➖ + }
Status Full RBF - RBF + RBF RBF Mined
AuditAudit - Coinbase + Coinbase Expected in Block Seen in Mempool - Not seen in Mempool - Added + Not seen in Mempool + Added + Prioritized Conflict