Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
284 changes: 284 additions & 0 deletions src/analytics/dto/bridge-performance-metric.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,284 @@
import { IsOptional, IsString, IsEnum, IsDateString, IsInt, Min } from 'class-validator';
import { Type } from 'class-transformer';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';

/**
* Time interval type
*/
export type TimeInterval = 'hourly' | 'daily' | 'weekly' | 'monthly';

/**
* Time interval enum for validation
*/
export enum TimeIntervalEnum {
HOURLY = 'hourly',
DAILY = 'daily',
WEEKLY = 'weekly',
MONTHLY = 'monthly',
}

/**
* DTO for querying historical performance metrics
*/
export class BridgePerformanceMetricQueryDto {
@ApiPropertyOptional({ description: 'Filter by bridge name' })
@IsOptional()
@IsString()
bridgeName?: string;

@ApiPropertyOptional({ description: 'Filter by source chain' })
@IsOptional()
@IsString()
sourceChain?: string;

@ApiPropertyOptional({ description: 'Filter by destination chain' })
@IsOptional()
@IsString()
destinationChain?: string;

@ApiPropertyOptional({ description: 'Filter by token' })
@IsOptional()
@IsString()
token?: string;

@ApiPropertyOptional({
description: 'Time interval for aggregation',
enum: TimeIntervalEnum,
default: TimeIntervalEnum.DAILY,
})
@IsOptional()
@IsEnum(TimeIntervalEnum)
timeInterval?: TimeInterval = TimeIntervalEnum.DAILY;

@ApiPropertyOptional({ description: 'Start date for time range (ISO 8601)' })
@IsOptional()
@IsDateString()
startDate?: string;

@ApiPropertyOptional({ description: 'End date for time range (ISO 8601)' })
@IsOptional()
@IsDateString()
endDate?: string;

@ApiPropertyOptional({ description: 'Page number for pagination', default: 1 })
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(1)
page?: number = 1;

@ApiPropertyOptional({ description: 'Items per page', default: 50 })
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(1)
limit?: number = 50;
}

/**
* DTO for a single performance metric data point
*/
export class BridgePerformanceMetricDto {
@ApiProperty({ description: 'Bridge name' })
bridgeName: string;

@ApiProperty({ description: 'Source chain' })
sourceChain: string;

@ApiProperty({ description: 'Destination chain' })
destinationChain: string;

@ApiPropertyOptional({ description: 'Token symbol' })
token?: string;

@ApiProperty({ description: 'Time interval', enum: TimeIntervalEnum })
timeInterval: TimeInterval;

@ApiProperty({ description: 'Timestamp for this metric period' })
timestamp: Date;

@ApiProperty({ description: 'Total transfers in this period' })
totalTransfers: number;

@ApiProperty({ description: 'Successful transfers' })
successfulTransfers: number;

@ApiProperty({ description: 'Failed transfers' })
failedTransfers: number;

@ApiProperty({ description: 'Success rate percentage' })
successRate: number;

@ApiProperty({ description: 'Failure rate percentage' })
failureRate: number;

@ApiPropertyOptional({ description: 'Average settlement time in milliseconds' })
averageSettlementTimeMs?: number;

@ApiPropertyOptional({ description: 'Minimum settlement time' })
minSettlementTimeMs?: number;

@ApiPropertyOptional({ description: 'Maximum settlement time' })
maxSettlementTimeMs?: number;

@ApiPropertyOptional({ description: 'Average fee amount' })
averageFee?: number;

@ApiPropertyOptional({ description: 'Minimum fee' })
minFee?: number;

@ApiPropertyOptional({ description: 'Maximum fee' })
maxFee?: number;

@ApiPropertyOptional({ description: 'Average slippage percentage' })
averageSlippagePercent?: number;

@ApiPropertyOptional({ description: 'Minimum slippage' })
minSlippagePercent?: number;

@ApiPropertyOptional({ description: 'Maximum slippage' })
maxSlippagePercent?: number;

@ApiProperty({ description: 'Total volume transferred' })
totalVolume: number;

@ApiProperty({ description: 'Total fees collected' })
totalFees: number;
}

/**
* DTO for paginated performance metrics response
*/
export class BridgePerformanceMetricResponseDto {
@ApiProperty({ description: 'Performance metrics data', type: [BridgePerformanceMetricDto] })
data: BridgePerformanceMetricDto[];

@ApiProperty({ description: 'Total number of records' })
total: number;

@ApiProperty({ description: 'Current page number' })
page: number;

@ApiProperty({ description: 'Items per page' })
limit: number;

@ApiProperty({ description: 'Total number of pages' })
totalPages: number;

@ApiProperty({ description: 'Time interval used' })
timeInterval: TimeInterval;

@ApiProperty({ description: 'Response generation timestamp' })
generatedAt: Date;
}

/**
* DTO for historical trends data
*/
export class HistoricalTrendsDto {
@ApiProperty({ description: 'Bridge name' })
bridgeName: string;

@ApiProperty({ description: 'Source chain' })
sourceChain: string;

@ApiProperty({ description: 'Destination chain' })
destinationChain: string;

@ApiPropertyOptional({ description: 'Token symbol' })
token?: string;

@ApiProperty({ description: 'Time interval', enum: TimeIntervalEnum })
timeInterval: TimeInterval;

@ApiProperty({ description: 'Trend data points', type: [BridgePerformanceMetricDto] })
trends: BridgePerformanceMetricDto[];

@ApiProperty({ description: 'Response generation timestamp' })
generatedAt: Date;
}

