Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
efa7b65
dynamic category sourcing: supabase
dev-dist Aug 6, 2025
8b7fc42
fix + tests
dev-dist Aug 7, 2025
d603690
more table updates
dev-dist Aug 8, 2025
a648f7e
batched feed approach
dev-dist Aug 8, 2025
7c8ba11
removed verbose logging
dev-dist Aug 8, 2025
7b1f327
updated key and test
dev-dist Aug 13, 2025
808d440
lint fix
dev-dist Aug 13, 2025
b51d49e
env naming
dev-dist Aug 13, 2025
444d022
removed tests
dev-dist Aug 26, 2025
fb9d61e
feedCategories refactor
dev-dist Aug 26, 2025
524439b
remove tests + refactor
dev-dist Aug 26, 2025
185b191
tables.tsx cleanup
dev-dist Aug 26, 2025
68aac93
remove tests
dev-dist Aug 26, 2025
8598023
remove feedenhancement
dev-dist Aug 26, 2025
562b2ba
aptos bugfix
dev-dist Aug 26, 2025
f8393ae
lint:fix
dev-dist Aug 26, 2025
075c0d0
remove more tests
dev-dist Aug 26, 2025
e771924
supbase fix
dev-dist Aug 26, 2025
aa24000
merge main
dev-dist Aug 29, 2025
098a424
restored deprecating tag
dev-dist Aug 29, 2025
fb78897
fix merge
dev-dist Aug 29, 2025
3a1fbdf
lint fix
dev-dist Aug 29, 2025
c2663f6
restore paginate noop
dev-dist Aug 29, 2025
56d28a8
nit
dev-dist Aug 29, 2025
8435d49
reverted unnecessary change
dev-dist Aug 29, 2025
95890b7
lint fix
dev-dist Aug 29, 2025
f63dd68
remove logging
dev-dist Aug 29, 2025
ea6ff37
astro version restore, remove typecheck test
dev-dist Aug 29, 2025
ee9ea19
update package.json
dev-dist Aug 29, 2025
120f0cd
remove more logging
dev-dist Aug 29, 2025
b83ba82
cut unused testing functions
dev-dist Aug 29, 2025
f5fa63e
merge main
dev-dist Sep 2, 2025
b2303b3
update categorziation logic to include hidden field
dev-dist Sep 2, 2025
ef8c631
lint fix
dev-dist Sep 2, 2025
f94d1d0
updated sorting
dev-dist Sep 2, 2025
e98c99c
Merge branch 'main' into tables/category-db-sourcing
dev-dist Sep 2, 2025
6128c86
Merge branch 'main' into tables/category-db-sourcing
dev-dist Sep 5, 2025
7582591
Merge branch 'main' into tables/category-db-sourcing
dev-dist Sep 8, 2025
e73781e
Merge branch 'main' of github.com:smartcontractkit/documentation into…
dev-dist Sep 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ server.log

# environment variables
.env
.env.local
.env.production

# macOS-specific files
Expand Down
82 changes: 82 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"@nanostores/preact": "^0.5.2",
"@nanostores/react": "^0.8.4",
"@openzeppelin/contracts": "^4.9.6",
"@supabase/supabase-js": "^2.53.0",
"astro": "^5.13.5",
"@solana-program/compute-budget": "^0.9.0",
"@solana-program/system": "^0.8.0",
Expand Down
177 changes: 177 additions & 0 deletions src/db/feedCategories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import { supabase } from "./supabase.js"

/* ===========================
Types
=========================== */

type FeedRiskRow = {
proxy_address: string
network: string
risk_status: string | null
}

export type FeedTierResult = { final: string | null }

/* ===========================
Category Config
=========================== */

