diff --git a/src/features/feeds/components/FeedList.tsx b/src/features/feeds/components/FeedList.tsx index f3071e16b08..682e13a3dda 100644 --- a/src/features/feeds/components/FeedList.tsx +++ b/src/features/feeds/components/FeedList.tsx @@ -15,6 +15,7 @@ import button from "@chainlink/design-system/button.module.css" import { updateTableOfContents } from "~/components/TableOfContents/tocStore.ts" import alertIcon from "../../../components/Alert/Assets/alert-icon.svg" import { ChainSelector } from "~/components/ChainSelector/ChainSelector.tsx" +import { isFeedVisible } from "../utils/feedVisibility.ts" export type DataFeedType = | "default" @@ -606,24 +607,35 @@ export const FeedList = ({ const networkTypes = { mainnet: false, testnet: false } // Filter networks by feed type - const filteredNetworks = chainMetadata.processedData.networks.filter((network) => { - if (isDeprecating) { - let foundDeprecated = false - network.metadata?.forEach((feed: any) => { - if (feed.feedCategory === "deprecating") { - foundDeprecated = true - } - }) - return foundDeprecated - } - - if (isStreams) return network.tags?.includes("streams") - if (isSmartData) return network.tags?.includes("smartData") - if (isRates) return network.tags?.includes("rates") - if (isUSGovernmentMacroeconomicData) return network.tags?.includes("usGovernmentMacroeconomicData") + const filteredNetworks = chainMetadata.processedData.networks + .filter((network) => { + if (isDeprecating) { + let foundDeprecated = false + network.metadata?.forEach((feed: any) => { + if (feed.feedCategory === "deprecating") { + foundDeprecated = true + } + }) + // A deprecating network is relevant only if it still has at least one non-hidden deprecating feed + if (!foundDeprecated) return false + const hasVisible = network.metadata?.some( + (feed: any) => feed.feedCategory === "deprecating" && feed.feedCategory !== "hidden" && !feed.docs?.hidden + ) + return !!hasVisible + } - return true - }) + if (isStreams) return network.tags?.includes("streams") + if (isSmartData) return network.tags?.includes("smartData") + if (isRates) return network.tags?.includes("rates") + if (isUSGovernmentMacroeconomicData) return network.tags?.includes("usGovernmentMacroeconomicData") + + return true + }) + .filter((network) => { + // Ensure the network has at least one visible feed for the current dataFeedType + const feeds = network.metadata || [] + return feeds.some((feed: any) => isFeedVisible(feed, dataFeedType, ecosystem)) + }) // Check available network types filteredNetworks.forEach((network) => { diff --git a/src/features/feeds/components/Tables.tsx b/src/features/feeds/components/Tables.tsx index 31baeb4545e..c4badc54af8 100644 --- a/src/features/feeds/components/Tables.tsx +++ b/src/features/feeds/components/Tables.tsx @@ -13,6 +13,7 @@ import { FEED_CATEGORY_CONFIG } from "../../../db/feedCategories.js" import { useBatchedFeedCategories, getFeedCategoryFromBatch, getNetworkIdentifier } from "./useBatchedFeedCategories.ts" import { isSharedSVR, isAaveSVR } from "~/features/feeds/utils/svrDetection.ts" import { ExpandableTableWrapper } from "./ExpandableTableWrapper.tsx" +import { isFeedVisible } from "~/features/feeds/utils/feedVisibility.ts" const feedItems = monitoredFeeds.mainnet @@ -1145,87 +1146,13 @@ export const MainnetTable = ({ if (isDeprecating) return !!metadata.docs.shutdownDate - if (dataFeedType === "streamsCrypto") { - const isValidStreamsFeed = - metadata.contractType === "verifier" && - (metadata.docs.feedType === "Crypto" || metadata.docs.feedType === "Crypto-DEX") - - if (showOnlyDEXFeeds) { - return isValidStreamsFeed && metadata.docs.feedType === "Crypto-DEX" - } - - return isValidStreamsFeed - } - if (dataFeedType === "streamsRwa") { - const isRwaFeed = - metadata.contractType === "verifier" && - (metadata.docs.feedType === "Equities" || - metadata.docs.feedType === "Forex" || - metadata.docs.feedType === "Datalink") - - if (!isRwaFeed) return false - - // Apply feed type filter - if (streamCategoryFilter === "datalink") { - if (metadata.docs.feedType !== "Datalink") return false - } else if (streamCategoryFilter === "equities") { - if (metadata.docs.feedType !== "Equities") return false - } else if (streamCategoryFilter === "forex") { - if (metadata.docs.feedType !== "Forex") return false - } - - // Apply schema filter - if (rwaSchemaFilter === "v8") { - return metadata.docs?.schema === "v8" || !metadata.docs?.schema - } - if (rwaSchemaFilter === "v11") { - return metadata.docs?.schema === "v11" - } - - return true - } - - if (dataFeedType === "streamsNav") { - return metadata.contractType === "verifier" && metadata.docs.feedType === "Net Asset Value" - } - - if (dataFeedType === "streamsExRate") { - return metadata.contractType === "verifier" && metadata.docs?.productTypeCode === "ExRate" - } - - if (dataFeedType === "streamsBacked") { - return metadata.contractType === "verifier" && metadata.docs.feedType === "Tokenized Equities" - } - - if (isSmartData) { - if (showOnlyMVRFeeds) { - return !metadata.docs?.hidden && metadata.docs?.isMVR === true && metadata.docs?.deliveryChannelCode !== "DS" - } - - return ( - !metadata.docs?.hidden && - metadata.docs?.deliveryChannelCode !== "DS" && - (metadata.docs?.productType === "Proof of Reserve" || - metadata.docs?.productType === "NAVLink" || - metadata.docs?.productType === "SmartAUM" || - metadata.docs?.isMVR === true) - ) - } - - if (isUSGovernmentMacroeconomicData) { - const isMacro = metadata.docs?.productTypeCode === "RefMacro" - return isMacro - } - - // Exclude MVR feeds from default view - return ( - !metadata.docs.porType && - metadata.contractType !== "verifier" && - metadata.docs.productType !== "Proof of Reserve" && - metadata.docs.productType !== "NAVLink" && - metadata.docs.productType !== "SmartAUM" && - metadata.docs?.productTypeCode !== "RefMacro" - ) + // Use shared visibility logic with filters + return isFeedVisible(metadata, dataFeedType as any, ecosystem, { + showOnlyDEXFeeds, + streamCategoryFilter, + rwaSchemaFilter, + showOnlyMVRFeeds, + }) }) .filter((metadata) => { if (isSmartData) { @@ -1428,98 +1355,13 @@ export const TestnetTable = ({ } if (batchCategory === "hidden") return false - if (isStreams) { - if (dataFeedType === "streamsCrypto") { - const isValidStreamsFeed = - metadata.contractType === "verifier" && - (metadata.feedType === "Crypto" || metadata.feedType === "Crypto-DEX") - - if (showOnlyDEXFeeds) { - return isValidStreamsFeed && metadata.feedType === "Crypto-DEX" - } - - return isValidStreamsFeed - } - - if (dataFeedType === "streamsRwa") { - const isRwaFeed = - metadata.contractType === "verifier" && - (metadata.docs.feedType === "Equities" || - metadata.docs.feedType === "Forex" || - metadata.docs.feedType === "Datalink") - - if (!isRwaFeed) return false - - // Apply feed type filter - if (streamCategoryFilter === "datalink") { - if (metadata.docs.feedType !== "Datalink") return false - } else if (streamCategoryFilter === "equities") { - if (metadata.docs.feedType !== "Equities") return false - } else if (streamCategoryFilter === "forex") { - if (metadata.docs.feedType !== "Forex") return false - } - - // Apply schema filter - if (rwaSchemaFilter === "v8") { - return metadata.docs?.schema === "v8" || !metadata.docs?.schema - } - if (rwaSchemaFilter === "v11") { - return metadata.docs?.schema === "v11" - } - - return true - } - - if (dataFeedType === "streamsExRate") { - return metadata.contractType === "verifier" && metadata.docs?.productTypeCode === "ExRate" - } - - if (dataFeedType === "streamsNav") { - return metadata.contractType === "verifier" && metadata.docs.feedType === "Net Asset Value" - } - - if (dataFeedType === "streamsBacked") { - return metadata.contractType === "verifier" && metadata.docs.feedType === "Tokenized Equities" - } - - // If we're in streams mode but didn't match any specific stream type, exclude this feed - return false - } - - if (isSmartData) { - if (showOnlyMVRFeeds) { - return !metadata.docs?.hidden && metadata.docs?.isMVR === true && metadata.docs?.deliveryChannelCode !== "DS" - } - - // Otherwise, include all SmartData feeds (MVR, PoR, NAVLink, SmartAUM) - return ( - !metadata.docs?.hidden && - metadata.docs?.deliveryChannelCode !== "DS" && - (metadata.docs?.productType === "Proof of Reserve" || - metadata.docs?.productType === "NAVLink" || - metadata.docs?.productType === "SmartAUM" || - metadata.docs?.isMVR === true) - ) - } - - if (isRates) - return !!(metadata.docs.productType === "Rates" || metadata.docs.productSubType === "Realized Volatility") - - if (isUSGovernmentMacroeconomicData) { - return metadata.docs?.productTypeCode === "RefMacro" - } - - // Exclude MVR feeds from default view - return ( - !metadata.feedId && - !metadata.docs.porType && - metadata.docs.productType !== "Rates" && - metadata.docs.productSubType !== "Realized Volatility" && - metadata.docs.productType !== "Proof of Reserve" && - metadata.docs.productType !== "NAVLink" && - metadata.docs.productType !== "SmartAUM" && - metadata.docs?.productTypeCode !== "RefMacro" - ) + // Use shared visibility logic with filters + return isFeedVisible(metadata, dataFeedType as any, undefined, { + showOnlyDEXFeeds, + streamCategoryFilter, + rwaSchemaFilter, + showOnlyMVRFeeds, + }) }) .filter((metadata) => { if (isSmartData) { diff --git a/src/features/feeds/utils/feedVisibility.ts b/src/features/feeds/utils/feedVisibility.ts new file mode 100644 index 00000000000..1f7280bc054 --- /dev/null +++ b/src/features/feeds/utils/feedVisibility.ts @@ -0,0 +1,134 @@ +import { DataFeedType } from "../components/FeedList.tsx" + +/** + * Determines if a feed should be visible based on: + * - Hidden flags (feedCategory === "hidden" or docs.hidden) + * - Data feed type filtering (streams, smartdata, rates, etc.) + * - Ecosystem filtering (deprecating) + * + * This logic is shared between table filtering and network availability checks. + */ +export interface FeedVisibilityOptions { + showOnlyDEXFeeds?: boolean + streamCategoryFilter?: string + rwaSchemaFilter?: string + showOnlyMVRFeeds?: boolean +} + +/** + * Determines if a feed should be visible based on: + * - Hidden flags (feedCategory === "hidden" or docs.hidden) + * - Data feed type filtering (streams, smartdata, rates, etc.) + * - Ecosystem filtering (deprecating) + * - Optional filters (DEX only, MVR only, schema version, etc.) + * + * This logic is shared between table filtering and network availability checks. + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function isFeedVisible( + feed: any, + dataFeedType: DataFeedType, + ecosystem = "", + options: FeedVisibilityOptions = {} +): boolean { + // =========================================================================== + // 1. Universal Exclusions + // =========================================================================== + // Always hide feeds marked as hidden in metadata or docs + if (feed.feedCategory === "hidden" || feed.docs?.hidden) return false + + const isDeprecating = ecosystem === "deprecating" + const isStreams = + dataFeedType === "streamsCrypto" || + dataFeedType === "streamsRwa" || + dataFeedType === "streamsNav" || + dataFeedType === "streamsExRate" || + dataFeedType === "streamsBacked" + const isSmartData = dataFeedType === "smartdata" + const isRates = dataFeedType === "rates" + const isUSGovernmentMacroeconomicData = dataFeedType === "usGovernmentMacroeconomicData" + + // =========================================================================== + // 2. Ecosystem-Specific Logic + // =========================================================================== + // If we are in the "deprecating" ecosystem view, ONLY show deprecating feeds. + if (isDeprecating && feed.feedCategory !== "deprecating") return false + + let isVisible = false + + // =========================================================================== + // 3. Data Feed Type Logic (Base Visibility) + // =========================================================================== + // Determine if the feed belongs to the requested category (Streams, SmartData, etc.) + + if (isStreams) { + // Streams feeds must be verified contracts + if (feed.contractType !== "verifier") return false + + if (dataFeedType === "streamsCrypto") { + isVisible = ["Crypto", "Crypto-DEX"].includes(feed.docs?.feedType) + } else if (dataFeedType === "streamsRwa") { + isVisible = ["Equities", "Forex", "Datalink"].includes(feed.docs?.feedType) + } else if (dataFeedType === "streamsNav") { + isVisible = feed.docs?.feedType === "Net Asset Value" + } else if (dataFeedType === "streamsExRate") { + isVisible = feed.docs?.productTypeCode === "ExRate" + } else if (dataFeedType === "streamsBacked") { + isVisible = feed.docs?.feedType === "Tokenized Equities" + } + } else if (isSmartData) { + // SmartData feeds (excluding DS delivery channel) + if (feed.docs?.deliveryChannelCode === "DS") isVisible = false + else + isVisible = + feed.docs?.isMVR === true || + feed.docs?.productType === "Proof of Reserve" || + feed.docs?.productType === "NAVLink" || + feed.docs?.productType === "SmartAUM" + } else if (isUSGovernmentMacroeconomicData) { + isVisible = feed.docs?.productTypeCode === "RefMacro" + } else if (isRates) { + isVisible = feed.docs?.productType === "Rates" || feed.docs?.productSubType === "Realized Volatility" + } else { + // Default data feeds (Standard Price Feeds) + // Exclude all special types to leave only the standard feeds + isVisible = + !feed.docs?.porType && + feed.contractType !== "verifier" && + feed.docs?.productType !== "Proof of Reserve" && + feed.docs?.productType !== "NAVLink" && + feed.docs?.productType !== "SmartAUM" && + feed.docs?.productType !== "Rates" && + feed.docs?.productTypeCode !== "RefMacro" && + !feed.docs?.isMVR + } + + if (!isVisible) return false + + // =========================================================================== + // 4. Optional Filters (User Selection) + // =========================================================================== + // Apply additional filters selected by the user in the UI + + // Filter: Show only DEX feeds (Streams Crypto) + if (dataFeedType === "streamsCrypto" && options.showOnlyDEXFeeds) { + if (feed.docs?.feedType !== "Crypto-DEX") return false + } + + // Filter: RWA Category & Schema (Streams RWA) + if (dataFeedType === "streamsRwa") { + if (options.streamCategoryFilter === "datalink" && feed.docs.feedType !== "Datalink") return false + if (options.streamCategoryFilter === "equities" && feed.docs.feedType !== "Equities") return false + if (options.streamCategoryFilter === "forex" && feed.docs.feedType !== "Forex") return false + + if (options.rwaSchemaFilter === "v8" && feed.docs?.schema === "v11") return false + if (options.rwaSchemaFilter === "v11" && feed.docs?.schema !== "v11") return false + } + + // Filter: Show only MVR feeds (SmartData) + if (isSmartData && options.showOnlyMVRFeeds) { + if (feed.docs?.isMVR !== true) return false + } + + return true +}