@@ -58,18 +58,11 @@ export function formatDeviation(threshold: number): string {
5858}
5959
6060export interface FeedMarkdownOptions {
61- /** Filter by schema version, e.g. "v8" or "v11". Only meaningful for streamsRwa. */
6261 schemaFilter ?: string
63- /** Public-facing API type name for use in generated URLs (e.g. "crypto" instead of "streamsCrypto"). */
6462 publicType ?: string
65- /** Restrict to "mainnet" or "testnet" networks only. */
6663 networkType ?: "mainnet" | "testnet"
6764}
6865
69- // ---------------------------------------------------------------------------
70- // Structured data types — used for JSON output
71- // ---------------------------------------------------------------------------
72-
7366export interface StreamEntry {
7467 name : string
7568 feedId : string
@@ -86,15 +79,10 @@ export interface FeedEntry {
8679 network : string
8780 networkName : string
8881 networkType : string
89- /** Blockchain ecosystem name (e.g. "Ethereum", "Arbitrum"). */
9082 chain : string
9183 svr ?: "shared" | "aave"
9284}
9385
94- // ---------------------------------------------------------------------------
95- // Shared data collectors — used by both markdown and JSON formatters
96- // ---------------------------------------------------------------------------
97-
9886// eslint-disable-next-line @typescript-eslint/no-explicit-any
9987function resolveSVR ( feed : any ) : "shared" | "aave" | undefined {
10088 const isShared = typeof feed . path === "string" && / - s h a r e d - s v r $ / . test ( feed . path )
@@ -109,22 +97,26 @@ export function collectStreamEntries(
10997 chainCache : Record < string , any > ,
11098 options : FeedMarkdownOptions = { }
11199) : StreamEntry [ ] {
112- const visibilityOpts = { schemaFilter : options . schemaFilter }
100+ const visibilityOpts = {
101+ ...( options . schemaFilter ? { schemaFilter : options . schemaFilter } : { } ) ,
102+ } as any
103+
113104 const seenFeedIds = new Set < string > ( )
114105 // eslint-disable-next-line @typescript-eslint/no-explicit-any
115106 const raw : any [ ] = [ ]
116107
117108 for ( const chain of CHAINS ) {
118109 const chainData = chainCache [ chain . page ]
119110 if ( ! chainData ?. networks ) continue
120- // eslint-disable-next-line @typescript-eslint/no-explicit-any
111+
121112 for ( const network of chainData . networks as any [ ] ) {
122113 if ( ! network ?. metadata ?. length ) continue
123114 if ( network . networkType !== "mainnet" ) continue
124- // eslint-disable-next-line @typescript-eslint/no-explicit-any
115+
125116 for ( const feed of network . metadata as any [ ] ) {
126117 if ( ! isFeedVisible ( feed , type , "" , visibilityOpts ) ) continue
127118 if ( ! feed . feedId || seenFeedIds . has ( feed . feedId ) ) continue
119+
128120 seenFeedIds . add ( feed . feedId )
129121 raw . push ( feed )
130122 }
@@ -133,55 +125,62 @@ export function collectStreamEntries(
133125
134126 raw . sort ( ( a , b ) => ( resolveStreamPair ( a ) ?? "" ) . localeCompare ( resolveStreamPair ( b ) ?? "" ) )
135127
136- // eslint-disable-next-line @typescript-eslint/no-explicit-any
137128 return raw . flatMap ( ( feed : any ) => {
138129 const name = resolveStreamPair ( feed )
139130 if ( ! name ) return [ ]
131+
140132 const tradingHours = resolveTradingHours ( feed )
133+
141134 const entry : StreamEntry = {
142135 name,
143136 feedId : feed . feedId ,
144137 assetClass : resolveAssetClass ( feed ) ,
145138 schema : resolveStreamSchema ( type , feed ) ,
146139 }
147- if ( tradingHours && tradingHours !== "—" ) entry . tradingHours = tradingHours
140+
141+ if ( tradingHours && tradingHours !== "—" ) {
142+ entry . tradingHours = tradingHours
143+ }
144+
148145 return [ entry ]
149146 } )
150147}
151148
152- /** Strips the " Data Feeds" suffix Chainlink appends to chain group titles. */
153149function resolveChainName ( chainTitle : string , chainPage : string ) : string {
154- // Handles "Arbitrum Data Feeds" → "Arbitrum" and "Data Feeds" (Ethereum) → "Ethereum"
155150 const stripped = chainTitle . replace ( / \s * D a t a F e e d s $ / , "" ) . trim ( )
156151 return stripped || chainPage . charAt ( 0 ) . toUpperCase ( ) + chainPage . slice ( 1 )
157152}
158153
159154export function collectFeedEntries (
160155 type : DataFeedType ,
161156 networkFilter : string | null ,
162- // eslint-disable-next-line @typescript-eslint/no-explicit-any
163157 chainCache : Record < string , any > ,
164158 options : FeedMarkdownOptions = { }
165159) : FeedEntry [ ] {
166- const visibilityOpts = { schemaFilter : options . schemaFilter }
160+ const visibilityOpts = {
161+ ...( options . schemaFilter ? { schemaFilter : options . schemaFilter } : { } ) ,
162+ } as any
163+
167164 const entries : FeedEntry [ ] = [ ]
168165
169166 for ( const chain of CHAINS ) {
170167 const chainData = chainCache [ chain . page ]
171168 if ( ! chainData ?. networks ) continue
169+
172170 const chainName = resolveChainName ( chain . title , chain . page )
173- // eslint-disable-next-line @typescript-eslint/no-explicit-any
171+
174172 for ( const network of chainData . networks as any [ ] ) {
175173 if ( ! network ?. metadata ?. length ) continue
176174 if ( networkFilter && network . queryString !== networkFilter ) continue
177175 if ( options . networkType && network . networkType !== options . networkType ) continue
178- // eslint-disable-next-line @typescript-eslint/no-explicit-any
176+
179177 const visibleFeeds = network . metadata . filter ( ( feed : any ) => isFeedVisible ( feed , type , "" , visibilityOpts ) )
178+
180179 if ( visibleFeeds . length === 0 ) continue
181180
182- // eslint-disable-next-line @typescript-eslint/no-explicit-any
183181 for ( const feed of visibleFeeds as any [ ] ) {
184182 const svr = resolveSVR ( feed )
183+
185184 const entry : FeedEntry = {
186185 name : feed . name || feed . assetName || "Unknown" ,
187186 proxyAddress : feed . proxyAddress || feed . transmissionsAccount || feed . contractAddress || "N/A" ,
@@ -192,13 +191,14 @@ export function collectFeedEntries(
192191 networkType : network . networkType ,
193192 chain : chainName ,
194193 }
194+
195195 if ( svr ) entry . svr = svr
196+
196197 entries . push ( entry )
197198 }
198199 }
199200 }
200201
201- // Sort alphabetically by feed name within each network for consistent, scannable output
202202 entries . sort ( ( a , b ) => {
203203 if ( a . network !== b . network ) return a . network . localeCompare ( b . network )
204204 return a . name . localeCompare ( b . name )
@@ -207,119 +207,46 @@ export function collectFeedEntries(
207207 return entries
208208}
209209
210- // ---------------------------------------------------------------------------
211- // Markdown formatter
212- // ---------------------------------------------------------------------------
213-
214- /**
215- * Builds a plain-markdown document listing feed addresses or stream IDs
216- * for the given type, optionally filtered to a single network.
217- */
218210export function buildFeedAddressMarkdown (
219211 type : DataFeedType ,
220212 networkFilter : string | null ,
221- // eslint-disable-next-line @typescript-eslint/no-explicit-any
222213 chainCache : Record < string , any > ,
223214 siteBase = "https://docs.chain.link" ,
224215 options : FeedMarkdownOptions = { }
225216) : string {
226217 const lines : string [ ] = [ ]
227218 const streams = isStreamsType ( type )
219+
228220 const label = FEED_TYPE_LABELS [ type ]
229- const baseUrl = `${ siteBase } /api/feeds/addresses?type=${ type } `
230- const visibilityOpts = { schemaFilter : options . schemaFilter }
231-
232- const mainnetQueryStrings : string [ ] = [ ]
233- const testnetQueryStrings : string [ ] = [ ]
234-
235- if ( ! streams ) {
236- for ( const chain of CHAINS ) {
237- const chainData = chainCache [ chain . page ]
238- if ( ! chainData ?. networks ) continue
239- // eslint-disable-next-line @typescript-eslint/no-explicit-any
240- for ( const network of chainData . networks as any [ ] ) {
241- if ( ! network ?. queryString ) continue
242- // eslint-disable-next-line @typescript-eslint/no-explicit-any
243- const hasFeeds = network . metadata ?. some ( ( feed : any ) => isFeedVisible ( feed , type , "" , visibilityOpts ) )
244- if ( ! hasFeeds ) continue
245- if ( network . networkType === "mainnet" ) mainnetQueryStrings . push ( network . queryString )
246- else testnetQueryStrings . push ( network . queryString )
247- }
248- }
249- }
250221
251222 if ( streams ) {
252223 lines . push ( `# Chainlink Data Streams: ${ label } ` )
253224 lines . push ( `Source: ${ siteBase } /data-streams` )
254- lines . push ( `Machine-readable endpoint: ${ siteBase } /api/streams/ids?type=${ options . publicType ?? type } ` )
255- lines . push ( `Static snapshot: ${ siteBase } /data-feeds/feed-addresses/${ type } .txt` )
256225 lines . push ( "" )
226+ lines . push ( `> Stream IDs for Chainlink Data Streams – ${ label . replace ( "Data Streams — " , "" ) } .` )
227+ lines . push ( `> These IDs are universal and valid across all supported networks.` )
257228 lines . push (
258- `> Stream IDs for Chainlink ** ${ label } **. Stream IDs are universal — the same ID is valid across all supported networks.`
229+ `> To use a stream ID, retrieve the verifier proxy for the target network from /data-streams/ networks.txt .`
259230 )
260- lines . push ( `> Supported networks and verifier proxy addresses: \` ${ siteBase } /api/streams/networks\` ` )
231+ lines . push ( `> Datasets may contain multiple schema versions. Filter by schema if needed. ` )
261232 lines . push ( "" )
262233
263234 const streamEntries = collectStreamEntries ( type , chainCache , options )
235+
264236 if ( streamEntries . length > 0 ) {
265237 lines . push ( "| Stream | Feed ID | Asset Class | Schema | Trading Hours |" )
266238 lines . push ( "|--------|---------|-------------|--------|---------------|" )
239+
267240 for ( const entry of streamEntries ) {
268241 lines . push (
269- `| ${ entry . name } | \`${ entry . feedId } \` | ${ entry . assetClass } | ${ entry . schema } | ${ entry . tradingHours ?? "—" } |`
242+ `| ${ entry . name } | \`${ entry . feedId } \` | ${ entry . assetClass } | \` ${ entry . schema } \` | ${ entry . tradingHours ?? "—" } |`
270243 )
271244 }
245+
272246 lines . push ( "" )
273247 } else {
274248 lines . push ( `No stream IDs found for type \`${ type } \`.` )
275249 }
276- } else {
277- lines . push ( `# Chainlink Feed Addresses: ${ label } ` )
278- lines . push ( `Source: ${ siteBase } /data-feeds/price-feeds/addresses` )
279- lines . push ( `Machine-readable endpoint: ${ baseUrl } ` )
280- lines . push ( `Static snapshot: ${ siteBase } /data-feeds/feed-addresses/${ type } .txt` )
281- lines . push ( "" )
282- lines . push ( `> Feed contract addresses for Chainlink **${ label } ** across all supported networks.` )
283- lines . push (
284- `> To narrow results to a single network, append \`&network={queryString}\` — e.g. \`${ baseUrl } &network=${ mainnetQueryStrings [ 0 ] ?? "ethereum-mainnet" } \``
285- )
286- lines . push ( `> Full network list with queryStrings: \`${ siteBase } /api/feeds/networks?type=${ type } \`` )
287- lines . push ( "" )
288- if ( mainnetQueryStrings . length > 0 ) {
289- lines . push ( `**Mainnet queryStrings:** ${ mainnetQueryStrings . map ( ( q ) => `\`${ q } \`` ) . join ( ", " ) } ` )
290- lines . push ( "" )
291- }
292- if ( testnetQueryStrings . length > 0 ) {
293- lines . push ( `**Testnet queryStrings:** ${ testnetQueryStrings . map ( ( q ) => `\`${ q } \`` ) . join ( ", " ) } ` )
294- lines . push ( "" )
295- }
296-
297- const feedEntries = collectFeedEntries ( type , networkFilter , chainCache , options )
298- if ( feedEntries . length === 0 ) {
299- lines . push (
300- networkFilter
301- ? `No feeds found for type \`${ type } \` on network \`${ networkFilter } \`. Check the network queryString or omit the \`network\` parameter to see all networks.`
302- : `No feeds found for type \`${ type } \`.`
303- )
304- } else {
305- let currentNetwork = ""
306- for ( const entry of feedEntries ) {
307- if ( entry . network !== currentNetwork ) {
308- currentNetwork = entry . network
309- const network = feedEntries . find ( ( e ) => e . network === currentNetwork ) !
310- lines . push ( `## ${ entry . chain } — ${ network . networkName } ` )
311- lines . push ( `- Network type: ${ network . networkType } ` )
312- lines . push ( `- Query string: \`${ network . network } \`` )
313- lines . push ( "" )
314- lines . push ( "| Feed Name | Proxy Address | Deviation | Heartbeat |" )
315- lines . push ( "|-----------|--------------|-----------|-----------|" )
316- }
317- const svrLabel = entry . svr === "shared" ? " (Shared SVR)" : entry . svr === "aave" ? " (Aave SVR)" : ""
318- const name = escapePipes ( `${ entry . name } ${ svrLabel } ` )
319- lines . push ( `| ${ name } | \`${ entry . proxyAddress } \` | ${ entry . deviation } | ${ entry . heartbeat } |` )
320- }
321- lines . push ( "" )
322- }
323250 }
324251
325252 return lines . join ( "\n" )
0 commit comments