export const FEED_CATEGORY_CONFIG = {
low: {
key: "low",
name: "Low Market Risk",
icon: "🟢",
title: "Low Market Risk - Feeds that deliver a market price for liquid assets with robust market structure.",
link: "/data-feeds/selecting-data-feeds#-low-market-risk-feeds",
},
medium: {
key: "medium",
name: "Medium Market Risk",
icon: "🟡",
title:
"Medium Market Risk - Feeds that deliver a market price for assets that show signs of liquidity-related risk or other market structure-related risk.",
link: "/data-feeds/selecting-data-feeds#-medium-market-risk-feeds",
},
high: {
key: "high",
name: "High Market Risk",
icon: "🔴",
title:
"High Market Risk - Feeds that deliver a heightened degree of some of the risk factors associated with Medium Market Risk Feeds, or a separate risk that makes the market price subject to uncertainty or volatile. In using a high market risk data feed you acknowledge that you understand the risks associated with such a feed and that you are solely responsible for monitoring and mitigating such risks.",
link: "/data-feeds/selecting-data-feeds#-high-market-risk-feeds",
},
new: {
key: "new",
name: "New Token",
icon: "🟠",
title:
"New Token - Tokens without the historical data required to implement a risk assessment framework may be launched in this category. Users must understand the additional market and volatility risks inherent with such assets. Users of New Token Feeds are responsible for independently verifying the liquidity and stability of the assets priced by feeds that they use.",
link: "/data-feeds/selecting-data-feeds#-new-token-feeds",
},
custom: {
key: "custom",
name: "Custom",
icon: "🔵",
title:
"Custom - Feeds built to serve a specific use case or rely on external contracts or data sources. These might not be suitable for general use or your use case's risk parameters. Users must evaluate the properties of a feed to make sure it aligns with their intended use case.",
link: "/data-feeds/selecting-data-feeds#-custom-feeds",
},
deprecating: {
key: "deprecating",
name: "Deprecating",
icon: "⭕",
title:
"Deprecating - These feeds are scheduled for deprecation. See the [Deprecation](/data-feeds/deprecating-feeds) page to learn more.",
link: "/data-feeds/deprecating-feeds",
},
} as const

export type CategoryKey = keyof typeof FEED_CATEGORY_CONFIG

/* ===========================
Small helpers
=========================== */

const TABLE = "docs_feeds_risk"

const normalizeKey = (v?: string | null): CategoryKey | undefined => {
if (!v) return undefined
const key = v.toLowerCase() as CategoryKey
return key in FEED_CATEGORY_CONFIG ? key : undefined
}

const chooseTier = (dbTier: string | null | undefined, fallback?: string): string | null => dbTier ?? fallback ?? null

const defaultCategoryList = () => Object.values(FEED_CATEGORY_CONFIG).map(({ key, name }) => ({ key, name }))

/* ===========================
Public API
=========================== */

export const getDefaultCategories = defaultCategoryList

/** Merge static categories with those dynamically present in the table. */
export async function getFeedCategories() {
try {
if (!supabase) return defaultCategoryList()

const { data, error } = await supabase
.from(TABLE)
.select("risk_status")
.not("risk_status", "is", null)
.neq("risk_status", "hidden")

if (error || !data) return defaultCategoryList()

const dynamic = Array.from(
new Set(data.map((d) => normalizeKey(d.risk_status)).filter(Boolean) as CategoryKey[])
).map((key) => ({ key, name: FEED_CATEGORY_CONFIG[key].name }))

// Dedup by key while keeping all defaults first
const byKey = new Map<string, { key: string; name: string }>()
defaultCategoryList().forEach((c) => byKey.set(c.key, c))
dynamic.forEach((c) => byKey.set(c.key, c))

return Array.from(byKey.values())
} catch {
return defaultCategoryList()
}
}

