Skip to content

Commit 1a9ea0b

Browse files
committed
Merge remote-tracking branch 'origin/master' into copy-commits-owen
2 parents f358b03 + daeed8f commit 1a9ea0b

File tree

71 files changed

+11208
-2991
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+11208
-2991
lines changed

app/(home)/stats/overview/page.tsx

Lines changed: 363 additions & 181 deletions
Large diffs are not rendered by default.

app/(home)/stats/playground/my-dashboards/page.tsx

Lines changed: 368 additions & 0 deletions
Large diffs are not rendered by default.

app/(home)/stats/playground/page.tsx

Lines changed: 1385 additions & 0 deletions
Large diffs are not rendered by default.

app/(home)/stats/primary-network/validators/page.tsx

Lines changed: 645 additions & 71 deletions
Large diffs are not rendered by default.

app/api/chain-stats/[chainId]/route.ts

Lines changed: 170 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ import { TimeSeriesDataPoint, TimeSeriesMetric, ICMDataPoint, ICMMetric, STATS_C
44
getTimestampsFromTimeRange, createTimeSeriesMetric, createICMMetric } from "@/types/stats";
55

66
interface ChainMetrics {
7-
activeAddresses: TimeSeriesMetric;
7+
activeAddresses: {
8+
daily: TimeSeriesMetric;
9+
weekly: TimeSeriesMetric;
10+
monthly: TimeSeriesMetric;
11+
};
812
activeSenders: TimeSeriesMetric;
913
cumulativeAddresses: TimeSeriesMetric;
1014
cumulativeDeployers: TimeSeriesMetric;
@@ -30,12 +34,26 @@ let cachedData: Map<string, { data: ChainMetrics; timestamp: number; icmTimeRang
3034
async function getTimeSeriesData(
3135
metricType: string,
3236
chainId: string,
33-
timeRange: string,
37+
timeRange: string,
38+
startTimestamp?: number,
39+
endTimestamp?: number,
3440
pageSize: number = 365,
3541
fetchAllPages: boolean = false
3642
): Promise<TimeSeriesDataPoint[]> {
3743
try {
38-
const { startTimestamp, endTimestamp } = getTimestampsFromTimeRange(timeRange);
44+
// Use provided timestamps if available, otherwise use timeRange
45+
let finalStartTimestamp: number;
46+
let finalEndTimestamp: number;
47+
48+
if (startTimestamp !== undefined && endTimestamp !== undefined) {
49+
finalStartTimestamp = startTimestamp;
50+
finalEndTimestamp = endTimestamp;
51+
} else {
52+
const timestamps = getTimestampsFromTimeRange(timeRange);
53+
finalStartTimestamp = timestamps.startTimestamp;
54+
finalEndTimestamp = timestamps.endTimestamp;
55+
}
56+
3957
let allResults: any[] = [];
4058

4159
const avalanche = new Avalanche({
@@ -46,8 +64,8 @@ async function getTimeSeriesData(
4664
const params: any = {
4765
chainId: chainId,
4866
metric: metricType as any,
49-
startTimestamp,
50-
endTimestamp,
67+
startTimestamp: finalStartTimestamp,
68+
endTimestamp: finalEndTimestamp,
5169
timeInterval: "day",
5270
pageSize,
5371
};
@@ -82,19 +100,84 @@ async function getTimeSeriesData(
82100
}
83101
}
84102

85-
async function getICMData(chainId: string, timeRange: string): Promise<ICMDataPoint[]> {
103+
// Separate active addresses fetching with proper time intervals (optimize other metrics as needed)
104+
async function getActiveAddressesData(chainId: string, timeRange: string, interval: 'day' | 'week' | 'month', pageSize: number = 365, fetchAllPages: boolean = false): Promise<TimeSeriesDataPoint[]> {
86105
try {
87-
const getDaysFromTimeRange = (range: string): number => {
88-
switch (range) {
89-
case '7d': return 7;
90-
case '30d': return 30;
91-
case '90d': return 90;
92-
case 'all': return 365;
93-
default: return 30;
94-
}
106+
const { startTimestamp, endTimestamp } = getTimestampsFromTimeRange(timeRange);
107+
let allResults: any[] = [];
108+
109+
const avalanche = new Avalanche({
110+
network: "mainnet"
111+
});
112+
113+
const rlToken = process.env.METRICS_BYPASS_TOKEN || '';
114+
const params: any = {
115+
chainId: chainId,
116+
metric: 'activeAddresses',
117+
startTimestamp,
118+
endTimestamp,
119+
timeInterval: interval,
120+
pageSize,
95121
};
122+
123+
if (rlToken) { params.rltoken = rlToken; }
124+
125+
const result = await avalanche.metrics.chains.getMetrics(params);
126+
127+
for await (const page of result) {
128+
if (!page?.result?.results || !Array.isArray(page.result.results)) {
129+
console.warn(`Invalid page structure for activeAddresses (${interval}) on chain ${chainId}:`, page);
130+
continue;
131+
}
132+
133+
allResults = allResults.concat(page.result.results);
134+
135+
if (!fetchAllPages) {
136+
break;
137+
}
138+
}
139+
140+
return allResults
141+
.sort((a: any, b: any) => b.timestamp - a.timestamp)
142+
.map((result: any) => ({
143+
timestamp: result.timestamp,
144+
value: result.value || 0,
145+
date: new Date(result.timestamp * 1000).toISOString().split('T')[0]
146+
}));
147+
} catch (error) {
148+
console.warn(`Failed to fetch activeAddresses data for chain ${chainId} with interval ${interval}:`, error);
149+
return [];
150+
}
151+
}
152+
153+
async function getICMData(
154+
chainId: string,
155+
timeRange: string,
156+
startTimestamp?: number,
157+
endTimestamp?: number
158+
): Promise<ICMDataPoint[]> {
159+
try {
160+
let days: number;
161+
162+
if (startTimestamp !== undefined && endTimestamp !== undefined) {
163+
// Calculate days from timestamps
164+
const startDate = new Date(startTimestamp * 1000);
165+
const endDate = new Date(endTimestamp * 1000);
166+
const diffTime = Math.abs(endDate.getTime() - startDate.getTime());
167+
days = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
168+
} else {
169+
const getDaysFromTimeRange = (range: string): number => {
170+
switch (range) {
171+
case '7d': return 7;
172+
case '30d': return 30;
173+
case '90d': return 90;
174+
case 'all': return 365;
175+
default: return 30;
176+
}
177+
};
178+
days = getDaysFromTimeRange(timeRange);
179+
}
96180

97-
const days = getDaysFromTimeRange(timeRange);
98181
const response = await fetch(`https://idx6.solokhin.com/api/${chainId}/metrics/dailyMessageVolume?days=${days}`, {
99182
headers: { 'Accept': 'application/json' },
100183
});
@@ -108,7 +191,7 @@ async function getICMData(chainId: string, timeRange: string): Promise<ICMDataPo
108191
return [];
109192
}
110193

111-
return data
194+
let filteredData = data
112195
.sort((a: any, b: any) => b.timestamp - a.timestamp)
113196
.map((item: any) => ({
114197
timestamp: item.timestamp,
@@ -117,6 +200,15 @@ async function getICMData(chainId: string, timeRange: string): Promise<ICMDataPo
117200
incomingCount: item.incomingCount || 0,
118201
outgoingCount: item.outgoingCount || 0,
119202
}));
203+
204+
// Filter by timestamps if provided
205+
if (startTimestamp !== undefined && endTimestamp !== undefined) {
206+
filteredData = filteredData.filter((item: ICMDataPoint) => {
207+
return item.timestamp >= startTimestamp && item.timestamp <= endTimestamp;
208+
});
209+
}
210+
211+
return filteredData;
120212
} catch (error) {
121213
console.warn(`Failed to fetch ICM data for chain ${chainId}:`, error);
122214
return [];
@@ -131,6 +223,8 @@ export async function GET(
131223
try {
132224
const { searchParams } = new URL(request.url);
133225
const timeRange = searchParams.get('timeRange') || '30d';
226+
const startTimestampParam = searchParams.get('startTimestamp');
227+
const endTimestampParam = searchParams.get('endTimestamp');
134228
const resolvedParams = await params;
135229
const chainId = resolvedParams.chainId;
136230

@@ -141,7 +235,34 @@ export async function GET(
141235
);
142236
}
143237

144-
const cacheKey = `${chainId}-${timeRange}`;
238+
// Parse timestamps if provided
239+
const startTimestamp = startTimestampParam ? parseInt(startTimestampParam, 10) : undefined;
240+
const endTimestamp = endTimestampParam ? parseInt(endTimestampParam, 10) : undefined;
241+
242+
// Validate timestamps
243+
if (startTimestamp !== undefined && isNaN(startTimestamp)) {
244+
return NextResponse.json(
245+
{ error: 'Invalid startTimestamp parameter' },
246+
{ status: 400 }
247+
);
248+
}
249+
if (endTimestamp !== undefined && isNaN(endTimestamp)) {
250+
return NextResponse.json(
251+
{ error: 'Invalid endTimestamp parameter' },
252+
{ status: 400 }
253+
);
254+
}
255+
if (startTimestamp !== undefined && endTimestamp !== undefined && startTimestamp > endTimestamp) {
256+
return NextResponse.json(
257+
{ error: 'startTimestamp must be less than or equal to endTimestamp' },
258+
{ status: 400 }
259+
);
260+
}
261+
262+
// Create cache key including timestamps if provided
263+
const cacheKey = startTimestamp !== undefined && endTimestamp !== undefined
264+
? `${chainId}-${startTimestamp}-${endTimestamp}`
265+
: `${chainId}-${timeRange}`;
145266

146267
if (searchParams.get('clearCache') === 'true') {
147268
cachedData.clear();
@@ -150,9 +271,10 @@ export async function GET(
150271
const cached = cachedData.get(cacheKey);
151272

152273
if (cached && Date.now() - cached.timestamp < STATS_CONFIG.CACHE.LONG_DURATION) {
153-
if (cached.icmTimeRange !== timeRange) {
274+
// Only refetch ICM data if timeRange changed (not for timestamp-based queries)
275+
if (startTimestamp === undefined && endTimestamp === undefined && cached.icmTimeRange !== timeRange) {
154276
try {
155-
const newICMData = await getICMData(chainId, timeRange);
277+
const newICMData = await getICMData(chainId, timeRange, startTimestamp, endTimestamp);
156278
cached.data.icmMessages = createICMMetric(newICMData);
157279
cached.icmTimeRange = timeRange;
158280
cachedData.set(cacheKey, cached);
@@ -180,7 +302,9 @@ export async function GET(
180302
const { pageSize, fetchAllPages } = config;
181303

182304
const [
183-
activeAddressesData,
305+
dailyActiveAddressesData,
306+
weeklyActiveAddressesData,
307+
monthlyActiveAddressesData,
184308
activeSendersData,
185309
cumulativeAddressesData,
186310
cumulativeDeployersData,
@@ -199,28 +323,34 @@ export async function GET(
199323
feesPaidData,
200324
icmData,
201325
] = await Promise.all([
202-
getTimeSeriesData('activeAddresses', chainId, timeRange, pageSize, fetchAllPages),
203-
getTimeSeriesData('activeSenders', chainId, timeRange, pageSize, fetchAllPages),
204-
getTimeSeriesData('cumulativeAddresses', chainId, timeRange, pageSize, fetchAllPages),
205-
getTimeSeriesData('cumulativeDeployers', chainId, timeRange, pageSize, fetchAllPages),
206-
getTimeSeriesData('txCount', chainId, timeRange, pageSize, fetchAllPages),
207-
getTimeSeriesData('cumulativeTxCount', chainId, timeRange, pageSize, fetchAllPages),
208-
getTimeSeriesData('cumulativeContracts', chainId, timeRange, pageSize, fetchAllPages),
209-
getTimeSeriesData('contracts', chainId, timeRange, pageSize, fetchAllPages),
210-
getTimeSeriesData('deployers', chainId, timeRange, pageSize, fetchAllPages),
211-
getTimeSeriesData('gasUsed', chainId, timeRange, pageSize, fetchAllPages),
212-
getTimeSeriesData('avgGps', chainId, timeRange, pageSize, fetchAllPages),
213-
getTimeSeriesData('maxGps', chainId, timeRange, pageSize, fetchAllPages),
214-
getTimeSeriesData('avgTps', chainId, timeRange, pageSize, fetchAllPages),
215-
getTimeSeriesData('maxTps', chainId, timeRange, pageSize, fetchAllPages),
216-
getTimeSeriesData('avgGasPrice', chainId, timeRange, pageSize, fetchAllPages),
217-
getTimeSeriesData('maxGasPrice', chainId, timeRange, pageSize, fetchAllPages),
218-
getTimeSeriesData('feesPaid', chainId, timeRange, pageSize, fetchAllPages),
219-
getICMData(chainId, timeRange),
326+
getActiveAddressesData(chainId, timeRange, 'day', pageSize, fetchAllPages),
327+
getActiveAddressesData(chainId, timeRange, 'week', pageSize, fetchAllPages),
328+
getActiveAddressesData(chainId, timeRange, 'month', pageSize, fetchAllPages),
329+
getTimeSeriesData('activeSenders', chainId, timeRange, startTimestamp, endTimestamp, pageSize, fetchAllPages),
330+
getTimeSeriesData('cumulativeAddresses', chainId, timeRange, startTimestamp, endTimestamp, pageSize, fetchAllPages),
331+
getTimeSeriesData('cumulativeDeployers', chainId, timeRange, startTimestamp, endTimestamp, pageSize, fetchAllPages),
332+
getTimeSeriesData('txCount', chainId, timeRange, startTimestamp, endTimestamp, pageSize, fetchAllPages),
333+
getTimeSeriesData('cumulativeTxCount', chainId, timeRange, startTimestamp, endTimestamp, pageSize, fetchAllPages),
334+
getTimeSeriesData('cumulativeContracts', chainId, timeRange, startTimestamp, endTimestamp, pageSize, fetchAllPages),
335+
getTimeSeriesData('contracts', chainId, timeRange, startTimestamp, endTimestamp, pageSize, fetchAllPages),
336+
getTimeSeriesData('deployers', chainId, timeRange, startTimestamp, endTimestamp, pageSize, fetchAllPages),
337+
getTimeSeriesData('gasUsed', chainId, timeRange, startTimestamp, endTimestamp, pageSize, fetchAllPages),
338+
getTimeSeriesData('avgGps', chainId, timeRange, startTimestamp, endTimestamp, pageSize, fetchAllPages),
339+
getTimeSeriesData('maxGps', chainId, timeRange, startTimestamp, endTimestamp, pageSize, fetchAllPages),
340+
getTimeSeriesData('avgTps', chainId, timeRange, startTimestamp, endTimestamp, pageSize, fetchAllPages),
341+
getTimeSeriesData('maxTps', chainId, timeRange, startTimestamp, endTimestamp, pageSize, fetchAllPages),
342+
getTimeSeriesData('avgGasPrice', chainId, timeRange, startTimestamp, endTimestamp, pageSize, fetchAllPages),
343+
getTimeSeriesData('maxGasPrice', chainId, timeRange, startTimestamp, endTimestamp, pageSize, fetchAllPages),
344+
getTimeSeriesData('feesPaid', chainId, timeRange, startTimestamp, endTimestamp, pageSize, fetchAllPages),
345+
getICMData(chainId, timeRange, startTimestamp, endTimestamp),
220346
]);
221347

222348
const metrics: ChainMetrics = {
223-
activeAddresses: createTimeSeriesMetric(activeAddressesData),
349+
activeAddresses: {
350+
daily: createTimeSeriesMetric(dailyActiveAddressesData),
351+
weekly: createTimeSeriesMetric(weeklyActiveAddressesData),
352+
monthly: createTimeSeriesMetric(monthlyActiveAddressesData),
353+
},
224354
activeSenders: createTimeSeriesMetric(activeSendersData),
225355
cumulativeAddresses: createTimeSeriesMetric(cumulativeAddressesData),
226356
cumulativeDeployers: createTimeSeriesMetric(cumulativeDeployersData),
@@ -277,7 +407,7 @@ export async function GET(
277407
if (cached) {
278408
if (cached.icmTimeRange !== fallbackTimeRange) {
279409
try {
280-
const newICMData = await getICMData(chainId, fallbackTimeRange);
410+
const newICMData = await getICMData(chainId, fallbackTimeRange, undefined, undefined);
281411
cached.data.icmMessages = createICMMetric(newICMData);
282412
cached.icmTimeRange = fallbackTimeRange;
283413
cachedData.set(fallbackCacheKey, cached);

0 commit comments

Comments
 (0)