mirror of
https://github.com/mempool/mempool.git
synced 2025-03-13 11:36:07 +01:00
Liquid: improve recent pegs pagination data query
This commit is contained in:
parent
33ac4056d8
commit
99aa6c9ed3
8 changed files with 74 additions and 90 deletions
|
@ -412,10 +412,10 @@ class ElementsParser {
|
|||
return rows[0];
|
||||
}
|
||||
|
||||
// Get recent pegouts from the federation (3 months old)
|
||||
public async $getRecentPegouts(): Promise<any> {
|
||||
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<any> {
|
||||
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<any> {
|
||||
const [rows] = await DB.query(`SELECT COUNT(*) AS pegs_count FROM elements_pegs;`);
|
||||
return rows[0];
|
||||
}
|
||||
}
|
||||
|
||||
export default new ElementsParser();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<th class="output text-left" *ngIf="!widget" i18n="liquid.fund-redemption-tx">Fund / Redemption Tx</th>
|
||||
<th class="address text-left" *ngIf="!widget" i18n="liquid.bitcoin-address">BTC Address</th>
|
||||
</thead>
|
||||
<tbody *ngIf="recentPegs$ | async as pegs; else skeleton" [style]="isLoading ? 'opacity: 0.75' : ''">
|
||||
<tbody *ngIf="recentPegsList$ | async as pegs; else skeleton" [style]="isLoading ? 'opacity: 0.75' : ''">
|
||||
<ng-container *ngIf="widget; else regularRows">
|
||||
<tr *ngFor="let peg of pegs | slice:0:5">
|
||||
<td class="transaction text-left widget">
|
||||
|
@ -39,7 +39,7 @@
|
|||
</tr>
|
||||
</ng-container>
|
||||
<ng-template #regularRows>
|
||||
<tr *ngFor="let peg of pegs | slice:(page - 1) * pageSize:page * pageSize">
|
||||
<tr *ngFor="let peg of pegs;">
|
||||
<td class="transaction text-left">
|
||||
<ng-container *ngIf="peg.amount > 0">
|
||||
<a [routerLink]="['/tx' | relativeUrl, peg.txid]" [fragment]="'vin=' + peg.txindex">
|
||||
|
@ -117,8 +117,8 @@
|
|||
</ng-template>
|
||||
</table>
|
||||
|
||||
<ngb-pagination *ngIf="!widget && recentPegs$ | async as pegs" class="pagination-container float-right mt-2" [class]="isLoading ? 'disabled' : ''"
|
||||
[collectionSize]="pegs.length" [rotate]="true" [maxSize]="maxSize" [pageSize]="15" [(page)]="page"
|
||||
<ngb-pagination *ngIf="!widget && pegsCount$ | async as pegsCount" class="pagination-container float-right mt-2" [class]="isLoading || isPegCountLoading ? 'disabled' : ''"
|
||||
[collectionSize]="pegsCount" [rotate]="true" [maxSize]="maxSize" [pageSize]="15" [(page)]="page"
|
||||
(pageChange)="pageChange(page)" [boundaryLinks]="true" [ellipses]="false">
|
||||
</ngb-pagination>
|
||||
|
||||
|
|
|
@ -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<RecentPeg[]> = of([]);
|
||||
@Input() recentPegOuts$: Observable<RecentPeg[]> = of([]);
|
||||
@Input() recentPegsList$: Observable<RecentPeg[]> = of([]);
|
||||
|
||||
env: Env;
|
||||
isLoading = true;
|
||||
isPegCountLoading = true;
|
||||
page = 1;
|
||||
pageSize = 15;
|
||||
maxSize = window.innerWidth <= 767.98 ? 3 : 5;
|
||||
skeletonLines: number[] = [];
|
||||
auditStatus$: Observable<AuditStatus>;
|
||||
auditUpdated$: Observable<boolean>;
|
||||
federationUtxos$: Observable<FederationUtxo[]>;
|
||||
recentPegs$: Observable<RecentPeg[]>;
|
||||
lastReservesBlockUpdate: number = 0;
|
||||
currentPeg$: Observable<CurrentPegs>;
|
||||
pegsCount$: Observable<number>;
|
||||
startingIndexSubject: BehaviorSubject<number> = 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
<div class="card smaller">
|
||||
<div class="card-body">
|
||||
<app-recent-pegs-stats [pegsVolume$]="pegsVolume$"></app-recent-pegs-stats>
|
||||
<app-recent-pegs-list [recentPegIns$]="recentPegIns$" [recentPegOuts$]="recentPegOuts$"[widget]="true"></app-recent-pegs-list>
|
||||
<app-recent-pegs-list [recentPegsList$]="recentPegsList$" [widget]="true"></app-recent-pegs-list>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -17,9 +17,7 @@ export class ReservesAuditDashboardComponent implements OnInit {
|
|||
auditUpdated$: Observable<boolean>;
|
||||
currentPeg$: Observable<CurrentPegs>;
|
||||
currentReserves$: Observable<CurrentPegs>;
|
||||
federationUtxos$: Observable<FederationUtxo[]>;
|
||||
recentPegIns$: Observable<RecentPeg[]>;
|
||||
recentPegOuts$: Observable<RecentPeg[]>;
|
||||
recentPegsList$: Observable<RecentPeg[]>;
|
||||
pegsVolume$: Observable<PegsVolume[]>;
|
||||
federationAddresses$: Observable<FederationAddress[]>;
|
||||
federationAddressesNumber$: Observable<number>;
|
||||
|
@ -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()
|
||||
);
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<div class="card-text">
|
||||
<div class="fee-text">{{ (+currentPeg.amount) / 100000000 | number: '1.2-2' }} <span>L-BTC</span></div>
|
||||
<span class="fiat">
|
||||
<span>As of block <a [routerLink]="['/block', currentPeg.hash]">{{ currentPeg.lastBlockUpdate }}</a></span>
|
||||
<span><ng-container i18n="shared.as-of-block">As of block</ng-container> <a [routerLink]="['/block', currentPeg.hash]">{{ currentPeg.lastBlockUpdate }}</a></span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -15,7 +15,7 @@
|
|||
<div class="card-text">
|
||||
<div class="fee-text">{{ (+currentReserves.amount) / 100000000 | number: '1.2-2' }} <span style="color: #b86d12;">BTC</span></div>
|
||||
<span class="fiat">
|
||||
<span>As of block <a href="{{ env.MEMPOOL_WEBSITE_URL + '/block/' + currentReserves.hash }}" target="_blank" style="color:#b86d12">{{ currentReserves.lastBlockUpdate }}</a></span>
|
||||
<span><ng-container i18n="shared.as-of-block">As of block</ng-container> <a href="{{ env.MEMPOOL_WEBSITE_URL + '/block/' + currentReserves.hash }}" target="_blank" style="color:#b86d12">{{ currentReserves.lastBlockUpdate }}</a></span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -33,7 +33,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<h5 class="card-title" i18n="dashboard.btc-reserves">BTC Reserves</h5>
|
||||
<h5 class="card-title" i18n="dashboard.btc-holdings">BTC Holdings</h5>
|
||||
<div class="card-text">
|
||||
<div class="skeleton-loader"></div>
|
||||
<div class="skeleton-loader"></div>
|
||||
|
|
|
@ -200,8 +200,12 @@ export class ApiService {
|
|||
return this.httpClient.get<FederationUtxo[]>(this.apiBaseUrl + this.apiBasePath + '/api/v1/liquid/reserves/utxos');
|
||||
}
|
||||
|
||||
recentPegOuts$(): Observable<RecentPeg[]> {
|
||||
return this.httpClient.get<RecentPeg[]>(this.apiBaseUrl + this.apiBasePath + '/api/v1/liquid/pegouts');
|
||||
recentPegsList$(count: number = 0): Observable<RecentPeg[]> {
|
||||
return this.httpClient.get<RecentPeg[]>(this.apiBaseUrl + this.apiBasePath + '/api/v1/liquid/pegs/list/' + count);
|
||||
}
|
||||
|
||||
pegsCount$(): Observable<any> {
|
||||
return this.httpClient.get<number>(this.apiBaseUrl + this.apiBasePath + '/api/v1/liquid/pegs/count');
|
||||
}
|
||||
|
||||
federationAddressesNumber$(): Observable<any> {
|
||||
|
|
Loading…
Add table
Reference in a new issue