import { ArrowLineDown } from "@phosphor-icons/react/dist/ssr/ArrowLineDown"; import { ArrowSquareOut } from "@phosphor-icons/react/dist/ssr/ArrowSquareOut"; import { ArrowsOutSimple } from "@phosphor-icons/react/dist/ssr/ArrowsOutSimple"; import { ClockCountdown } from "@phosphor-icons/react/dist/ssr/ClockCountdown"; import { StackPlus } from "@phosphor-icons/react/dist/ssr/StackPlus"; import { Badge } from "@pythnetwork/component-library/Badge"; import { Button } from "@pythnetwork/component-library/Button"; import { type Props as CardProps, Card, } from "@pythnetwork/component-library/Card"; import { Drawer, DrawerTrigger } from "@pythnetwork/component-library/Drawer"; import { StatCard } from "@pythnetwork/component-library/StatCard"; import type { ElementType } from "react"; import { AssetClassesDrawer } from "./asset-classes-drawer"; import { ComingSoonList } from "./coming-soon-list"; import styles from "./index.module.scss"; import { PriceFeedsCard } from "./price-feeds-card"; import { Cluster, getFeeds } from "../../services/pyth"; import { priceFeeds as priceFeedsStaticConfig } from "../../static-data/price-feeds"; import { activeChains } from "../../static-data/stats"; import { LivePrice } from "../LivePrices"; import { YesterdaysPricesProvider, PriceFeedChangePercent, } from "../PriceFeedChangePercent"; import { PriceFeedIcon } from "../PriceFeedIcon"; import { PriceFeedTag } from "../PriceFeedTag"; const PRICE_FEEDS_ANCHOR = "priceFeeds"; export const PriceFeeds = async () => { const priceFeeds = await getPriceFeeds(); const numFeedsByAssetClass = getNumFeedsByAssetClass(priceFeeds.activeFeeds); const featuredComingSoon = [ ...filterFeeds( priceFeeds.comingSoon, priceFeedsStaticConfig.featuredComingSoon, ), ...priceFeeds.comingSoon.filter( ({ symbol }) => !priceFeedsStaticConfig.featuredComingSoon.includes(symbol), ), ].slice(0, 5); const featuredRecentlyAdded = filterFeeds( priceFeeds.activeFeeds, priceFeedsStaticConfig.featuredRecentlyAdded, ); return (

Price Feeds

} /> } /> } />
[ symbol, product.price_account, ]), )} > } feeds={featuredRecentlyAdded} showPrices linkFeeds /> } feeds={featuredComingSoon} toolbar={ Coming Soon {priceFeeds.comingSoon.length} } > symbol, )} /> } /> ({ symbol: feed.symbol, icon: , id: feed.product.price_account, displaySymbol: feed.product.display_symbol, assetClass: feed.product.asset_type, exponent: feed.price.exponent, numQuoters: feed.price.numQuoters, }))} />
); }; type FeaturedFeedsCardProps = Omit< CardProps, "children" > & { showPrices?: boolean | undefined; linkFeeds?: boolean | undefined; feeds: { symbol: string; product: { display_symbol: string; price_account: string; description: string; }; }[]; }; const FeaturedFeedsCard = ({ showPrices, linkFeeds, feeds, ...props }: FeaturedFeedsCardProps) => (
{feeds.map((feed) => (
{showPrices && (
)}
))}
); const getPriceFeeds = async () => { const priceFeeds = await getFeeds(Cluster.Pythnet); const activeFeeds = priceFeeds.filter((feed) => isActive(feed)); const comingSoon = priceFeeds.filter((feed) => !isActive(feed)); return { activeFeeds, comingSoon }; }; const getNumFeedsByAssetClass = ( feeds: { product: { asset_type: string } }[], ): Record => { const classes: Record = {}; for (const feed of feeds) { const assetType = feed.product.asset_type; classes[assetType] = (classes[assetType] ?? 0) + 1; } return classes; }; const filterFeeds = ( feeds: T[], symbols: string[], ): T[] => symbols.map((symbol) => { const feed = feeds.find((feed) => feed.symbol === symbol); if (feed) { return feed; } else { throw new NoSuchFeedError(symbol); } }); const isActive = (feed: { price: { minPublishers: number } }) => feed.price.minPublishers <= 50; class NoSuchFeedError extends Error { constructor(symbol: string) { super(`No feed exists named ${symbol}`); this.name = "NoSuchFeedError"; } }