diff --git a/src/config/sidebar.ts b/src/config/sidebar.ts index d47bf0f9a8f..3285159b8c9 100644 --- a/src/config/sidebar.ts +++ b/src/config/sidebar.ts @@ -1171,6 +1171,10 @@ export const SIDEBAR: Partial> = { title: "Cryptocurrency", url: "data-streams/crypto-streams", children: [ + { + title: "Report Schema v2 (Crypto Standard)", + url: "data-streams/reference/report-schema-v2", + }, { title: "Report Schema v3 (Crypto Advanced)", url: "data-streams/reference/report-schema-v3", diff --git a/src/content/data-streams/llms-full.txt b/src/content/data-streams/llms-full.txt index 24de670fa17..2d31d8fdee9 100644 --- a/src/content/data-streams/llms-full.txt +++ b/src/content/data-streams/llms-full.txt @@ -5559,9 +5559,14 @@ Each stream category uses one or more report schemas to deliver data. Several ca Cryptocurrency streams deliver high-frequency market data for digital assets. +- [**Crypto Standard (v2)**](#crypto-standard-v2) provides a single consensus price point for crypto assets. - [**Crypto Advanced (v3)**](#crypto-advanced-v3) aggregate order-book liquidity across top venues to provide a consensus mid price alongside simulated bid and ask levels. - [**Crypto Advanced (v3) - DEX State Price**](#crypto-advanced-v3---dex-state-price) derive pricing from onchain liquidity pools to reflect decentralized market conditions. +### Crypto Standard (v2) + +[Chainlink Cryptocurrency Reports](/data-streams/reference/report-schema-v2) provide market data for digital assets using a streamlined schema. Each report includes a single time-weighted average price (`price`), making it suitable for use cases that require a TWAP price point. + ### Crypto Advanced (v3) [Chainlink Cryptocurrency Reports](/data-streams/reference/report-schema-v3) provide market data for digital assets, supporting high-frequency, onchain use cases. Each report includes a consensus mid price (`price`), as well as simulated bid (`bid`) and ask (`ask`) prices that estimate the impact of buying or selling at a specified liquidity depth. These values help protocols and applications understand current market conditions and potential slippage for larger trades. @@ -5758,6 +5763,33 @@ As a result, these volume fields should not be treated as a proxy for total mark --- +# Report Schema: Crypto Standard (v2) +Source: https://docs.chain.link/data-streams/reference/report-schema-v2 + + + +Cryptocurrency streams adhere to the report schema outlined below. + +## Time-Weighted Average Price (TWAP) + +The v2 schema is used by TWAP streams. The `price` field carries the time-weighted average price for the stream's configured window, not a spot or median price. Each TWAP stream is scoped to one asset and one window length (for example, 30 seconds or 60 seconds). The window length is part of the stream's name and metadata. + +## Schema Fields + +| Field | Type | Description | +| ----------------------- | --------- | ---------------------------------------------------------- | +| `feedId` | `bytes32` | Unique identifier for the Data Streams feed | +| `validFromTimestamp` | `uint32` | Earliest timestamp when the price is valid (seconds) | +| `observationsTimestamp` | `uint32` | Latest timestamp when the price is valid (seconds) | +| `nativeFee` | `uint192` | Cost to verify report onchain (native token) | +| `linkFee` | `uint192` | Cost to verify report onchain (LINK) | +| `expiresAt` | `uint32` | Expiration date of the report (seconds) | +| `price` | `int192` | Time-weighted average price (TWAP) for the stream's window | + +**Note**: Future Cryptocurrency streams may use different report schemas. + +--- + # Report Schema: Crypto Advanced (v3) - DEX State Price Source: https://docs.chain.link/data-streams/reference/report-schema-v3-dex diff --git a/src/content/data-streams/reference/report-schema-overview.mdx b/src/content/data-streams/reference/report-schema-overview.mdx index 5fa73315609..7019b912598 100644 --- a/src/content/data-streams/reference/report-schema-overview.mdx +++ b/src/content/data-streams/reference/report-schema-overview.mdx @@ -26,9 +26,18 @@ Each stream category uses one or more report schemas to deliver data. Several ca - + Cryptocurrency + + Crypto Standard (v2) + + Crypto prices (single price point) + + price + + + Crypto Advanced (v3) @@ -113,9 +122,14 @@ Each stream category uses one or more report schemas to deliver data. Several ca Cryptocurrency streams deliver high-frequency market data for digital assets. +- [**Crypto Standard (v2)**](#crypto-standard-v2) provides a single consensus price point for crypto assets. - [**Crypto Advanced (v3)**](#crypto-advanced-v3) aggregate order-book liquidity across top venues to provide a consensus mid price alongside simulated bid and ask levels. - [**Crypto Advanced (v3) - DEX State Price**](#crypto-advanced-v3---dex-state-price) derive pricing from onchain liquidity pools to reflect decentralized market conditions. +### Crypto Standard (v2) + +[Chainlink Cryptocurrency Reports](/data-streams/reference/report-schema-v2) provide market data for digital assets using a streamlined schema. Each report includes a single time-weighted average price (`price`), making it suitable for use cases that require a TWAP price point. + ### Crypto Advanced (v3) [Chainlink Cryptocurrency Reports](/data-streams/reference/report-schema-v3) provide market data for digital assets, supporting high-frequency, onchain use cases. Each report includes a consensus mid price (`price`), as well as simulated bid (`bid`) and ask (`ask`) prices that estimate the impact of buying or selling at a specified liquidity depth. These values help protocols and applications understand current market conditions and potential slippage for larger trades. diff --git a/src/content/data-streams/reference/report-schema-v2.mdx b/src/content/data-streams/reference/report-schema-v2.mdx new file mode 100644 index 00000000000..de1d1debc0a --- /dev/null +++ b/src/content/data-streams/reference/report-schema-v2.mdx @@ -0,0 +1,29 @@ +--- +section: dataStreams +date: Last Modified +title: "Report Schema: Crypto Standard (v2)" +metadata: + title: "Crypto Standard (v2) | Chainlink Data Streams" + description: "Learn about Chainlink Data Streams Crypto Standard (v2) schema, including fields, encoding, and examples to integrate market data into your applications." + keywords: ["Report Schema", "Cryptocurrency", "v2 Schema", "Data Format", "Report Structure", "Crypto Standard"] +--- + +import DataStreams from "@features/data-streams/common/DataStreams.astro" +import ReportSchemaTabs from "@features/data-streams/common/ReportSchemaTabs.astro" +import SchemaFieldsTable from "@features/data-streams/common/SchemaFieldsTable.astro" + + + + + +Cryptocurrency streams adhere to the report schema outlined below. + +## Time-Weighted Average Price (TWAP) + +The v2 schema is used by TWAP streams. The `price` field carries the time-weighted average price for the stream's configured window, not a spot or median price. Each TWAP stream is scoped to one asset and one window length (for example, 30 seconds or 60 seconds). The window length is part of the stream's name and metadata. + +## Schema Fields + + + +**Note**: Future Cryptocurrency streams may use different report schemas. diff --git a/src/features/data-streams/common/ReportSchemaTabs.astro b/src/features/data-streams/common/ReportSchemaTabs.astro index d3b6101ffe7..ab92811480c 100644 --- a/src/features/data-streams/common/ReportSchemaTabs.astro +++ b/src/features/data-streams/common/ReportSchemaTabs.astro @@ -5,6 +5,10 @@ import { PageTabs } from "@components" const currentUrl = Astro.url.pathname const allReportSchemaPages = [ + { + name: "Crypto Standard (v2)", + url: "/data-streams/reference/report-schema-v2", + }, { name: "Crypto Advanced (v3)", url: "/data-streams/reference/report-schema-v3", diff --git a/src/features/feeds/components/FeedList.module.css b/src/features/feeds/components/FeedList.module.css index f49d0919330..595679da2a7 100644 --- a/src/features/feeds/components/FeedList.module.css +++ b/src/features/feeds/components/FeedList.module.css @@ -182,7 +182,7 @@ div.shutDate > hr { position: relative; cursor: pointer; width: auto; - min-width: 200px; + min-width: 250px; } .filterDropdown_search { @@ -316,7 +316,7 @@ div.shutDate > hr { background: var(--white); z-index: 1; margin-top: var(--space-2x); - min-width: 165px; + min-width: 250px; width: 100%; box-shadow: var(--shadow-mid-secondary); border: var(--border-width-primary) solid var(--color-border-primary); diff --git a/src/features/feeds/components/FeedList.tsx b/src/features/feeds/components/FeedList.tsx index dcebf53a87e..0a22000e630 100644 --- a/src/features/feeds/components/FeedList.tsx +++ b/src/features/feeds/components/FeedList.tsx @@ -61,6 +61,13 @@ const schemaFilterOptions: FilterOption[] = [ { label: "RWA Advanced (v11)", value: "v11" }, ] +const cryptoSchemaFilterOptions: FilterOption[] = [ + { label: "All", value: "all" }, + { label: "Crypto Standard (v2)", value: "v2" }, + { label: "Crypto Advanced (v3)", value: "v3" }, + { label: "Crypto Advanced DEX (v3)", value: "v3-dex" }, +] + const feedTypeFilterOptions: FilterOption[] = [ { label: "All", value: "all" }, { label: "Datalink Streams", value: "datalink" }, @@ -76,7 +83,7 @@ const tradingHoursFilterOptions: FilterOption[] = [ ] const isSchemaFilterValue = (value: unknown): value is SchemaFilterValue => - value === "all" || value === "v8" || value === "v11" + value === "all" || value === "v2" || value === "v3" || value === "v3-dex" || value === "v8" || value === "v11" const isStreamsRwaFeedTypeValue = (value: unknown): value is StreamsRwaFeedTypeValue => value === "all" || value === "datalink" || value === "equities" || value === "forex" const isTradingHoursFilterValue = (value: unknown): value is TradingHoursFilterValue => @@ -358,6 +365,14 @@ export const FeedList = ({ const setRwaSchemaFilter = (next: SchemaFilterValue) => { setRwaSchemaFilterParam(next === "all" ? [] : next) } + const [cryptoSchemaFilterParam, setCryptoSchemaFilterParam] = useQueryString("cryptoSchema") + const cryptoSchemaFilter = + typeof cryptoSchemaFilterParam === "string" && isSchemaFilterValue(cryptoSchemaFilterParam) + ? cryptoSchemaFilterParam + : "all" + const setCryptoSchemaFilter = (next: SchemaFilterValue) => { + setCryptoSchemaFilterParam(next === "all" ? [] : next) + } const [testnetRwaSchemaFilterParam, setTestnetRwaSchemaFilterParam] = useQueryString("testnetSchema") const testnetRwaSchemaFilter = typeof testnetRwaSchemaFilterParam === "string" && isSchemaFilterValue(testnetRwaSchemaFilterParam) @@ -366,6 +381,14 @@ export const FeedList = ({ const setTestnetRwaSchemaFilter = (next: SchemaFilterValue) => { setTestnetRwaSchemaFilterParam(next === "all" ? [] : next) } + const [testnetCryptoSchemaFilterParam, setTestnetCryptoSchemaFilterParam] = useQueryString("testnetCryptoSchema") + const testnetCryptoSchemaFilter = + typeof testnetCryptoSchemaFilterParam === "string" && isSchemaFilterValue(testnetCryptoSchemaFilterParam) + ? testnetCryptoSchemaFilterParam + : "all" + const setTestnetCryptoSchemaFilter = (next: SchemaFilterValue) => { + setTestnetCryptoSchemaFilterParam(next === "all" ? [] : next) + } const [show24x5FeedsTestnetParam, setShow24x5FeedsTestnetParam] = useQueryString("testnetShow24x5") const show24x5FeedsTestnet = force24x5Only || show24x5FeedsTestnetParam === "true" const showApacEquitiesFeedsTestnet = forceApacEquitiesOnly @@ -1241,36 +1264,36 @@ export const FeedList = ({
{dataFeedType === "streamsCrypto" && ( -
- - -
+ <> + handleDropdownToggle("main-crypto-schema", isOpen)} + onClose={closeAllDropdowns} + label="Filter schema" + options={cryptoSchemaFilterOptions} + value={cryptoSchemaFilter} + groupId="crypto-schema-main" + onSelect={(next) => { + setCryptoSchemaFilter(next) + setCurrentPage("1") + }} + /> +
+ +
+ )} {dataFeedType === "streamsRwa" && ( <> @@ -1343,6 +1366,7 @@ export const FeedList = ({ )} {(searchValue || rwaSchemaFilter !== "all" || + cryptoSchemaFilter !== "all" || (!forceStreamCategoryFilter && streamCategoryFilter !== "all") || show24x5Feeds) && (
) : null} + {schemaKey === "v2" && + metadata.docs?.attributeType === "TWAP" && + (() => { + const twapWindow = getTwapWindowSeconds(metadata) + if (!twapWindow) return null + return ( +
+
+ TWAP window: +
+
{twapWindow} seconds
+
+ ) + })()} {streamsCategoryMap[metadata.feedCategory] ? (
@@ -1559,7 +1597,6 @@ export const StreamsTr = ({ metadata, isMainnet, showRiskColumn = isMainnet }) =
) : null} {(() => { - const schemaKey = getSchemaDefinitionKey(metadata) const schemaDef = schemaKey ? REPORT_SCHEMA_DEFINITIONS[schemaKey] : undefined if (!schemaDef || !schemaKey) return null return @@ -1579,6 +1616,7 @@ export const MainnetTable = ({ showOnlyDEXFeeds, showOnlyDatalinkFeeds, rwaSchemaFilter, + cryptoSchemaFilter, streamCategoryFilter, show24x5Feeds, showApacEquitiesFeeds, @@ -1600,7 +1638,8 @@ export const MainnetTable = ({ showOnlyMVRFeeds: boolean showOnlyDEXFeeds: boolean showOnlyDatalinkFeeds?: boolean - rwaSchemaFilter?: "all" | "v8" | "v11" + rwaSchemaFilter?: SchemaFilterValue + cryptoSchemaFilter?: SchemaFilterValue streamCategoryFilter?: "all" | "datalink" | "equities" | "forex" show24x5Feeds?: boolean showApacEquitiesFeeds?: boolean @@ -1637,6 +1676,7 @@ export const MainnetTable = ({ showOnlyDatalinkFeeds, streamCategoryFilter, rwaSchemaFilter, + cryptoSchemaFilter, showOnlyMVRFeeds, tokenizedEquityProvider, }, @@ -1736,6 +1776,7 @@ export const TestnetTable = ({ showOnlyDEXFeeds, showOnlyDatalinkFeeds, rwaSchemaFilter, + cryptoSchemaFilter, streamCategoryFilter, show24x5Feeds, showApacEquitiesFeeds, @@ -1756,7 +1797,8 @@ export const TestnetTable = ({ showOnlyMVRFeeds?: boolean showOnlyDEXFeeds?: boolean showOnlyDatalinkFeeds?: boolean - rwaSchemaFilter?: "all" | "v8" | "v11" + rwaSchemaFilter?: SchemaFilterValue + cryptoSchemaFilter?: SchemaFilterValue streamCategoryFilter?: "all" | "datalink" | "equities" | "forex" show24x5Feeds?: boolean showApacEquitiesFeeds?: boolean @@ -1788,6 +1830,7 @@ export const TestnetTable = ({ showOnlyDatalinkFeeds, streamCategoryFilter, rwaSchemaFilter, + cryptoSchemaFilter, showOnlyMVRFeeds, tokenizedEquityProvider, }, diff --git a/src/features/feeds/components/reportSchemaData.ts b/src/features/feeds/components/reportSchemaData.ts index d3e5594ec59..515642bb0fa 100644 --- a/src/features/feeds/components/reportSchemaData.ts +++ b/src/features/feeds/components/reportSchemaData.ts @@ -33,6 +33,15 @@ const COMMON_FIELDS: SchemaField[] = [ ] export const REPORT_SCHEMA_DEFINITIONS: Record = { + v2: { + label: "Report Schema v2 (Crypto Standard)", + shortLabel: "v2 (Crypto)", + url: "/data-streams/reference/report-schema-v2", + fields: [ + ...COMMON_FIELDS, + { field: "price", type: "int192", description: "Time-weighted average price (TWAP) for the stream's window" }, + ], + }, "v3-crypto": { label: "Report Schema v3 (Crypto Advanced)", shortLabel: "v3 (Crypto)", diff --git a/src/features/feeds/types.ts b/src/features/feeds/types.ts index 126782836bf..b74507a0002 100644 --- a/src/features/feeds/types.ts +++ b/src/features/feeds/types.ts @@ -18,7 +18,7 @@ export type DataFeedType = | "streamsExRate" | "streamsBacked" -export type SchemaFilterValue = "all" | "v8" | "v11" +export type SchemaFilterValue = "all" | "v2" | "v3" | "v3-dex" | "v8" | "v11" export type StreamsRwaFeedTypeValue = "all" | "datalink" | "equities" | "forex" export type TradingHoursFilterValue = "all" | "regular" | "extended" | "overnight" diff --git a/src/features/feeds/utils/feedMetadata.ts b/src/features/feeds/utils/feedMetadata.ts index 5c6e67319c6..a374f59f5cd 100644 --- a/src/features/feeds/utils/feedMetadata.ts +++ b/src/features/feeds/utils/feedMetadata.ts @@ -32,6 +32,8 @@ export function getSchemaVersion(feed: FeedMetadata): string | undefined { const match = clicProductName.match(/-0(\d{2})$/) if (!match) return undefined + if (match[1] === "02") return "v2" + if (match[1] === "03") return "v3" if (match[1] === "04" || match[1] === "08") return "v8" if (match[1] === "09") return "v9" if (match[1] === "11") return "v11" @@ -280,3 +282,26 @@ export function mergeStreamEnvironmentAvailability(feedAvailability: { mainnet: testnet: feedAvailability.testnet || verifierAvailability.testnet, } } + +/** Extract the TWAP window length (in seconds) from stream metadata, if present. */ +export function getTwapWindowSeconds(metadata: FeedMetadata): number | undefined { + const clicProductName = metadata.docs?.clicProductName + if (typeof clicProductName === "string") { + const match = clicProductName.match(/-TWAP-(\d+)s-/i) + if (match) { + const seconds = Number.parseInt(match[1], 10) + if (!Number.isNaN(seconds)) return seconds + } + } + + const name = metadata.name || metadata.docs?.clicProductName + if (typeof name === "string") { + const match = name.match(/-TWAP-(\d+)s/i) + if (match) { + const seconds = Number.parseInt(match[1], 10) + if (!Number.isNaN(seconds)) return seconds + } + } + + return undefined +} diff --git a/src/features/feeds/utils/feedVisibility.ts b/src/features/feeds/utils/feedVisibility.ts index 8c9f8286222..cc8646363d1 100644 --- a/src/features/feeds/utils/feedVisibility.ts +++ b/src/features/feeds/utils/feedVisibility.ts @@ -70,6 +70,7 @@ export interface FeedVisibilityOptions { showOnlyDatalinkFeeds?: boolean streamCategoryFilter?: string rwaSchemaFilter?: string + cryptoSchemaFilter?: string showOnlyMVRFeeds?: boolean tokenizedEquityProvider?: string } @@ -124,7 +125,17 @@ export function isFeedVisible( isVisible = dataFeedType === "streamsRwa" || (DATALINK_STREAM_MATCH[dataFeedType]?.(feed) ?? false) } else { if (dataFeedType === "streamsCrypto") { - isVisible = ["Crypto", "Crypto-DEX"].includes(feed.docs?.feedType) + const schemaVersion = getSchemaVersion(feed) + const feedType = feed.docs?.feedType + // Only show streams whose schema is explicitly v2 or v3 on the crypto page. + // Avoid falling back to feedType heuristics when the schema is missing or ambiguous. + if (schemaVersion === "v2" && feedType === "Crypto") { + isVisible = true + } else if (schemaVersion === "v3" && (feedType === "Crypto" || feedType === "Crypto-DEX")) { + isVisible = true + } else { + isVisible = false + } } else if (dataFeedType === "streamsRwa") { isVisible = ["Equities", "Forex"].includes(feed.docs?.feedType) } else if (dataFeedType === "streamsNav") { @@ -132,7 +143,14 @@ export function isFeedVisible( } else if (dataFeedType === "streamsExRate") { isVisible = feed.docs?.productTypeCode === "ExRate" } else if (dataFeedType === "streamsBacked") { - isVisible = feed.docs?.feedType === "Tokenized Equities" + const schemaVersion = getSchemaVersion(feed) + const feedType = feed.docs?.feedType + // Only show streams whose schema is explicitly v10 on the tokenized asset page. + if (schemaVersion === "v10" && feedType === "Tokenized Equities") { + isVisible = true + } else { + isVisible = false + } } } } else if (isSmartData) { @@ -190,6 +208,14 @@ export function isFeedVisible( if (options.rwaSchemaFilter === "v11" && schemaVersion !== "v11") return false } + if (dataFeedType === "streamsCrypto" && options.cryptoSchemaFilter && options.cryptoSchemaFilter !== "all") { + const schemaVersion = getSchemaVersion(feed) + const feedType = feed.docs?.feedType + if (options.cryptoSchemaFilter === "v2" && (schemaVersion !== "v2" || feedType !== "Crypto")) return false + if (options.cryptoSchemaFilter === "v3" && (schemaVersion !== "v3" || feedType !== "Crypto")) return false + if (options.cryptoSchemaFilter === "v3-dex" && (schemaVersion !== "v3" || feedType !== "Crypto-DEX")) return false + } + if (isSmartData && options.showOnlyMVRFeeds) { if (feed.docs?.isMVR !== true) return false }