diff --git a/src/config/sidebar.ts b/src/config/sidebar.ts index 7ffb0d295e6..160dd9cbad5 100644 --- a/src/config/sidebar.ts +++ b/src/config/sidebar.ts @@ -1300,7 +1300,7 @@ export const SIDEBAR_WITH_SUNSET_PRODUCTS: Partial onboarded. Searchers can [subscribe here](https://chainlinkcommunity.typeform.com/to/s55Hu2tK) to receive timely > Chainlink SVR updates. -> **TIP: SVR Now Available on Base, Arbitrum, and BNB Chain** +> **TIP: SVR Now Available Beyond Ethereum Mainnet** > -> Chainlink SVR has expanded beyond Ethereum Mainnet and is now available on Base, Arbitrum, and BNB Chain via the -> Atlas auction system. See the +> Chainlink SVR has expanded beyond Ethereum Mainnet and is now available on Base, Arbitrum, BNB Chain, Monad, Arc, +> HyperEVM, Ink, Robinhood Chain, and Unichain via the Atlas auction system. See the > [Atlas searcher onboarding guide](/data-feeds/svr-feeds/searcher-onboarding-atlas) for integration > details. @@ -6262,7 +6262,7 @@ If the **private route** fails or times out, the SVR feed automatically **revert ## SVR Feed Variants -Chainlink SVR Feeds are available in three variants: +Each SVR feed carries a variant label in the [Feed Addresses](/data-feeds/price-feeds/addresses) table and on [data.chain.link](https://data.chain.link/). The label identifies which protocol or integration the feed is intended for. ### SVR (Shared) @@ -6272,9 +6272,9 @@ Canonical shared SVR feeds for use by any protocol. These are the recommended fe Dedicated SVR feeds exclusively for the Aave protocol. -### SVR-Backup (Legacy) +### Compound SVR Feeds -Legacy shared SVR feeds that are still actively used by existing applications. Searchers should continue to monitor these feeds. New integrations should use the [SVR (Shared)](#svr-shared) feeds instead. +Dedicated SVR feeds exclusively for the Compound protocol. ## Default SVR Activation @@ -6282,7 +6282,7 @@ Chainlink SVR is activated by default for certain Chainlink Data Feeds on certai Integrators should expect zero service disruption during activation, and no action is needed on their part. -On these networks, SVR feeds have an **SVR** label on the [Feed Addresses](/data-feeds/price-feeds/addresses?network=base\&showSvr=true) page and on [data.chain.link](https://data.chain.link/). Previously deployed SVR feeds remain available and function as expected, and can be identified with the **SVR-Backup** label. +On these networks, SVR feeds have an **SVR** label on the [Feed Addresses](/data-feeds/price-feeds/addresses?network=base\&showSvr=true) page and on [data.chain.link](https://data.chain.link/). Previously deployed SVR feeds remain available and function as expected, and can be identified by their variant label. For any questions, contact [chainlink_data_feeds@smartcontract.com](mailto:chainlink_data_feeds@smartcontract.com). Protocols can also opt out at any time by reading from the "Standard" proxy that complements every SVR proxy for the relevant feed. @@ -6325,10 +6325,10 @@ This code sample is essentially the same as reading from a standard Chainlink Pr Chainlink SVR supports two auction paths depending on the network: -| Network | Auction System | Guide | -| -------------------------------- | ------------------- | ---------------------------------------------------------------------------------------------------------------- | -| Ethereum Mainnet | Flashbots MEV-Share | [Searcher Onboarding: Ethereum Mainnet](/data-feeds/svr-feeds/searcher-onboarding-ethereum) | -| Base, Arbitrum, BNB Chain, Monad | Atlas | [Searcher Onboarding: Atlas (Base, Arbitrum, BNB Chain, Monad)](/data-feeds/svr-feeds/searcher-onboarding-atlas) | +| Network | Auction System | Guide | +| ------------------------------------------------------------------------------- | ------------------- | ------------------------------------------------------------------------------------------- | +| Ethereum Mainnet | Flashbots MEV-Share | [Searcher Onboarding: Ethereum Mainnet](/data-feeds/svr-feeds/searcher-onboarding-ethereum) | +| Base, Arbitrum, BNB Chain, Monad, Arc, HyperEVM, Ink, Robinhood Chain, Unichain | Atlas | [Searcher Onboarding: Atlas](/data-feeds/svr-feeds/searcher-onboarding-atlas) | ## Economics and Revenue Split @@ -6368,7 +6368,9 @@ Source: https://docs.chain.link/data-feeds/svr-feeds/searcher-onboarding-atlas > > Searchers can [subscribe here](https://chainlinkcommunity.typeform.com/to/s55Hu2tK) to receive timely Chainlink SVR updates. -[Chainlink Smart Value Recapture (SVR)](/data-feeds/svr-feeds) has expanded to Base, Arbitrum, BNB Chain, and Monad with a new auction system, **Atlas**. This guide provides existing Chainlink SVR searchers with the technical specifications and integration requirements for participating in all non-ETH mainnet SVR auctions. +[Chainlink Smart Value Recapture (SVR)](/data-feeds/svr-feeds) has expanded beyond Ethereum Mainnet with a new auction system, **Atlas**. This guide provides existing Chainlink SVR searchers with the technical specifications and integration requirements for participating in all non-ETH mainnet SVR auctions. + +Atlas is currently live on the following networks: Base, Arbitrum, BNB Chain, Monad, Arc, HyperEVM, Ink, Robinhood Chain, and Unichain. See [Contract References](#contract-references) for the deployed addresses. ## Core Components @@ -6395,43 +6397,10 @@ Recommended native token amounts for bonding: ## Contract References -The following contracts are deployed at version **v1.6.4**. - -### Base - -Contract -Address - -Atlas (v1.6.4) - -DappControl - -### Arbitrum - -Contract -Address - -Atlas (v1.6.4) - -DappControl - -### BNB Chain - -Contract -Address - -Atlas (v1.6.4) - -DappControl - -### Monad - -Contract -Address - -Atlas (v1.6.4) +Each chain has a live Atlas deployment and a live DappControl: -DappControl +- **Atlas**: The auction contract, deployed at version **v1.6.4**. Searchers bond native tokens here to cover the gas costs of their operation and the oracle update, and call it to unbond and redeem. +- **DappControl**: The dApp-specific module that defines the auction rules for Chainlink SVR. Searchers read parameters such as the solver gas limit from it, and filter incoming user operations by its address. ## Feed Aggregator Addresses @@ -6439,9 +6408,8 @@ DappControl > > Searchers need to listen to the **Aggregator address** for price reports, not the proxy address. You can find the > aggregator address by reading `aggregator` in the proxy contract. For convenience, we've included the aggregator -> address in the table below. Use **Aave-SVR** type feeds for Aave, **SVR** type feeds for other protocols, and -> **SVR-Backup** for the legacy shared feeds. For the full list of SVR feeds, see the [SVR Feeds -> page](/data-feeds/svr-feeds). +> address in the table below. Each feed is labeled with its SVR variant (for example, **Shared SVR**, **Aave SVR**, or +> **Compound SVR**). For the full list of SVR feeds, see the [SVR Feeds page](/data-feeds/svr-feeds). ## Guide @@ -6640,7 +6608,7 @@ interface IAtlas { function depositAndBond(uint256 amountToBond) external payable; } -address atlasAddress = address(0x8e098Dfd60aEC9bCf07fd3cA5933e9F22b1b4A0d); +address atlasAddress = address(0x583dcFef0D240DC80753F0F0B26513feE27D9B77); uint256 amountToBond = 1e18; IAtlas(atlasAddress).depositAndBond{ value: amountToBond }(amountToBond); @@ -6664,7 +6632,7 @@ interface IAtlas { function ESCROW_DURATION() external view returns (uint256); } -address atlasAddress = address(0x8e098Dfd60aEC9bCf07fd3cA5933e9F22b1b4A0d); +address atlasAddress = address(0x583dcFef0D240DC80753F0F0B26513feE27D9B77); uint256 amountToUnbond = 1e18; IAtlas(atlasAddress).unbond(amountToUnbond); @@ -7027,7 +6995,7 @@ var ( chainId = big.NewInt(8453) // Filtering notifications for the Chainlink dApp control (module) address only - dAppControlAddress = common.HexToAddress("0xe15BBa987C002ecc3586e81244517877D294d291") + dAppControlAddress = common.HexToAddress("0xa5E1a36938769cbd5a26f5e19D8FCB379f597c83") ) // This function returns true if the notification is of interest and false @@ -7210,7 +7178,7 @@ This guide explains how to identify and bid on **Chainlink SVR** liquidation opp through **Flashbots MEV-Share**. SVR Feeds support a private transmission channel for oracle price updates, enabling searchers to backrun new prices with liquidation transactions and compete for profit. -For Base, Arbitrum, and BNB Chain, see the [Atlas searcher onboarding guide](/data-feeds/svr-feeds/searcher-onboarding-atlas). +For all other supported networks, see the [Atlas searcher onboarding guide](/data-feeds/svr-feeds/searcher-onboarding-atlas). ### Why Chainlink SVR? @@ -7232,9 +7200,8 @@ For more background on how Aave liquidations work, see [Aave Documentation](http > > Searchers need to listen to the **Aggregator address** for price reports, not the proxy address. You can find the > aggregator address by reading `aggregator` in the proxy contract. For convenience, we've included the aggregator -> address in the table below. Use **Aave-SVR** type feeds for Aave, **SVR** type feeds for other protocols, and -> **SVR-Backup** for the legacy shared feeds. For the full list of SVR feeds, see the [SVR Feeds -> page](/data-feeds/svr-feeds). +> address in the table below. Each feed is labeled with its SVR variant (for example, **Shared SVR**, **Aave SVR**, or +> **Compound SVR**). For the full list of SVR feeds, see the [SVR Feeds page](/data-feeds/svr-feeds). ## How to Participate as a Searcher diff --git a/src/content/data-feeds/svr-feeds/index.mdx b/src/content/data-feeds/svr-feeds/index.mdx index baa5bc21970..49e9d22f9fe 100644 --- a/src/content/data-feeds/svr-feeds/index.mdx +++ b/src/content/data-feeds/svr-feeds/index.mdx @@ -6,7 +6,7 @@ isIndex: true whatsnext: { "SVR Searcher Onboarding: Ethereum Mainnet": "/data-feeds/svr-feeds/searcher-onboarding-ethereum", - "SVR Searcher Onboarding: Atlas (Base, Arbitrum, BNB Chain)": "/data-feeds/svr-feeds/searcher-onboarding-atlas", + "SVR Searcher Onboarding: Atlas": "/data-feeds/svr-feeds/searcher-onboarding-atlas", "Learn how to read answers from Data Feeds": "/data-feeds/price-feeds", "Find contract addresses for Price Feeds": "/data-feeds/price-feeds/addresses", "Data Feeds API Reference": "/data-feeds/api-reference", @@ -28,10 +28,10 @@ import { Aside, ClickToZoom } from "@components"

- diff --git a/src/features/data/api/index.ts b/src/features/data/api/index.ts index e87356cc363..ef8c7cc7db0 100644 --- a/src/features/data/api/index.ts +++ b/src/features/data/api/index.ts @@ -47,6 +47,7 @@ export interface ChainMetadata { path: string proxyAddress: null | string secondaryProxyAddress?: string + svrDisplayLabel?: string threshold: number valuePrefix: string valueSuffix?: string diff --git a/src/features/data/chains.ts b/src/features/data/chains.ts index a36e7312aef..3eaa5b0d692 100644 --- a/src/features/data/chains.ts +++ b/src/features/data/chains.ts @@ -113,7 +113,7 @@ export const CHAINS: Chain[] = [ title: "Arc Data Feeds", img: "/assets/chains/arc.svg", networkStatusUrl: "https://status.arc.network/", - tags: ["default", "smartData", "extendedHours"], + tags: ["default", "smartData", "extendedHours", "svrAtlas"], supportedFeatures: ["feeds"], networks: [ { @@ -357,7 +357,7 @@ export const CHAINS: Chain[] = [ title: "HyperEVM Data Feeds", img: "/assets/chains/hyperevm.svg", networkStatusUrl: "https://hyperevmscan.statuspage.io/", - tags: ["default", "smartData"], + tags: ["default", "smartData", "svrAtlas"], supportedFeatures: ["feeds"], networks: [ { @@ -376,7 +376,7 @@ export const CHAINS: Chain[] = [ title: "Ink Data Feeds", img: "/assets/chains/ink.svg", networkStatusUrl: "https://status.inkonchain.com/", - tags: ["default"], + tags: ["default", "svrAtlas"], supportedFeatures: ["feeds"], networks: [ { @@ -626,7 +626,7 @@ export const CHAINS: Chain[] = [ title: "Robinhood Chain Data Feeds", img: "/assets/chains/robinhood-chain.svg", networkStatusUrl: "https://status.robinhoodchain.offchain.io/", - tags: ["default", "tokenizedEquity"], + tags: ["default", "tokenizedEquity", "svrAtlas"], supportedFeatures: ["feeds"], networks: [ { @@ -867,7 +867,7 @@ export const CHAINS: Chain[] = [ title: "Unichain Data Feeds", img: "/assets/chains/unichain.svg", networkStatusUrl: "https://status.unichain.org/", - tags: ["default"], + tags: ["default", "svrAtlas"], supportedFeatures: ["feeds"], networks: [ { diff --git a/src/features/feeds/components/AtlasContractsTable.module.css b/src/features/feeds/components/AtlasContractsTable.module.css new file mode 100644 index 00000000000..2a6ff44dfa8 --- /dev/null +++ b/src/features/feeds/components/AtlasContractsTable.module.css @@ -0,0 +1,14 @@ +/* Overrides the nth-child widths from Tables.module.css. The doubled class + raises specificity above those rules without referencing a foreign class. */ +.atlasTable.atlasTable th:nth-child(1), +.atlasTable.atlasTable td:nth-child(1) { + width: 1%; + white-space: nowrap; +} + +.atlasTable.atlasTable th:nth-child(2), +.atlasTable.atlasTable td:nth-child(2), +.atlasTable.atlasTable th:nth-child(3), +.atlasTable.atlasTable td:nth-child(3) { + width: 50%; +} diff --git a/src/features/feeds/components/AtlasContractsTable.tsx b/src/features/feeds/components/AtlasContractsTable.tsx new file mode 100644 index 00000000000..cd2fc880a13 --- /dev/null +++ b/src/features/feeds/components/AtlasContractsTable.tsx @@ -0,0 +1,63 @@ +/** @jsxImportSource preact */ +import { clsx } from "~/lib/clsx/clsx.ts" +import tableStyles from "./Tables.module.css" +import atlasStyles from "./AtlasContractsTable.module.css" +import { ATLAS_CONTRACTS } from "../utils/atlasContracts.ts" + +const ATLAS_CHAINS: { label: string; key: string }[] = [ + { label: "Arbitrum", key: "arbitrum" }, + { label: "Arc", key: "arc" }, + { label: "Base", key: "base" }, + { label: "BNB Chain", key: "bnb-chain" }, + { label: "HyperEVM", key: "hyperevm" }, + { label: "Ink", key: "ink" }, + { label: "Monad", key: "monad" }, + { label: "Robinhood Chain", key: "robinhood" }, + { label: "Unichain", key: "unichain" }, +] + +export const AddressCell = ({ address, explorerUrl }: { address: string; explorerUrl: string }) => ( +
+ + + {address} + +
+) + +export const AtlasContractsTable = () => ( +
+ + + + + + + + + + {ATLAS_CHAINS.map(({ label, key }) => { + const contracts = ATLAS_CONTRACTS[key] + if (!contracts) return null + return ( + + + + + + ) + })} + +
NetworkAtlas (v1.6.4)DappControl
{label} + + + +
+
+) diff --git a/src/features/feeds/components/FeedList.tsx b/src/features/feeds/components/FeedList.tsx index 82592f0d0f7..3a3fde2334f 100644 --- a/src/features/feeds/components/FeedList.tsx +++ b/src/features/feeds/components/FeedList.tsx @@ -19,7 +19,9 @@ import { networkHasVisibleFeeds, type ExtendedHoursCategory, } from "../utils/feedVisibility.ts" -import { chainHasSvrFeeds, getSvrType, type SvrFeedType } from "../utils/svrDetection.ts" +import { chainHasSvrFeeds, getSvrTypesOnNetwork, getSvrTypeDocLink } from "../utils/svrDetection.ts" +import { getAtlasContracts } from "../utils/atlasContracts.ts" +import { AddressCell } from "./AtlasContractsTable.tsx" import { filterChainsByFeedTypeTag, networkMatchesFeedTypeTag, @@ -354,7 +356,7 @@ export const FeedList = ({ const [showOnlyDEXFeeds, setShowOnlyDEXFeeds] = useState(false) const [showOnlyDEXFeedsTestnet, setShowOnlyDEXFeedsTestnet] = useState(false) // SVR type filters (only used when dataFeedType === "svr") - const [svrTypeFilters, setSvrTypeFilters] = useState>(new Set()) + const [svrTypeFilters, setSvrTypeFilters] = useState>(new Set()) const [showOnlyDatalinkFeeds, setShowOnlyDatalinkFeeds] = useState(false) const [showOnlyDatalinkFeedsTestnet, setShowOnlyDatalinkFeedsTestnet] = useState(false) const [show24x5FeedsParam, setShow24x5FeedsParam] = useQueryString("show24x5") @@ -545,6 +547,20 @@ export const FeedList = ({ forceExtendedHoursCategory, ]) + const svrTypesOnNetwork = useMemo(() => { + if (!isSvr || !currentChainMetadata.processedData) return [] + const labels = new Set() + currentChainMetadata.processedData.networks?.forEach((network) => { + getSvrTypesOnNetwork(network).forEach((label) => labels.add(label)) + }) + return [...labels].sort() + }, [isSvr, currentChainMetadata.processedData]) + + const atlasContracts = useMemo( + () => (isSvr ? getAtlasContracts(selectedChain.page) : null), + [isSvr, selectedChain.page] + ) + useEffect(() => { if (!chainHasSvr && showOnlySVR) { setShowOnlySVR(false) @@ -1759,6 +1775,36 @@ export const FeedList = ({ examples.

)} + {isSvr && atlasContracts && ( + <> +

+ Searchers bond native tokens to the Atlas contract and query the{" "} + DappControl contract for auction parameters such as the solver gas limit. +

+ + + + + + + + + + + + + + + + + +
ContractAddress
Atlas (v1.6.4) + +
DappControl + +
+ + )} {network.name === "Aptos Mainnet" && ( <>

@@ -1930,91 +1976,36 @@ export const FeedList = ({ )} - {isSvr && ( - <> - - - - ? - - - - - - ? - - - + {isSvr && + svrTypesOnNetwork.map((svrType) => ( + ? - - )} + ))}

- {getSvrType(metadata, network)} + {getSvrType(metadata)} )} @@ -695,7 +695,7 @@ const DefaultTr = ({
- {getSvrType(metadata, network)} Proxy: + {getSvrType(metadata)} Proxy:
{hideAddress ? ( @@ -728,30 +728,21 @@ const DefaultTr = ({ )}
- {getSvrType(metadata, network) === "Aave-SVR" && !hideAddress && ( + {getSvrType(metadata) === "Aave SVR" && !hideAddress && (
⚠️ Aave Dedicated Feed: This SVR proxy feed is dedicated exclusively for use by the Aave protocol. Learn more about{" "} - + Aave SVR Feeds .
)} - {getSvrType(metadata, network) === "SVR" && !hideAddress && ( + {getSvrType(metadata) !== "Aave SVR" && !hideAddress && (
- 🔗 SVR Feed: This SVR proxy feed is usable by any protocol. Learn more about{" "} - - SVR Feeds - - . -
- )} - {getSvrType(metadata, network) === "SVR-Backup" && !hideAddress && ( -
- 🔗 SVR-Backup Feed: This is a legacy SVR proxy feed. New integrations should use - the SVR feeds. Learn more about{" "} - + 🔗 {getSvrType(metadata)} Feed: This SVR proxy feed is usable by any protocol. + Learn more about{" "} + SVR Feeds . @@ -1680,7 +1671,7 @@ export const MainnetTable = ({ tokenizedEquityProvider?: string forceExtendedHoursCategory?: ExtendedHoursCategory isSvr?: boolean - svrTypeFilters?: Set + svrTypeFilters?: Set }) => { if (!network.metadata) return null @@ -1714,10 +1705,10 @@ export const MainnetTable = ({ const typeFilteredMetadata = useMemo(() => { if (!isSvr || !svrTypeFilters || svrTypeFilters.size === 0) return filteredMetadata return filteredMetadata.filter((m) => { - const svrType = getSvrType(m, network) + const svrType = getSvrType(m) return svrType && !svrTypeFilters.has(svrType) }) - }, [filteredMetadata, isSvr, svrTypeFilters, network]) + }, [filteredMetadata, isSvr, svrTypeFilters]) const slicedFilteredMetadata = typeFilteredMetadata.slice(firstAddr, lastAddr) diff --git a/src/features/feeds/utils/atlasContracts.ts b/src/features/feeds/utils/atlasContracts.ts new file mode 100644 index 00000000000..8e67b559a5c --- /dev/null +++ b/src/features/feeds/utils/atlasContracts.ts @@ -0,0 +1,65 @@ +/** + * Atlas + DappControl addresses for SVR auctions, keyed by chain page. + * Sourced from the reference-data-directory `dAppControl` entries, where + * `destination` is the Atlas contract. Tempo is absent: it has SVR but no Atlas. + */ +export interface AtlasContracts { + atlas: string + dappControl: string + /** Block explorer URL template; `%s` is replaced with the address. */ + explorerUrl: string +} + +export const ATLAS_CONTRACTS: Record = { + base: { + atlas: "0x583dcFef0D240DC80753F0F0B26513feE27D9B77", + dappControl: "0xa5E1a36938769cbd5a26f5e19D8FCB379f597c83", + explorerUrl: "https://basescan.org/address/%s", + }, + arbitrum: { + atlas: "0x8ad1aE9D97C79aA68A0a151E83ff3942f68F86C1", + dappControl: "0xe15BBa987C002ecc3586e81244517877D294d291", + explorerUrl: "https://arbiscan.io/address/%s", + }, + "bnb-chain": { + atlas: "0x21B7d28B882772A1Cfe633Daee6f42ebb95DeC4E", + dappControl: "0x7D50b32444609A9B53BcF208c159C8d0d0767835", + explorerUrl: "https://bscscan.com/address/%s", + }, + monad: { + atlas: "0x2DA28fedc4643c787CB5c5e84fa6AaDb596875E8", + dappControl: "0xa40d9f38621b4ffb2181508b973519e8133951c0", + explorerUrl: "https://monadvision.com/address/%s", + }, + arc: { + atlas: "0x3b7B38362bB7E2F000Cd2432343F3483F785F435", + dappControl: "0x5210729C598746F2bD7A22d5B66A7cDE72f462E7", + explorerUrl: "https://explorer.arc.io/address/%s", + }, + hyperevm: { + atlas: "0x137B8Fdf027598f1Bf5F2d60cB081bD8539BD8c7", + dappControl: "0x2ac121f89b39BA231CBF8Abf7aC6D0140f6B8821", + explorerUrl: "https://hyperevmscan.io/address/%s", + }, + ink: { + atlas: "0x3efbaBE0ee916A4677D281c417E895a3e7411Ac2", + dappControl: "0xB0B992eaed9CbB37667E25D9C5214CDEB406Bd13", + explorerUrl: "https://explorer.inkonchain.com/address/%s", + }, + robinhood: { + atlas: "0x3efbaBE0ee916A4677D281c417E895a3e7411Ac2", + dappControl: "0xC9eACB1CF3bBe2874ccF5d25737c418Cfb944340", + explorerUrl: "https://robinhoodchain.blockscout.com/address/%s", + }, + unichain: { + atlas: "0xbAf4fBB4FD65199D5795Ee4990B9661387ce3B6e", + dappControl: "0x763D32a9EDf2F1684F1Dee50AB6CcCa5b68EF655", + explorerUrl: "https://uniscan.xyz/address/%s", + }, +} + +/** Returns the Atlas contracts for a chain page, or null when Atlas is not deployed. */ +export function getAtlasContracts(chainPage?: string): AtlasContracts | null { + if (!chainPage) return null + return ATLAS_CONTRACTS[chainPage] ?? null +} diff --git a/src/features/feeds/utils/svrDetection.ts b/src/features/feeds/utils/svrDetection.ts index 74580d2fd81..26f6c1b0125 100644 --- a/src/features/feeds/utils/svrDetection.ts +++ b/src/features/feeds/utils/svrDetection.ts @@ -2,20 +2,34 @@ import { type ChainMetadata } from "~/features/data/api/index.ts" import type { DataFeedType } from "../types.ts" import { type FeedVisibilityOptions, isFeedVisible } from "./feedVisibility.ts" -// This file contains functions to detect and classify SVR feeds based on their metadata. -// SVR feeds are identified by the presence of a secondaryProxyAddress. -// -// Classification (based on path suffix): -// *-svr (no "shared") → Aave-SVR (dedicated to Aave) -// *-shared-svr-2 → SVR (new shared, canonical) -// *-shared-svr (no "-2") → SVR-Backup (legacy shared) - -export type SvrFeedType = "Aave-SVR" | "SVR" | "SVR-Backup" - +// SVR feeds are identified by the presence of a `secondaryProxyAddress`. export function isSvrFeed(metadata: ChainMetadata): boolean { return !!metadata?.secondaryProxyAddress } +/** Fallback label used when the RDD does not provide `svrDisplayLabel`. */ +export const DEFAULT_SVR_LABEL = "SVR" + +/** Returns the SVR variant label for a feed, or `null` if it is not an SVR feed. */ +export function getSvrType(metadata: ChainMetadata): string | null { + if (!metadata?.secondaryProxyAddress) return null + return metadata.svrDisplayLabel || DEFAULT_SVR_LABEL +} + +/** Returns the distinct SVR variant labels present on a network, sorted. */ +export function getSvrTypesOnNetwork( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + network: any +): string[] { + const labels = new Set() + // eslint-disable-next-line @typescript-eslint/no-explicit-any + network?.metadata?.forEach((feed: any) => { + const label = getSvrType(feed) + if (label) labels.add(label) + }) + return [...labels].sort() +} + export function networkHasSvrFeeds( // eslint-disable-next-line @typescript-eslint/no-explicit-any network: any, @@ -38,68 +52,20 @@ export function chainHasSvrFeeds( ecosystem = "", options: FeedVisibilityOptions = {} ): boolean { - return chain?.networks?.some((network: any) => networkHasSvrFeeds(network, dataFeedType, ecosystem, options)) ?? false -} - -/** - * Determines if a feed is a legacy shared SVR feed (SVR-Backup). - * Path ends with "-shared-svr" but NOT "-shared-svr-2". - */ -export const isSharedSVR = (metadata: ChainMetadata): boolean => { - return typeof metadata.path === "string" && /-shared-svr$/.test(metadata.path) -} - -/** - * Determines if a feed is a new shared SVR feed (canonical SVR). - * Path ends with "-shared-svr-2". - */ -export const isNewSharedSVR = (metadata: ChainMetadata): boolean => { - return typeof metadata.path === "string" && /-shared-svr-2$/.test(metadata.path) -} - -/** - * Determines if a feed is an Aave dedicated SVR feed. - * Has a secondary proxy address but is neither a shared nor new-shared SVR feed. - */ -export const isAaveSVR = (metadata: ChainMetadata): boolean => { - return !!metadata?.secondaryProxyAddress && !isSharedSVR(metadata) && !isNewSharedSVR(metadata) -} - -/** - * Determines whether a network has at least one new shared SVR feed - * (path ends with "-shared-svr-2"). When a network has both versions, - * the legacy "-shared-svr" feeds are considered backups. - */ -export const networkHasNewSharedSVR = ( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - network: any -): boolean => { return ( - network?.metadata?.some( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (feed: any) => isNewSharedSVR(feed) - ) ?? false + // eslint-disable-next-line @typescript-eslint/no-explicit-any + chain?.networks?.some((network: any) => networkHasSvrFeeds(network, dataFeedType, ecosystem, options)) ?? false ) } -/** - * Returns the SVR feed type label for a given feed metadata. - * - * Classification is network-aware: a "-shared-svr" feed is only labeled - * "SVR-Backup" when the network also has a "-shared-svr-2" feed. On networks - * that only have "-shared-svr" feeds (e.g. BNB, Arbitrum, Monad), those feeds - * are the canonical SVR feeds and are labeled "SVR". - */ -export const getSvrType = ( - metadata: ChainMetadata, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - network?: any -): SvrFeedType | null => { - if (!metadata?.secondaryProxyAddress) return null - if (isNewSharedSVR(metadata)) return "SVR" - if (isSharedSVR(metadata)) { - // Only treat as backup when the network also has a new shared SVR feed. - return networkHasNewSharedSVR(network) ? "SVR-Backup" : "SVR" +/** Docs anchor for an SVR variant label; unknown labels fall back to the overview. */ +export function getSvrTypeDocLink(label: string): string { + switch (label) { + case "Aave SVR": + return "/data-feeds/svr-feeds#aave-svr-feeds" + case "Shared SVR": + return "/data-feeds/svr-feeds#svr-shared" + default: + return "/data-feeds/svr-feeds" } - return "Aave-SVR" }