@@ -42,6 +42,57 @@ const takeEvery = (arr: any[], n: number = 25) => {
4242 return arr . filter ( ( _ , i ) => i % n === 0 )
4343}
4444
45+ /**
46+ * For exchange rates, ensure we get at least one of every pair.
47+ * We take a consistent sample by selecting entries at regular block intervals,
48+ * ensuring coverage of all unique pairs.
49+ */
50+ const takeExchangeRateValidationEntries = ( arr : any [ ] ) => {
51+ // Group by pair
52+ const byPair = new Map < string , any [ ] > ( )
53+ for ( const entry of arr ) {
54+ const pair = entry . pair
55+ if ( ! byPair . has ( pair ) ) {
56+ byPair . set ( pair , [ ] )
57+ }
58+ byPair . get ( pair ) ! . push ( entry )
59+ }
60+
61+ const result : any [ ] = [ ]
62+
63+ // For each pair, take entries at consistent block intervals
64+ for ( const [ pair , entries ] of byPair ) {
65+ // Sort by blockNumber to ensure consistent ordering
66+ entries . sort ( ( a , b ) => a . blockNumber - b . blockNumber )
67+
68+ // Always include the first entry for this pair
69+ result . push ( entries [ 0 ] )
70+
71+ // Take entries at block intervals divisible by 100000
72+ const atIntervals = entries . filter ( ( entry ) => entry . blockNumber % 100000 === 0 )
73+ for ( const entry of atIntervals ) {
74+ if ( ! result . includes ( entry ) ) {
75+ result . push ( entry )
76+ }
77+ }
78+
79+ // If we only have the first entry, take a few more at regular intervals
80+ const entriesForPair = result . filter ( ( e ) => e . pair === pair )
81+ if ( entriesForPair . length < 3 && entries . length > 3 ) {
82+ const step = Math . floor ( entries . length / 3 )
83+ for ( let i = step ; i < entries . length ; i += step ) {
84+ if ( ! result . includes ( entries [ i ] ) ) {
85+ result . push ( entries [ i ] )
86+ }
87+ }
88+ }
89+ }
90+
91+ // Sort final result by blockNumber for consistency
92+ result . sort ( ( a , b ) => a . blockNumber - b . blockNumber )
93+ return result
94+ }
95+
4596const oTokens = ( prefix : string , address : string ) => {
4697 return gql ( `
4798 ${ prefix } _oTokens: oTokens(
@@ -985,6 +1036,26 @@ const protocolDailyStatDetails = () => {
9851036 ` )
9861037}
9871038
1039+ const exchangeRates = ( ) => {
1040+ return gql ( `
1041+ exchangeRates: exchangeRates(
1042+ limit: ${ LIMIT } ,
1043+ orderBy: [blockNumber_ASC, id_ASC],
1044+ where: { timestamp_gte: "2025-01-01T00:00:00Z", timestamp_lte: "${ twoDaysAgo . toISOString ( ) } " }
1045+ ) {
1046+ id
1047+ chainId
1048+ timestamp
1049+ blockNumber
1050+ pair
1051+ base
1052+ quote
1053+ rate
1054+ decimals
1055+ }
1056+ ` )
1057+ }
1058+
9881059const kebabCase = ( str : string ) => {
9891060 return str
9901061 . replace ( / ( [ a - z ] ) ( [ A - Z ] ) / g, '$1-$2' )
@@ -1100,6 +1171,7 @@ const main = async () => {
11001171 aeroPoolEpochStates ( ) ,
11011172 protocolDailyStatDetails ( ) ,
11021173 protocolDailyStats ( ) ,
1174+ exchangeRates ( ) ,
11031175 ] . map ( ( query ) => `query Query { ${ query } }` )
11041176
11051177 console . log ( 'Total queries:' , queries . length )
@@ -1121,6 +1193,9 @@ const main = async () => {
11211193 // If there are fewer than 20 total entries, save them all
11221194 if ( rawData . length < 20 || takeAll . includes ( key ) ) {
11231195 validationData = rawData
1196+ } else if ( key === 'exchangeRates' ) {
1197+ // Special handling for exchange rates: ensure at least one of every pair
1198+ validationData = takeExchangeRateValidationEntries ( rawData )
11241199 } else {
11251200 // Otherwise, filter to validation entries
11261201 validationData = takeValidationEntries ( rawData )
0 commit comments