@@ -4,7 +4,11 @@ import { TimeSeriesDataPoint, TimeSeriesMetric, ICMDataPoint, ICMMetric, STATS_C
44 getTimestampsFromTimeRange , createTimeSeriesMetric , createICMMetric } from "@/types/stats" ;
55
66interface 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
3034async 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