Skip to content
Open
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
544 changes: 363 additions & 181 deletions app/(home)/stats/overview/page.tsx

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion app/(home)/stats/primary-network/validators/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
"use client";
import * as React from "react";
import { useState, useEffect, useMemo } from "react";
import {
Area,
Expand Down
106 changes: 84 additions & 22 deletions app/api/chain-stats/[chainId]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ import { TimeSeriesDataPoint, TimeSeriesMetric, ICMDataPoint, ICMMetric, STATS_C
getTimestampsFromTimeRange, createTimeSeriesMetric, createICMMetric } from "@/types/stats";

interface ChainMetrics {
activeAddresses: TimeSeriesMetric;
activeAddresses: {
daily: TimeSeriesMetric;
weekly: TimeSeriesMetric;
monthly: TimeSeriesMetric;
};
activeSenders: TimeSeriesMetric;
cumulativeAddresses: TimeSeriesMetric;
cumulativeDeployers: TimeSeriesMetric;
Expand Down Expand Up @@ -96,7 +100,57 @@ async function getTimeSeriesData(
}
}

async function getICMData(chainId: string, timeRange: string, startTimestamp?: number, endTimestamp?: number): Promise<ICMDataPoint[]> {
// Separate active addresses fetching with proper time intervals (optimize other metrics as needed)
async function getActiveAddressesData(chainId: string, timeRange: string, interval: 'day' | 'week' | 'month', pageSize: number = 365, fetchAllPages: boolean = false): Promise<TimeSeriesDataPoint[]> {
try {
const { startTimestamp, endTimestamp } = getTimestampsFromTimeRange(timeRange);
let allResults: any[] = [];

const avalanche = new Avalanche({
network: "mainnet"
});

const rlToken = process.env.METRICS_BYPASS_TOKEN || '';
const params: any = {
chainId: chainId,
metric: 'activeAddresses',
startTimestamp,
endTimestamp,
timeInterval: interval,
pageSize,
};

if (rlToken) { params.rltoken = rlToken; }

const result = await avalanche.metrics.chains.getMetrics(params);

for await (const page of result) {
if (!page?.result?.results || !Array.isArray(page.result.results)) {
console.warn(`Invalid page structure for activeAddresses (${interval}) on chain ${chainId}:`, page);
continue;
}

allResults = allResults.concat(page.result.results);

if (!fetchAllPages) {
break;
}
}

return allResults
.sort((a: any, b: any) => b.timestamp - a.timestamp)
.map((result: any) => ({
timestamp: result.timestamp,
value: result.value || 0,
date: new Date(result.timestamp * 1000).toISOString().split('T')[0]
}));
} catch (error) {
console.warn(`Failed to fetch activeAddresses data for chain ${chainId} with interval ${interval}:`, error);
return [];
}
}

async function getICMData(chainId: string, timeRange: string): Promise<ICMDataPoint[]> {
try {
let days: number;

Expand Down Expand Up @@ -243,7 +297,9 @@ export async function GET(
const { pageSize, fetchAllPages } = config;

const [
activeAddressesData,
dailyActiveAddressesData,
weeklyActiveAddressesData,
monthlyActiveAddressesData,
activeSendersData,
cumulativeAddressesData,
cumulativeDeployersData,
Expand All @@ -262,28 +318,34 @@ export async function GET(
feesPaidData,
icmData,
] = await Promise.all([
getTimeSeriesData('activeAddresses', chainId, timeRange, startTimestamp, endTimestamp, pageSize, fetchAllPages),
getTimeSeriesData('activeSenders', chainId, timeRange, startTimestamp, endTimestamp, pageSize, fetchAllPages),
getTimeSeriesData('cumulativeAddresses', chainId, timeRange, startTimestamp, endTimestamp, pageSize, fetchAllPages),
getTimeSeriesData('cumulativeDeployers', chainId, timeRange, startTimestamp, endTimestamp, pageSize, fetchAllPages),
getTimeSeriesData('txCount', chainId, timeRange, startTimestamp, endTimestamp, pageSize, fetchAllPages),
getTimeSeriesData('cumulativeTxCount', chainId, timeRange, startTimestamp, endTimestamp, pageSize, fetchAllPages),
getTimeSeriesData('cumulativeContracts', chainId, timeRange, startTimestamp, endTimestamp, pageSize, fetchAllPages),
getTimeSeriesData('contracts', chainId, timeRange, startTimestamp, endTimestamp, pageSize, fetchAllPages),
getTimeSeriesData('deployers', chainId, timeRange, startTimestamp, endTimestamp, pageSize, fetchAllPages),
getTimeSeriesData('gasUsed', chainId, timeRange, startTimestamp, endTimestamp, pageSize, fetchAllPages),
getTimeSeriesData('avgGps', chainId, timeRange, startTimestamp, endTimestamp, pageSize, fetchAllPages),
getTimeSeriesData('maxGps', chainId, timeRange, startTimestamp, endTimestamp, pageSize, fetchAllPages),
getTimeSeriesData('avgTps', chainId, timeRange, startTimestamp, endTimestamp, pageSize, fetchAllPages),
getTimeSeriesData('maxTps', chainId, timeRange, startTimestamp, endTimestamp, pageSize, fetchAllPages),
getTimeSeriesData('avgGasPrice', chainId, timeRange, startTimestamp, endTimestamp, pageSize, fetchAllPages),
getTimeSeriesData('maxGasPrice', chainId, timeRange, startTimestamp, endTimestamp, pageSize, fetchAllPages),
getTimeSeriesData('feesPaid', chainId, timeRange, startTimestamp, endTimestamp, pageSize, fetchAllPages),
getICMData(chainId, timeRange, startTimestamp, endTimestamp),
getActiveAddressesData(chainId, timeRange, 'day', pageSize, fetchAllPages),
getActiveAddressesData(chainId, timeRange, 'week', pageSize, fetchAllPages),
getActiveAddressesData(chainId, timeRange, 'month', pageSize, fetchAllPages),
getTimeSeriesData('activeSenders', chainId, timeRange, pageSize, fetchAllPages),
getTimeSeriesData('cumulativeAddresses', chainId, timeRange, pageSize, fetchAllPages),
getTimeSeriesData('cumulativeDeployers', chainId, timeRange, pageSize, fetchAllPages),
getTimeSeriesData('txCount', chainId, timeRange, pageSize, fetchAllPages),
getTimeSeriesData('cumulativeTxCount', chainId, timeRange, pageSize, fetchAllPages),
getTimeSeriesData('cumulativeContracts', chainId, timeRange, pageSize, fetchAllPages),
getTimeSeriesData('contracts', chainId, timeRange, pageSize, fetchAllPages),
getTimeSeriesData('deployers', chainId, timeRange, pageSize, fetchAllPages),
getTimeSeriesData('gasUsed', chainId, timeRange, pageSize, fetchAllPages),
getTimeSeriesData('avgGps', chainId, timeRange, pageSize, fetchAllPages),
getTimeSeriesData('maxGps', chainId, timeRange, pageSize, fetchAllPages),
getTimeSeriesData('avgTps', chainId, timeRange, pageSize, fetchAllPages),
getTimeSeriesData('maxTps', chainId, timeRange, pageSize, fetchAllPages),
getTimeSeriesData('avgGasPrice', chainId, timeRange, pageSize, fetchAllPages),
getTimeSeriesData('maxGasPrice', chainId, timeRange, pageSize, fetchAllPages),
getTimeSeriesData('feesPaid', chainId, timeRange, pageSize, fetchAllPages),
getICMData(chainId, timeRange),
]);

const metrics: ChainMetrics = {
activeAddresses: createTimeSeriesMetric(activeAddressesData),
activeAddresses: {
daily: createTimeSeriesMetric(dailyActiveAddressesData),
weekly: createTimeSeriesMetric(weeklyActiveAddressesData),
monthly: createTimeSeriesMetric(monthlyActiveAddressesData),
},
activeSenders: createTimeSeriesMetric(activeSendersData),
cumulativeAddresses: createTimeSeriesMetric(cumulativeAddressesData),
cumulativeDeployers: createTimeSeriesMetric(cumulativeDeployersData),
Expand Down
138 changes: 96 additions & 42 deletions app/api/overview-stats/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ interface ChainOverviewMetrics {
chainName: string;
chainLogoURI: string;
txCount: TimeSeriesMetric;
activeAddresses: TimeSeriesMetric;
activeAddresses: {
daily: TimeSeriesMetric;
weekly: TimeSeriesMetric;
monthly: TimeSeriesMetric;
};
icmMessages: ICMMetric;
validatorCount: number | string;
}
Expand All @@ -22,7 +26,11 @@ interface OverviewMetrics {
chains: ChainOverviewMetrics[];
aggregated: {
totalTxCount: TimeSeriesMetric;
totalActiveAddresses: TimeSeriesMetric;
totalActiveAddresses: {
daily: TimeSeriesMetric;
weekly: TimeSeriesMetric;
monthly: TimeSeriesMetric;
};
totalICMMessages: ICMMetric;
totalValidators: number;
activeChains: number;
Expand Down Expand Up @@ -89,10 +97,7 @@ async function getTimeSeriesData(
}

// separate active addresses fetching with proper time intervals
async function getActiveAddressesData(chainId: string, timeRange: string): Promise<TimeSeriesDataPoint[]> {
const intervalMapping = STATS_CONFIG.ACTIVE_ADDRESSES_INTERVALS[timeRange as keyof typeof STATS_CONFIG.ACTIVE_ADDRESSES_INTERVALS];
if (!intervalMapping) { return [] }

async function getActiveAddressesData(chainId: string, timeRange: string, interval: 'day' | 'week' | 'month'): Promise<TimeSeriesDataPoint[]> {
try {
const { startTimestamp, endTimestamp } = getTimestampsFromTimeRange(timeRange);
let allResults: any[] = [];
Expand All @@ -102,7 +107,7 @@ async function getActiveAddressesData(chainId: string, timeRange: string): Promi
metric: 'activeAddresses',
startTimestamp,
endTimestamp,
timeInterval: intervalMapping,
timeInterval: interval,
pageSize: 1,
};

Expand All @@ -122,7 +127,7 @@ async function getActiveAddressesData(chainId: string, timeRange: string): Promi
date: new Date(result.timestamp * 1000).toISOString().split('T')[0]
}));
} catch (error) {
console.warn(`Failed to fetch active addresses data for chain ${chainId}:`, error);
console.warn(`Failed to fetch active addresses data for chain ${chainId} with interval ${interval}:`, error);
return [];
}
}
Expand Down Expand Up @@ -194,9 +199,11 @@ async function fetchChainMetrics(chain: any, timeRange: string): Promise<ChainOv
const config = STATS_CONFIG.TIME_RANGES[timeRange as keyof typeof STATS_CONFIG.TIME_RANGES] || STATS_CONFIG.TIME_RANGES['30d'];
const { pageSize, fetchAllPages } = config;

const [txCountData, activeAddressesData, icmData, validatorCount] = await Promise.all([
const [txCountData, dailyAddresses, weeklyAddresses, monthlyAddresses, icmData, validatorCount] = await Promise.all([
getTimeSeriesData('txCount', chain.chainId, timeRange, pageSize, fetchAllPages),
getActiveAddressesData(chain.chainId, timeRange),
getActiveAddressesData(chain.chainId, timeRange, 'day'),
getActiveAddressesData(chain.chainId, timeRange, 'week'),
getActiveAddressesData(chain.chainId, timeRange, 'month'),
getICMData(chain.chainId, timeRange),
getValidatorCount(chain.subnetId),
]);
Expand All @@ -206,7 +213,11 @@ async function fetchChainMetrics(chain: any, timeRange: string): Promise<ChainOv
chainName: chain.chainName,
chainLogoURI: chain.logoUri,
txCount: createTimeSeriesMetricWithPeriodSum(txCountData), // Period sum for overview
activeAddresses: createTimeSeriesMetric(activeAddressesData),
activeAddresses: {
daily: createTimeSeriesMetric(dailyAddresses),
weekly: createTimeSeriesMetric(weeklyAddresses),
monthly: createTimeSeriesMetric(monthlyAddresses),
},
icmMessages: createICMMetricWithPeriodSum(icmData), // Period sum
validatorCount,
};
Expand Down Expand Up @@ -268,88 +279,127 @@ export async function GET(request: Request) {
}

const aggregatedTxData: TimeSeriesDataPoint[] = [];
const aggregatedAddressData: TimeSeriesDataPoint[] = [];
const aggregatedDailyAddressData: TimeSeriesDataPoint[] = [];
const aggregatedWeeklyAddressData: TimeSeriesDataPoint[] = [];
const aggregatedMonthlyAddressData: TimeSeriesDataPoint[] = [];
const aggregatedICMData: ICMDataPoint[] = [];

let totalValidators = 0;
let activeChains = 0;
let totalTxCountAllTime = 0;
let totalActiveAddressesAllTime = 0;
let totalDailyActiveAddressesAllTime = 0;
let totalWeeklyActiveAddressesAllTime = 0;
let totalMonthlyActiveAddressesAllTime = 0;
let totalICMMessagesAllTime = 0;

const dateMap = new Map<string, { tx: number; addresses: number; icm: number }>();
const dateMaps = {
tx: new Map<string, number>(),
daily: new Map<string, number>(),
weekly: new Map<string, number>(),
monthly: new Map<string, number>(),
icm: new Map<string, number>(),
};

chainMetrics.forEach(chain => {
if (typeof chain.validatorCount === 'number') {
totalValidators += chain.validatorCount;
}

const hasTx = chain.txCount.data.length > 0 && typeof chain.txCount.current_value === 'number' && chain.txCount.current_value > 0;
const hasAddresses = chain.activeAddresses.data.length > 0 && typeof chain.activeAddresses.current_value === 'number' && chain.activeAddresses.current_value > 0;
const hasAddresses = chain.activeAddresses.daily.data.length > 0 && typeof chain.activeAddresses.daily.current_value === 'number' && chain.activeAddresses.daily.current_value > 0;

if (hasTx || hasAddresses) { activeChains++; }

chain.txCount.data.forEach(point => {
const date = point.date;
const value = typeof point.value === 'number' ? point.value : 0;
const current = dateMap.get(date) || { tx: 0, addresses: 0, icm: 0 };
current.tx += value;
dateMap.set(date, current);
const current = dateMaps.tx.get(date) || 0;
dateMaps.tx.set(date, current + value);
totalTxCountAllTime += value;
});

chain.activeAddresses.data.forEach(point => {
chain.activeAddresses.daily.data.forEach(point => {
const date = point.date;
const value = typeof point.value === 'number' ? point.value : 0;
const current = dateMap.get(date) || { tx: 0, addresses: 0, icm: 0 };
current.addresses += value;
dateMap.set(date, current);
totalActiveAddressesAllTime += value;
const current = dateMaps.daily.get(date) || 0;
dateMaps.daily.set(date, current + value);
totalDailyActiveAddressesAllTime += value;
});

chain.activeAddresses.weekly.data.forEach(point => {
const date = point.date;
const value = typeof point.value === 'number' ? point.value : 0;
const current = dateMaps.weekly.get(date) || 0;
dateMaps.weekly.set(date, current + value);
totalWeeklyActiveAddressesAllTime += value;
});

chain.activeAddresses.monthly.data.forEach(point => {
const date = point.date;
const value = typeof point.value === 'number' ? point.value : 0;
const current = dateMaps.monthly.get(date) || 0;
dateMaps.monthly.set(date, current + value);
totalMonthlyActiveAddressesAllTime += value;
});

chain.icmMessages.data.forEach(point => {
const date = point.date;
const current = dateMap.get(date) || { tx: 0, addresses: 0, icm: 0 };
current.icm += point.messageCount;
dateMap.set(date, current);
const current = dateMaps.icm.get(date) || 0;
dateMaps.icm.set(date, current + point.messageCount);
totalICMMessagesAllTime += point.messageCount;
});
});

Array.from(dateMap.entries()).forEach(([date, values]) => {
Array.from(dateMaps.tx.entries()).forEach(([date, value]) => {
const timestamp = Math.floor(new Date(date).getTime() / 1000);
aggregatedTxData.push({
timestamp,
value: values.tx,
date
});
aggregatedTxData.push({ timestamp, value, date });
});

aggregatedAddressData.push({
timestamp,
value: values.addresses,
date
});
Array.from(dateMaps.daily.entries()).forEach(([date, value]) => {
const timestamp = Math.floor(new Date(date).getTime() / 1000);
aggregatedDailyAddressData.push({ timestamp, value, date });
});

Array.from(dateMaps.weekly.entries()).forEach(([date, value]) => {
const timestamp = Math.floor(new Date(date).getTime() / 1000);
aggregatedWeeklyAddressData.push({ timestamp, value, date });
});

Array.from(dateMaps.monthly.entries()).forEach(([date, value]) => {
const timestamp = Math.floor(new Date(date).getTime() / 1000);
aggregatedMonthlyAddressData.push({ timestamp, value, date });
});

Array.from(dateMaps.icm.entries()).forEach(([date, messageCount]) => {
const timestamp = Math.floor(new Date(date).getTime() / 1000);
aggregatedICMData.push({
timestamp,
date,
messageCount: values.icm,
messageCount,
incomingCount: 0,
outgoingCount: 0
});
});

// Sort by timestamp descending
aggregatedTxData.sort((a, b) => b.timestamp - a.timestamp);
aggregatedAddressData.sort((a, b) => b.timestamp - a.timestamp);
aggregatedDailyAddressData.sort((a, b) => b.timestamp - a.timestamp);
aggregatedWeeklyAddressData.sort((a, b) => b.timestamp - a.timestamp);
aggregatedMonthlyAddressData.sort((a, b) => b.timestamp - a.timestamp);
aggregatedICMData.sort((a, b) => b.timestamp - a.timestamp);

// Create aggregated metrics using period sum methods
const totalTxMetric = createTimeSeriesMetricWithPeriodSum(aggregatedTxData);
totalTxMetric.current_value = totalTxCountAllTime;

const totalAddressMetric = createTimeSeriesMetricWithPeriodSum(aggregatedAddressData);
totalAddressMetric.current_value = totalActiveAddressesAllTime;
const totalDailyAddressMetric = createTimeSeriesMetric(aggregatedDailyAddressData);
totalDailyAddressMetric.current_value = totalDailyActiveAddressesAllTime;

const totalWeeklyAddressMetric = createTimeSeriesMetric(aggregatedWeeklyAddressData);
totalWeeklyAddressMetric.current_value = totalWeeklyActiveAddressesAllTime;

const totalMonthlyAddressMetric = createTimeSeriesMetric(aggregatedMonthlyAddressData);
totalMonthlyAddressMetric.current_value = totalMonthlyActiveAddressesAllTime;

const totalICMMetric = createICMMetricWithPeriodSum(aggregatedICMData);
totalICMMetric.current_value = totalICMMessagesAllTime;
Expand All @@ -358,7 +408,11 @@ export async function GET(request: Request) {
chains: chainMetrics,
aggregated: {
totalTxCount: totalTxMetric,
totalActiveAddresses: totalAddressMetric,
totalActiveAddresses: {
daily: totalDailyAddressMetric,
weekly: totalWeeklyAddressMetric,
monthly: totalMonthlyAddressMetric,
},
totalICMMessages: totalICMMetric,
totalValidators,
activeChains
Expand Down
Loading