Add peg ins/out volume to stats components

This commit is contained in:
natsoni 2024-02-06 17:35:02 +01:00
parent 602c87bea0
commit b561648082
No known key found for this signature in database
GPG Key ID: C65917583181743B
11 changed files with 105 additions and 13 deletions

View File

@ -433,6 +433,16 @@ class ElementsParser {
const [rows] = await DB.query(query);
return rows;
}
// Get all peg in / out from the last month
public async $getPegsVolumeDaily(): Promise<any> {
const pegInQuery = await DB.query(`SELECT SUM(amount) AS volume, COUNT(*) AS number FROM elements_pegs WHERE amount > 0 and datetime > UNIX_TIMESTAMP(TIMESTAMPADD(DAY, -1, CURRENT_TIMESTAMP()));`);
const pegOutQuery = await DB.query(`SELECT SUM(amount) AS volume, COUNT(*) AS number FROM elements_pegs WHERE amount < 0 and datetime > UNIX_TIMESTAMP(TIMESTAMPADD(DAY, -1, CURRENT_TIMESTAMP()));`);
return [
pegInQuery[0][0],
pegOutQuery[0][0]
];
}
}
export default new ElementsParser();

View File

@ -17,6 +17,7 @@ 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/volume', this.$getPegsVolumeDaily)
.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)
@ -189,6 +190,18 @@ class LiquidRoutes {
}
}
private async $getPegsVolumeDaily(req: Request, res: Response) {
try {
const pegsVolume = await elementsParser.$getPegsVolumeDaily();
res.header('Pragma', 'public');
res.header('Cache-control', 'public');
res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString());
res.json(pegsVolume);
} catch (e) {
res.status(500).send(e instanceof Error ? e.message : e);
}
}
}
export default new LiquidRoutes();

View File

@ -18,7 +18,7 @@
</thead>
<tbody *ngIf="recentPegs$ | async as pegs; else skeleton" [style]="isLoading ? 'opacity: 0.75' : ''">
<ng-container *ngIf="widget; else regularRows">
<tr *ngFor="let peg of pegs | slice:0:6">
<tr *ngFor="let peg of pegs | slice:0:5">
<td class="transaction text-left widget">
<ng-container *ngIf="peg.amount > 0">
<a [routerLink]="['/tx' | relativeUrl, peg.txid]" [fragment]="'vin=' + peg.txindex">

View File

