diff --git a/backend/src/api/liquid/elements-parser.ts b/backend/src/api/liquid/elements-parser.ts index 8c3d519ab..101a03aca 100644 --- a/backend/src/api/liquid/elements-parser.ts +++ b/backend/src/api/liquid/elements-parser.ts @@ -398,39 +398,24 @@ class ElementsParser { return rows; } - // Get all of the federation addresses one month ago, most balances first - public async $getFederationAddressesOneMonthAgo(): Promise { - const query = ` - SELECT COUNT(*) AS addresses_count_one_month FROM ( - SELECT bitcoinaddress, SUM(amount) AS balance - FROM federation_txos - WHERE - (blocktime < UNIX_TIMESTAMP(TIMESTAMPADD(DAY, -30, CURRENT_TIMESTAMP()))) - AND - ((unspent = 1) OR (unspent = 0 AND lasttimeupdate > UNIX_TIMESTAMP(TIMESTAMPADD(DAY, -30, CURRENT_TIMESTAMP())))) - GROUP BY bitcoinaddress - ) AS result;`; + // Get the total number of federation addresses + public async $getFederationAddressesNumber(): Promise { + const query = `SELECT COUNT(DISTINCT bitcoinaddress) AS address_count FROM federation_txos WHERE unspent = 1;`; const [rows] = await DB.query(query); return rows[0]; } - // Get all of the UTXOs held by the federation one month ago, most recent first - public async $getFederationUtxosOneMonthAgo(): Promise { - const query = ` - SELECT COUNT(*) AS utxos_count_one_month FROM federation_txos - WHERE - (blocktime < UNIX_TIMESTAMP(TIMESTAMPADD(DAY, -30, CURRENT_TIMESTAMP()))) - AND - ((unspent = 1) OR (unspent = 0 AND lasttimeupdate > UNIX_TIMESTAMP(TIMESTAMPADD(DAY, -30, CURRENT_TIMESTAMP())))) - ORDER BY blocktime DESC;`; + // Get the total number of federation utxos + public async $getFederationUtxosNumber(): Promise { + const query = `SELECT COUNT(*) AS utxo_count FROM federation_txos WHERE unspent = 1;`; const [rows] = await DB.query(query); return rows[0]; } - // Get recent pegouts from the federation (3 months old) - public async $getRecentPegouts(): Promise { - const query = `SELECT txid, txindex, amount, bitcoinaddress, bitcointxid, bitcoinindex, datetime AS blocktime FROM elements_pegs WHERE amount < 0 AND datetime > UNIX_TIMESTAMP(TIMESTAMPADD(DAY, -90, CURRENT_TIMESTAMP())) ORDER BY blocktime;`; - const [rows] = await DB.query(query); + // Get recent pegs in / out + public async $getPegsList(count: number = 0): Promise { + const query = `SELECT txid, txindex, amount, bitcoinaddress, bitcointxid, bitcoinindex, datetime AS blocktime FROM elements_pegs ORDER BY block DESC LIMIT 15 OFFSET ?;`; + const [rows] = await DB.query(query, [count]); return rows; } @@ -443,6 +428,12 @@ class ElementsParser { pegOutQuery[0][0] ]; } + + // Get the total pegs number + public async $getPegsCount(): Promise { + const [rows] = await DB.query(`SELECT COUNT(*) AS pegs_count FROM elements_pegs;`); + return rows[0]; + } } export default new ElementsParser(); diff --git a/backend/src/api/liquid/liquid.routes.ts b/backend/src/api/liquid/liquid.routes.ts index b87348d1b..01d91e3b0 100644 --- a/backend/src/api/liquid/liquid.routes.ts +++ b/backend/src/api/liquid/liquid.routes.ts @@ -17,14 +17,15 @@ class LiquidRoutes { app .get(config.MEMPOOL.API_URL_PREFIX + 'liquid/pegs', this.$getElementsPegs) .get(config.MEMPOOL.API_URL_PREFIX + 'liquid/pegs/month', this.$getElementsPegsByMonth) + .get(config.MEMPOOL.API_URL_PREFIX + 'liquid/pegs/list/:count', this.$getPegsList) .get(config.MEMPOOL.API_URL_PREFIX + 'liquid/pegs/volume', this.$getPegsVolumeDaily) + .get(config.MEMPOOL.API_URL_PREFIX + 'liquid/pegs/count', this.$getPegsCount) .get(config.MEMPOOL.API_URL_PREFIX + 'liquid/reserves', this.$getFederationReserves) .get(config.MEMPOOL.API_URL_PREFIX + 'liquid/reserves/month', this.$getFederationReservesByMonth) - .get(config.MEMPOOL.API_URL_PREFIX + 'liquid/pegouts', this.$getPegOuts) .get(config.MEMPOOL.API_URL_PREFIX + 'liquid/reserves/addresses', this.$getFederationAddresses) - .get(config.MEMPOOL.API_URL_PREFIX + 'liquid/reserves/addresses/previous-month', this.$getFederationAddressesOneMonthAgo) + .get(config.MEMPOOL.API_URL_PREFIX + 'liquid/reserves/addresses/total', this.$getFederationAddressesNumber) .get(config.MEMPOOL.API_URL_PREFIX + 'liquid/reserves/utxos', this.$getFederationUtxos) - .get(config.MEMPOOL.API_URL_PREFIX + 'liquid/reserves/utxos/previous-month', this.$getFederationUtxosOneMonthAgo) + .get(config.MEMPOOL.API_URL_PREFIX + 'liquid/reserves/utxos/total', this.$getFederationUtxosNumber) .get(config.MEMPOOL.API_URL_PREFIX + 'liquid/reserves/status', this.$getFederationAuditStatus) ; } @@ -142,12 +143,12 @@ class LiquidRoutes { } } - private async $getFederationAddressesOneMonthAgo(req: Request, res: Response) { + private async $getFederationAddressesNumber(req: Request, res: Response) { try { - const federationAddresses = await elementsParser.$getFederationAddressesOneMonthAgo(); + const federationAddresses = await elementsParser.$getFederationAddressesNumber(); res.header('Pragma', 'public'); res.header('Cache-control', 'public'); - res.setHeader('Expires', new Date(Date.now() + 1000 * 60 * 60 * 24).toUTCString()); + res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString()); res.json(federationAddresses); } catch (e) { res.status(500).send(e instanceof Error ? e.message : e); @@ -166,25 +167,25 @@ class LiquidRoutes { } } - private async $getFederationUtxosOneMonthAgo(req: Request, res: Response) { + private async $getFederationUtxosNumber(req: Request, res: Response) { try { - const federationUtxos = await elementsParser.$getFederationUtxosOneMonthAgo(); + const federationUtxos = await elementsParser.$getFederationUtxosNumber(); res.header('Pragma', 'public'); res.header('Cache-control', 'public'); - res.setHeader('Expires', new Date(Date.now() + 1000 * 60 * 60 * 24).toUTCString()); + res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString()); res.json(federationUtxos); } catch (e) { res.status(500).send(e instanceof Error ? e.message : e); } } - private async $getPegOuts(req: Request, res: Response) { + private async $getPegsList(req: Request, res: Response) { try { - const recentPegOuts = await elementsParser.$getRecentPegouts(); + const recentPegs = await elementsParser.$getPegsList(parseInt(req.params?.count)); res.header('Pragma', 'public'); res.header('Cache-control', 'public'); res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString()); - res.json(recentPegOuts); + res.json(recentPegs); } catch (e) { res.status(500).send(e instanceof Error ? e.message : e); } @@ -202,6 +203,18 @@ class LiquidRoutes { } } + private async $getPegsCount(req: Request, res: Response) { + try { + const pegsCount = await elementsParser.$getPegsCount(); + res.header('Pragma', 'public'); + res.header('Cache-control', 'public'); + res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString()); + res.json(pegsCount); + } catch (e) { + res.status(500).send(e instanceof Error ? e.message : e); + } + } + } export default new LiquidRoutes(); diff --git a/backend/src/api/statistics/statistics-api.ts b/backend/src/api/statistics/statistics-api.ts index 5c6896619..c7c3f37b0 100644 --- a/backend/src/api/statistics/statistics-api.ts +++ b/backend/src/api/statistics/statistics-api.ts @@ -285,7 +285,7 @@ class StatisticsApi { public async $list2H(): Promise { try { - const query = `SELECT *, UNIX_TIMESTAMP(added) as added FROM statistics ORDER BY statistics.added DESC LIMIT 120`; + const query = `SELECT *, UNIX_TIMESTAMP(added) as added FROM statistics WHERE added BETWEEN DATE_SUB(NOW(), INTERVAL 2 HOUR) AND NOW() ORDER BY statistics.added DESC`; const [rows] = await DB.query({ sql: query, timeout: this.queryTimeout }); return this.mapStatisticToOptimizedStatistic(rows as Statistic[]); } catch (e) { @@ -296,7 +296,7 @@ class StatisticsApi { public async $list24H(): Promise { try { - const query = `SELECT *, UNIX_TIMESTAMP(added) as added FROM statistics ORDER BY statistics.added DESC LIMIT 1440`; + const query = `SELECT *, UNIX_TIMESTAMP(added) as added FROM statistics WHERE added BETWEEN DATE_SUB(NOW(), INTERVAL 24 HOUR) AND NOW() ORDER BY statistics.added DESC`; const [rows] = await DB.query({ sql: query, timeout: this.queryTimeout }); return this.mapStatisticToOptimizedStatistic(rows as Statistic[]); } catch (e) { diff --git a/backend/src/api/statistics/statistics.ts b/backend/src/api/statistics/statistics.ts index 494777aad..2926a4b17 100644 --- a/backend/src/api/statistics/statistics.ts +++ b/backend/src/api/statistics/statistics.ts @@ -6,6 +6,7 @@ import statisticsApi from './statistics-api'; class Statistics { protected intervalTimer: NodeJS.Timer | undefined; + protected lastRun: number = 0; protected newStatisticsEntryCallback: ((stats: OptimizedStatistic) => void) | undefined; public setNewStatisticsEntryCallback(fn: (stats: OptimizedStatistic) => void) { @@ -23,15 +24,21 @@ class Statistics { setTimeout(() => { this.runStatistics(); this.intervalTimer = setInterval(() => { - this.runStatistics(); + this.runStatistics(true); }, 1 * 60 * 1000); }, difference); } - private async runStatistics(): Promise { + public async runStatistics(skipIfRecent = false): Promise { if (!memPool.isInSync()) { return; } + + if (skipIfRecent && new Date().getTime() / 1000 - this.lastRun < 30) { + return; + } + + this.lastRun = new Date().getTime() / 1000; const currentMempool = memPool.getMempool(); const txPerSecond = memPool.getTxPerSecond(); const vBytesPerSecond = memPool.getVBytesPerSecond(); diff --git a/backend/src/api/websocket-handler.ts b/backend/src/api/websocket-handler.ts index 3091a09cf..b78389b64 100644 --- a/backend/src/api/websocket-handler.ts +++ b/backend/src/api/websocket-handler.ts @@ -23,6 +23,7 @@ import priceUpdater from '../tasks/price-updater'; import { ApiPrice } from '../repositories/PricesRepository'; import accelerationApi from './services/acceleration'; import mempool from './mempool'; +import statistics from './statistics/statistics'; interface AddressTransactions { mempool: MempoolTransactionExtended[], @@ -723,6 +724,7 @@ class WebsocketHandler { } this.printLogs(); + await statistics.runStatistics(); const _memPool = memPool.getMempool(); @@ -1014,6 +1016,8 @@ class WebsocketHandler { client.send(this.serializeResponse(response)); } }); + + await statistics.runStatistics(); } // takes a dictionary of JSON serialized values diff --git a/frontend/src/app/components/about/about-sponsors.component.html b/frontend/src/app/components/about/about-sponsors.component.html index ae7232682..9471fc78f 100644 --- a/frontend/src/app/components/about/about-sponsors.component.html +++ b/frontend/src/app/components/about/about-sponsors.component.html @@ -1,4 +1,4 @@ -
+