/**
* Batch lookup: returns a Map of `${address}-${network}` → { final }.
* Uses DB value when present; otherwise uses per-item fallback.
*/
export async function getFeedRiskTiersBatch(
feedRequests: Array<{
contractAddress: string
network: string
fallbackCategory?: string
}>
): Promise<Map<string, FeedTierResult>> {
const out = new Map<string, FeedTierResult>()
const keyFor = (addr: string, net: string) => `${addr}-${net}`

if (!supabase) {
feedRequests.forEach(({ contractAddress, network, fallbackCategory }) =>
out.set(keyFor(contractAddress, network), { final: chooseTier(null, fallbackCategory) })
)
return out
}

const networks = Array.from(new Set(feedRequests.map((r) => r.network)))
const addresses = Array.from(new Set(feedRequests.map((r) => r.contractAddress)))

try {
const { data, error } = await supabase
.from(TABLE)
.select("proxy_address, network, risk_status")
.in("proxy_address", addresses)
.in("network", networks)
.limit(1000)

if (error) {
feedRequests.forEach(({ contractAddress, network, fallbackCategory }) =>
out.set(keyFor(contractAddress, network), { final: chooseTier(null, fallbackCategory) })
)
return out
}

const lookup = new Map<string, string | null>()
;(data as FeedRiskRow[] | null)?.forEach((row) =>
lookup.set(keyFor(row.proxy_address, row.network), row.risk_status ?? null)
)

feedRequests.forEach(({ contractAddress, network, fallbackCategory }) => {
const key = keyFor(contractAddress, network)
out.set(key, { final: chooseTier(lookup.get(key), fallbackCategory) })
})

return out
} catch {
feedRequests.forEach(({ contractAddress, network, fallbackCategory }) =>
out.set(keyFor(contractAddress, network), { final: chooseTier(null, fallbackCategory) })
)
return out
}
}
15 changes: 15 additions & 0 deletions src/db/supabase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { createClient } from "@supabase/supabase-js"

const supabaseUrl = import.meta.env.PUBLIC_SUPABASE_URL
const supabaseKey = import.meta.env.PUBLIC_SUPABASE_ANON_KEY

// Export a function that safely creates the client
export function getSupabaseClient() {
if (!supabaseUrl || !supabaseKey) {
return null
}
return createClient(supabaseUrl, supabaseKey)
}

// Export the client instance (may be null)
export const supabase = getSupabaseClient()
18 changes: 16 additions & 2 deletions src/features/feeds/components/FeedList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import { ChainMetadata } from "~/features/data/api/index.ts"
import useQueryString from "~/hooks/useQueryString.ts"
import { RefObject } from "preact"
import { getFeedCategories } from "../../../db/feedCategories.js"
import SectionWrapper from "~/components/SectionWrapper/SectionWrapper.tsx"
import button from "@chainlink/design-system/button.module.css"
import { updateTableOfContents } from "~/components/TableOfContents/tocStore.ts"
Expand Down Expand Up @@ -137,14 +138,27 @@
const testnetLastAddr = Number(testnetCurrentPage) * testnetAddrPerPage
const testnetFirstAddr = testnetLastAddr - testnetAddrPerPage

const dataFeedCategory = [
// Dynamic feed categories loaded from Supabase
const [dataFeedCategory, setDataFeedCategory] = useState([
{ key: "low", name: "Low Market Risk" },
{ key: "medium", name: "Medium Market Risk" },
{ key: "high", name: "High Market Risk" },
{ key: "custom", name: "Custom" },
{ key: "new", name: "New Token" },
{ key: "deprecating", name: "Deprecating" },
]
])

// Load dynamic categories from Supabase on component mount
useEffect(() => {
const loadCategories = async () => {
try {
const categories = await getFeedCategories()
setDataFeedCategory(categories)
} catch (error) {}
}

loadCategories()
}, [])
const smartDataTypes = [
{ key: "Proof of Reserve", name: "Proof of Reserve" },
{ key: "NAVLink", name: "NAVLink" },
Expand Down Expand Up @@ -659,8 +673,8 @@

{(() => {
// Handle deprecating feeds from initialCache if available
if (isDeprecating && initialCache && initialCache.deprecated && (initialCache.deprecated as any).networks) {

Check warning on line 676 in src/features/feeds/components/FeedList.tsx

View workflow job for this annotation

GitHub Actions / eslint

Unexpected any. Specify a different type

Check warning on line 676 in src/features/feeds/components/FeedList.tsx

View workflow job for this annotation

GitHub Actions / eslint

Unexpected any. Specify a different type
return (initialCache.deprecated as any).networks

Check warning on line 677 in src/features/feeds/components/FeedList.tsx

View workflow job for this annotation

GitHub Actions / eslint

Unexpected any. Specify a different type

Check warning on line 677 in src/features/feeds/components/FeedList.tsx

View workflow job for this annotation

GitHub Actions / eslint

Unexpected any. Specify a different type
.filter((network: any) => {
let foundDeprecated = false
network.metadata?.forEach((feed: any) => {
Expand Down
Loading
Loading