@ -47,7 +47,7 @@ export class RecentPegsListComponent implements OnInit {
ngOnInit(): void {
this.isLoading = !this.widget;
this.env = this.stateService.env;
this.skeletonLines = this.widget === true ? [...Array(6).keys()] : [...Array(15).keys()];
this.skeletonLines = this.widget === true ? [...Array(5).keys()] : [...Array(15).keys()];
if (!this.widget) {
this.seoService.setTitle($localize`:@@a8b0889ea1b41888f1e247f2731cc9322198ca04:Recent Peg-In / Out's`);

View File

@ -1,7 +1,47 @@
<div class="fee-estimation-container">
<div class="item">
<a class="title-link" [routerLink]="['/audit/pegs' | relativeUrl]">
<h5 class="card-title"><ng-container i18n="liquid.recent-pegs">Recent Peg-In / Out's</ng-container>&nbsp;<fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true" style="font-size: 13px; color: #4a68b9"></fa-icon></h5>
</a>
<div *ngIf="(pegsVolume$ | async) as pegsVolume; else loadingData">
<div class="fee-estimation-container">
<div class="item">
<a class="title-link" [routerLink]="['/audit/pegs' | relativeUrl]">
<h5 class="card-title"><ng-container i18n="liquid.recent-pegs">Recent Peg-In / Out's</ng-container>&nbsp;<fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true" style="font-size: 13px; color: #4a68b9"></fa-icon></h5>
</a>
</div>
</div>
<div class="fee-estimation-container">
<div class="item">
<div class="card-text">
<div class="fee-text credit" i18n-ngbTooltip="liquid.peg-ins-volume-day" ngbTooltip="24h Peg-In Volume" placement="top">+{{ (+pegsVolume[0].volume) / 100000000 | number: '1.2-2' }} <span i18n="shared.addresses">BTC</span></div>
<div class="fiat">{{ (+pegsVolume[0].number) }} <span i18n="liquid.peg-ins">Peg-Ins</span></div>
</div>
</div>
<div class="item">
<div class="card-text">
<div class="fee-text debit" i18n-ngbTooltip="liquid.peg-out-volume-day" ngbTooltip="24h Peg-Out Volume" placement="top">{{ (+pegsVolume[1].volume) / 100000000 | number: '1.2-2' }} <span i18n="shared.addresses">BTC</span></div>
<div class="fiat">{{ (+pegsVolume[1].number) }} <span i18n="liquid.peg-outs">Peg-Outs</span></div>
</div>
</div>
</div>
</div>
<ng-template #loadingData>
<div class="fee-estimation-container loading-container">
<div class="item">
<a class="title-link" [routerLink]="['/audit/pegs' | relativeUrl]">
<h5 class="card-title"><ng-container i18n="liquid.recent-pegs">Recent Peg-In / Out's</ng-container>&nbsp;<fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true" style="font-size: 13px; color: #4a68b9"></fa-icon></h5>
</a>
</div>
</div>
<div class="fee-estimation-container">
<div class="item">
<div class="card-text">
<div class="skeleton-loader"></div>
<div class="skeleton-loader"></div>
</div>
</div>
<div class="item">
<div class="card-text">
<div class="skeleton-loader"></div>
<div class="skeleton-loader"></div>
</div>
</div>
</div>
</ng-template>

View File

@ -1,7 +1,6 @@
.fee-estimation-container {
display: flex;
justify-content: space-between;
padding-bottom: 1rem;
@media (min-width: 376px) {
flex-direction: row;
}
@ -36,6 +35,7 @@
top: 0px;
}
.fee-text{
border-bottom: 1px solid #ffffff1c;
width: fit-content;
margin: auto;
line-height: 1.45;
@ -69,3 +69,11 @@
text-decoration: none;
color: inherit;
}
.credit {
color: #7CB342;
}
.debit {
color: #D81B60;
}

View File

@ -1,4 +1,7 @@
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { PegsVolume } from '../../../interfaces/node-api.interface';
@Component({
selector: 'app-recent-pegs-stats',
templateUrl: './recent-pegs-stats.component.html',
@ -6,10 +9,11 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class RecentPegsStatsComponent implements OnInit {
@Input() pegsVolume$: Observable<PegsVolume[]>;
constructor() { }
ngOnInit(): void {
}
}

View File

@ -25,7 +25,7 @@
<div class="col">
<div class="card">
<div class="card-body">
<app-recent-pegs-stats></app-recent-pegs-stats>
<app-recent-pegs-stats [pegsVolume$]="pegsVolume$"></app-recent-pegs-stats>
<app-recent-pegs-list [recentPegIns$]="recentPegIns$" [recentPegOuts$]="recentPegOuts$"[widget]="true"></app-recent-pegs-list>
</div>
</div>

View File

@ -4,7 +4,7 @@ import { WebsocketService } from '../../../services/websocket.service';
import { StateService } from '../../../services/state.service';
import { Observable, Subject, combineLatest, delayWhen, filter, interval, map, of, share, shareReplay, startWith, switchMap, takeUntil, tap, throttleTime, timer } from 'rxjs';
import { ApiService } from '../../../services/api.service';
import { AuditStatus, CurrentPegs, FederationAddress, FederationUtxo, RecentPeg } from '../../../interfaces/node-api.interface';
import { AuditStatus, CurrentPegs, FederationAddress, FederationUtxo, PegsVolume, RecentPeg } from '../../../interfaces/node-api.interface';
@Component({
selector: 'app-reserves-audit-dashboard',
@ -20,6 +20,7 @@ export class ReservesAuditDashboardComponent implements OnInit {
federationUtxos$: Observable<FederationUtxo[]>;
recentPegIns$: Observable<RecentPeg[]>;
recentPegOuts$: Observable<RecentPeg[]>;
pegsVolume$: Observable<PegsVolume[]>;
federationAddresses$: Observable<FederationAddress[]>;
federationAddressesOneMonthAgo$: Observable<any>;
liquidPegsMonth$: Observable<any>;
@ -127,6 +128,13 @@ export class ReservesAuditDashboardComponent implements OnInit {
share()
);
this.pegsVolume$ = this.auditUpdated$.pipe(
filter(auditUpdated => auditUpdated === true),
throttleTime(40000),
switchMap(_ => this.apiService.pegsVolume$()),
share()
);
this.federationAddresses$ = this.auditUpdated$.pipe(
filter(auditUpdated => auditUpdated === true),
throttleTime(40000),

View File

@ -82,6 +82,11 @@ export interface CurrentPegs {
hash: string;
}
export interface PegsVolume {
volume: string;
number: number;
}
export interface FederationAddress {
bitcoinaddress: string;
balance: string;

View File

@ -1,7 +1,7 @@
import { Injectable } from '@angular/core';
import { HttpClient, HttpParams, HttpResponse } from '@angular/common/http';
import { CpfpInfo, OptimizedMempoolStats, AddressInformation, LiquidPegs, ITranslators,
PoolStat, BlockExtended, TransactionStripped, RewardStats, AuditScore, BlockSizesAndWeights, RbfTree, BlockAudit, Acceleration, AccelerationHistoryParams, CurrentPegs, AuditStatus, FederationAddress, FederationUtxo, RecentPeg } from '../interfaces/node-api.interface';
PoolStat, BlockExtended, TransactionStripped, RewardStats, AuditScore, BlockSizesAndWeights, RbfTree, BlockAudit, Acceleration, AccelerationHistoryParams, CurrentPegs, AuditStatus, FederationAddress, FederationUtxo, RecentPeg, PegsVolume } from '../interfaces/node-api.interface';
import { BehaviorSubject, Observable, catchError, filter, of, shareReplay, take, tap } from 'rxjs';
import { StateService } from './state.service';
import { IBackendInfo, WebsocketResponse } from '../interfaces/websocket.interface';
@ -182,6 +182,10 @@ export class ApiService {
return this.httpClient.get<CurrentPegs>(this.apiBaseUrl + this.apiBasePath + '/api/v1/liquid/pegs');
}
pegsVolume$(): Observable<PegsVolume[]> {
return this.httpClient.get<PegsVolume[]>(this.apiBaseUrl + this.apiBasePath + '/api/v1/liquid/pegs/volume');
}
listLiquidPegsMonth$(): Observable<LiquidPegs[]> {
return this.httpClient.get<LiquidPegs[]>(this.apiBaseUrl + this.apiBasePath + '/api/v1/liquid/pegs/month');
}