If you're an individual...

Become a Community Sponsor @@ -13,4 +13,4 @@
-
\ No newline at end of file +
diff --git a/frontend/src/app/components/about/about-sponsors.component.scss b/frontend/src/app/components/about/about-sponsors.component.scss index f3e675fd4..7c01bb9a3 100644 --- a/frontend/src/app/components/about/about-sponsors.component.scss +++ b/frontend/src/app/components/about/about-sponsors.component.scss @@ -6,6 +6,11 @@ align-items: center; gap: 20px; margin: 68px auto; + text-align: center; +} + +#become-sponsor-container.account { + margin: 20px auto; } .become-sponsor { diff --git a/frontend/src/app/components/about/about-sponsors.component.ts b/frontend/src/app/components/about/about-sponsors.component.ts index 6d8f4f7de..6a47c3bd4 100644 --- a/frontend/src/app/components/about/about-sponsors.component.ts +++ b/frontend/src/app/components/about/about-sponsors.component.ts @@ -8,6 +8,7 @@ import { EnterpriseService } from '../../services/enterprise.service'; }) export class AboutSponsorsComponent { @Input() host = 'https://mempool.space'; + @Input() context = 'about'; constructor(private enterpriseService: EnterpriseService) { } 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 760cadda4..dd07645bf 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 @@ -78,10 +78,7 @@ - -