/**
* DTO for performance comparison between bridges
*/
export class BridgePerformanceComparisonDto {
@ApiProperty({ description: 'Bridge name' })
bridgeName: string;

@ApiProperty({ description: 'Source chain' })
sourceChain: string;

@ApiProperty({ description: 'Destination chain' })
destinationChain: string;

@ApiProperty({ description: 'Time interval', enum: TimeIntervalEnum })
timeInterval: TimeInterval;

@ApiProperty({ description: 'Number of data points' })
dataPoints: number;

@ApiProperty({ description: 'Average success rate over period' })
avgSuccessRate: number;

@ApiProperty({ description: 'Average settlement time over period' })
avgSettlementTimeMs: number;

@ApiProperty({ description: 'Average fee over period' })
avgFee: number;

@ApiProperty({ description: 'Average slippage over period' })
avgSlippagePercent: number;

@ApiProperty({ description: 'Total volume over period' })
totalVolume: number;

@ApiProperty({ description: 'Total transfers over period' })
totalTransfers: number;

@ApiProperty({ description: 'Trend direction (improving/declining/stable)' })
trendDirection: 'improving' | 'declining' | 'stable';
}

/**
* DTO for performance comparison response
*/
export class BridgePerformanceComparisonResponseDto {
@ApiProperty({ description: 'Comparison data', type: [BridgePerformanceComparisonDto] })
comparisons: BridgePerformanceComparisonDto[];

@ApiProperty({ description: 'Time interval used' })
timeInterval: TimeInterval;

@ApiProperty({ description: 'Start date of comparison period' })
startDate: Date;

@ApiProperty({ description: 'End date of comparison period' })
endDate: Date;

@ApiProperty({ description: 'Response generation timestamp' })
generatedAt: Date;
}

/**
* DTO for aggregation trigger request
*/
export class TriggerAggregationDto {
@ApiPropertyOptional({
description: 'Time interval to aggregate',
enum: TimeInterval,
default: TimeInterval.DAILY,
})
@IsOptional()
@IsEnum(TimeInterval)
timeInterval?: TimeInterval = TimeInterval.DAILY;

@ApiPropertyOptional({ description: 'Date to aggregate (defaults to previous period)' })
@IsOptional()
@IsDateString()
date?: string;

@ApiPropertyOptional({ description: 'Specific bridge to aggregate' })
@IsOptional()
@IsString()
bridgeName?: string;
}
109 changes: 109 additions & 0 deletions src/analytics/entities/bridge-performance-metric.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
Index,
} from 'typeorm';

/**
* Time interval for historical metrics aggregation
*/
export type TimeInterval = 'hourly' | 'daily' | 'weekly' | 'monthly';

/**
* BridgePerformanceMetric Entity
*
* Stores historical performance metrics for bridge routes over time.
* Supports multiple time intervals for flexible analysis.
*/
@Entity('bridge_performance_metrics')
@Index(['bridgeName', 'sourceChain', 'destinationChain', 'timeInterval', 'timestamp'])
@Index(['timeInterval', 'timestamp'])
export class BridgePerformanceMetric {
@PrimaryGeneratedColumn('uuid')
id: string;

@Column({ name: 'bridge_name' })
bridgeName: string;

@Column({ name: 'source_chain' })
sourceChain: string;

@Column({ name: 'destination_chain' })
destinationChain: string;

@Column({ name: 'token', nullable: true })
token: string | null;

@Column({
name: 'time_interval',
type: 'enum',
enum: ['hourly', 'daily', 'weekly', 'monthly'],
})
timeInterval: TimeInterval;

@Column({ name: 'total_transfers', type: 'int', default: 0 })
totalTransfers: number;

@Column({ name: 'successful_transfers', type: 'int', default: 0 })
successfulTransfers: number;

@Column({ name: 'failed_transfers', type: 'int', default: 0 })
failedTransfers: number;

@Column({ name: 'average_settlement_time_ms', type: 'bigint', nullable: true })
averageSettlementTimeMs: number | null;

@Column({ name: 'min_settlement_time_ms', type: 'bigint', nullable: true })
minSettlementTimeMs: number | null;

@Column({ name: 'max_settlement_time_ms', type: 'bigint', nullable: true })
maxSettlementTimeMs: number | null;

@Column({ name: 'average_fee', type: 'decimal', precision: 30, scale: 10, nullable: true })
averageFee: number | null;

@Column({ name: 'min_fee', type: 'decimal', precision: 30, scale: 10, nullable: true })
minFee: number | null;

@Column({ name: 'max_fee', type: 'decimal', precision: 30, scale: 10, nullable: true })
maxFee: number | null;

@Column({ name: 'average_slippage_percent', type: 'decimal', precision: 10, scale: 4, nullable: true })
averageSlippagePercent: number | null;

@Column({ name: 'min_slippage_percent', type: 'decimal', precision: 10, scale: 4, nullable: true })
minSlippagePercent: number | null;

@Column({ name: 'max_slippage_percent', type: 'decimal', precision: 10, scale: 4, nullable: true })
maxSlippagePercent: number | null;

@Column({ name: 'total_volume', type: 'decimal', precision: 30, scale: 10, default: 0 })
totalVolume: number;

@Column({ name: 'total_fees', type: 'decimal', precision: 30, scale: 10, default: 0 })
totalFees: number;

@Column({ type: 'timestamptz' })
timestamp: Date;

@CreateDateColumn({ name: 'created_at' })
createdAt: Date;

/**
* Computed success rate percentage
*/
get successRate(): number {
if (this.totalTransfers === 0) return 0;
return (this.successfulTransfers / this.totalTransfers) * 100;
}

/**
* Computed failure rate percentage
*/
get failureRate(): number {
if (this.totalTransfers === 0) return 0;
return (this.failedTransfers / this.totalTransfers) * 100;
}
}
Loading
Loading