diff --git a/backend/src/api/liquid/elements-parser.ts b/backend/src/api/liquid/elements-parser.ts index 88d2e1332..101a03aca 100644 --- a/backend/src/api/liquid/elements-parser.ts +++ b/backend/src/api/liquid/elements-parser.ts @@ -412,10 +412,10 @@ class ElementsParser { 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; } @@ -428,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 04af59c85..01d91e3b0 100644 --- a/backend/src/api/liquid/liquid.routes.ts +++ b/backend/src/api/liquid/liquid.routes.ts @@ -17,10 +17,11 @@ 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/total', this.$getFederationAddressesNumber) .get(config.MEMPOOL.API_URL_PREFIX + 'liquid/reserves/utxos', this.$getFederationUtxos) @@ -178,13 +179,13 @@ class LiquidRoutes { } } - 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/frontend/src/app/components/liquid-reserves-audit/recent-pegs-list/recent-pegs-list.component.html b/frontend/src/app/components/liquid-reserves-audit/recent-pegs-list/recent-pegs-list.component.html index e5547fd3f..2b0c49bd2 100644 --- a/frontend/src/app/components/liquid-reserves-audit/recent-pegs-list/recent-pegs-list.component.html +++ b/frontend/src/app/components/liquid-reserves-audit/recent-pegs-list/recent-pegs-list.component.html @@ -15,7 +15,7 @@ Fund / Redemption Tx BTC Address - + @@ -39,7 +39,7 @@ - + @@ -117,8 +117,8 @@ - diff --git a/frontend/src/app/components/liquid-reserves-audit/recent-pegs-list/recent-pegs-list.component.ts b/frontend/src/app/components/liquid-reserves-audit/recent-pegs-list/recent-pegs-list.component.ts index d839d891f..9df119cfc 100644 --- a/frontend/src/app/components/liquid-reserves-audit/recent-pegs-list/recent-pegs-list.component.ts +++ b/frontend/src/app/components/liquid-reserves-audit/recent-pegs-list/recent-pegs-list.component.ts @@ -1,9 +1,9 @@ import { Component, OnInit, ChangeDetectionStrategy, Input } from '@angular/core'; -import { Observable, Subject, combineLatest, of, timer } from 'rxjs'; +import { BehaviorSubject, Observable, Subject, combineLatest, of, timer } from 'rxjs'; import { delayWhen, filter, map, share, shareReplay, switchMap, takeUntil, tap, throttleTime } from 'rxjs/operators'; import { ApiService } from '../../../services/api.service'; import { Env, StateService } from '../../../services/state.service'; -import { AuditStatus, CurrentPegs, FederationUtxo, RecentPeg } from '../../../interfaces/node-api.interface'; +import { AuditStatus, CurrentPegs, RecentPeg } from '../../../interfaces/node-api.interface'; import { WebsocketService } from '../../../services/websocket.service'; import { SeoService } from '../../../services/seo.service'; @@ -15,21 +15,22 @@ import { SeoService } from '../../../services/seo.service'; }) export class RecentPegsListComponent implements OnInit { @Input() widget: boolean = false; - @Input() recentPegIns$: Observable = of([]); - @Input() recentPegOuts$: Observable = of([]); + @Input() recentPegsList$: Observable = of([]); env: Env; isLoading = true; + isPegCountLoading = true; page = 1; pageSize = 15; maxSize = window.innerWidth <= 767.98 ? 3 : 5; skeletonLines: number[] = []; auditStatus$: Observable; auditUpdated$: Observable; - federationUtxos$: Observable; - recentPegs$: Observable; lastReservesBlockUpdate: number = 0; currentPeg$: Observable; + pegsCount$: Observable; + startingIndexSubject: BehaviorSubject = new BehaviorSubject(0); + currentIndex: number = 0; lastPegBlockUpdate: number = 0; lastPegAmount: string = ''; isLoad: boolean = true; @@ -93,53 +94,36 @@ export class RecentPegsListComponent implements OnInit { share() ); - this.federationUtxos$ = this.auditUpdated$.pipe( + this.pegsCount$ = this.auditUpdated$.pipe( filter(auditUpdated => auditUpdated === true), - throttleTime(40000), - switchMap(_ => this.apiService.federationUtxos$()), + tap(() => this.isPegCountLoading = true), + switchMap(_ => this.apiService.pegsCount$()), + map((data) => data.pegs_count), + tap(() => this.isPegCountLoading = false), share() ); - this.recentPegIns$ = this.federationUtxos$.pipe( - map(federationUtxos => federationUtxos.filter(utxo => utxo.pegtxid).map(utxo => { - return { - txid: utxo.pegtxid, - txindex: utxo.pegindex, - amount: utxo.amount, - bitcoinaddress: utxo.bitcoinaddress, - bitcointxid: utxo.txid, - bitcoinindex: utxo.txindex, - blocktime: utxo.pegblocktime, - } - })), - share() - ); - - this.recentPegOuts$ = this.auditUpdated$.pipe( - filter(auditUpdated => auditUpdated === true), - throttleTime(40000), - switchMap(_ => this.apiService.recentPegOuts$()), + this.recentPegsList$ = combineLatest([ + this.auditStatus$, + this.auditUpdated$, + this.startingIndexSubject + ]).pipe( + filter(([auditStatus, auditUpdated, startingIndex]) => { + const auditStatusCheck = auditStatus.isAuditSynced === true; + const auditUpdatedCheck = auditUpdated === true; + const startingIndexCheck = startingIndex !== this.currentIndex; + return auditStatusCheck && (auditUpdatedCheck || startingIndexCheck); + }), + tap(([_, __, startingIndex]) => { + this.currentIndex = startingIndex; + this.isLoading = true; + }), + switchMap(([_, __, startingIndex]) => this.apiService.recentPegsList$(startingIndex)), + tap(() => this.isLoading = false), share() ); } - - this.recentPegs$ = combineLatest([ - this.recentPegIns$, - this.recentPegOuts$ - ]).pipe( - map(([recentPegIns, recentPegOuts]) => { - return [ - ...recentPegIns, - ...recentPegOuts - ].sort((a, b) => { - return b.blocktime - a.blocktime; - }); - }), - filter(recentPegs => recentPegs.length > 0), - tap(_ => this.isLoading = false), - share() - ); } ngOnDestroy(): void { @@ -148,6 +132,7 @@ export class RecentPegsListComponent implements OnInit { } pageChange(page: number): void { + this.startingIndexSubject.next((page - 1) * 15); this.page = page; } diff --git a/frontend/src/app/components/liquid-reserves-audit/reserves-audit-dashboard/reserves-audit-dashboard.component.html b/frontend/src/app/components/liquid-reserves-audit/reserves-audit-dashboard/reserves-audit-dashboard.component.html index 39bade669..47457324b 100644 --- a/frontend/src/app/components/liquid-reserves-audit/reserves-audit-dashboard/reserves-audit-dashboard.component.html +++ b/frontend/src/app/components/liquid-reserves-audit/reserves-audit-dashboard/reserves-audit-dashboard.component.html @@ -26,7 +26,7 @@
- +
diff --git a/frontend/src/app/components/liquid-reserves-audit/reserves-audit-dashboard/reserves-audit-dashboard.component.ts b/frontend/src/app/components/liquid-reserves-audit/reserves-audit-dashboard/reserves-audit-dashboard.component.ts index 7ffaeb05a..78cb607d4 100644 --- a/frontend/src/app/components/liquid-reserves-audit/reserves-audit-dashboard/reserves-audit-dashboard.component.ts +++ b/frontend/src/app/components/liquid-reserves-audit/reserves-audit-dashboard/reserves-audit-dashboard.component.ts @@ -17,9 +17,7 @@ export class ReservesAuditDashboardComponent implements OnInit { auditUpdated$: Observable; currentPeg$: Observable; currentReserves$: Observable; - federationUtxos$: Observable; - recentPegIns$: Observable; - recentPegOuts$: Observable; + recentPegsList$: Observable; pegsVolume$: Observable; federationAddresses$: Observable; federationAddressesNumber$: Observable; @@ -101,32 +99,10 @@ export class ReservesAuditDashboardComponent implements OnInit { share() ); - this.federationUtxos$ = this.auditUpdated$.pipe( + this.recentPegsList$ = this.auditUpdated$.pipe( filter(auditUpdated => auditUpdated === true), throttleTime(40000), - switchMap(_ => this.apiService.federationUtxos$()), - share() - ); - - this.recentPegIns$ = this.federationUtxos$.pipe( - map(federationUtxos => federationUtxos.filter(utxo => utxo.pegtxid).map(utxo => { - return { - txid: utxo.pegtxid, - txindex: utxo.pegindex, - amount: utxo.amount, - bitcoinaddress: utxo.bitcoinaddress, - bitcointxid: utxo.txid, - bitcoinindex: utxo.txindex, - blocktime: utxo.pegblocktime, - } - })), - share() - ); - - this.recentPegOuts$ = this.auditUpdated$.pipe( - filter(auditUpdated => auditUpdated === true), - throttleTime(40000), - switchMap(_ => this.apiService.recentPegOuts$()), + switchMap(_ => this.apiService.recentPegsList$()), share() ); diff --git a/frontend/src/app/components/liquid-reserves-audit/reserves-supply-stats/reserves-supply-stats.component.html b/frontend/src/app/components/liquid-reserves-audit/reserves-supply-stats/reserves-supply-stats.component.html index 5352f67e8..5a76f3820 100644 --- a/frontend/src/app/components/liquid-reserves-audit/reserves-supply-stats/reserves-supply-stats.component.html +++ b/frontend/src/app/components/liquid-reserves-audit/reserves-supply-stats/reserves-supply-stats.component.html @@ -6,7 +6,7 @@
@@ -15,7 +15,7 @@
{{ (+currentReserves.amount) / 100000000 | number: '1.2-2' }} BTC
- As of block {{ currentReserves.lastBlockUpdate }} + As of block {{ currentReserves.lastBlockUpdate }}
@@ -33,7 +33,7 @@
-
BTC Reserves
+
BTC Holdings
diff --git a/frontend/src/app/services/api.service.ts b/frontend/src/app/services/api.service.ts index 7eb1f0b96..7bd41c8a0 100644 --- a/frontend/src/app/services/api.service.ts +++ b/frontend/src/app/services/api.service.ts @@ -200,8 +200,12 @@ export class ApiService { return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/liquid/reserves/utxos'); } - recentPegOuts$(): Observable { - return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/liquid/pegouts'); + recentPegsList$(count: number = 0): Observable { + return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/liquid/pegs/list/' + count); + } + + pegsCount$(): Observable { + return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/liquid/pegs/count'); } federationAddressesNumber$(): Observable {