UNPKG

@kinetiq-research/markets-assets

Version:

Asset metadata (descriptions, oracle references) for Kinetiq markets

68 lines (56 loc) • 7.57 kB
## Purpose Provides per-asset metadata (oracle URLs, localized descriptions, news-classifier keywords, brand colors, category normalization) for tradeable assets on Kinetiq Markets. Consumed by both the Markets app and Pulse services. ## Public API - `./get-asset-metadata` — `getAssetMetadata({ symbol })` returns `{ oracleSymbol, oracleUrl }` for a given asset symbol (fields are `undefined` for unknown symbols) - `./get-asset-name` — `getAssetName({ symbol })` returns the English `description_short` for the asset DESCRIPTION — what the thing IS (e.g., `'NVIDIA'` for `NVDA`, `'Top 100 US tech stocks'` for `USTECH`). Backend / non-localized callers (Pulse briefs, universe seeding) should use this. **Keyed by BARE lowercase symbol, and it takes no `dex`** — see Patterns. For what the trade app CALLS a market (`'QQQ'` for `USTECH`), use `./to-display-symbol` instead. - `./to-display-symbol` — `toDisplaySymbol({ annotationDisplayName?, hyperliquidSymbol, symbol })` resolves what a perp listing is CALLED: curated `PERP_SYMBOL_OVERRIDES` (by raw HL name, then by clean symbol) → the exchange's live `perpConciseAnnotations` `displayName` → the clean symbol. The single source of truth, shared by `normalizePerpsMeta` (trade app) and pulse-shared's `buildTickerUniverse` (Pulse) so the two cannot name one instrument differently. A display name is for RENDERING only — it is not unique (`xyz:VIX` is annotated `VVV`, itself a real symbol), so never parse one back to an asset. - `./get-token-icon-url` — `getTokenIconUrl(symbol)` builds the `/icons/<SYMBOL>.svg` token icon URL - `./has-news-keywords` — `hasNewsKeywords({ dex, symbol })` returns true iff the asset has an entry in `assetNewsKeywords`. Used by Pulse's news-tracked allowlist gate as the "tracked by Markets" predicate (HL lists hundreds of perps, but only ~63 have curated keyword coverage). - `./normalize-asset-category` — `normalizeAssetCategory({ raw, isMainHyperliquid? })` maps Hyperliquid's free-form `perpConciseAnnotations` category string to the narrow `AssetCategory` union (`'commodity' | 'crypto' | 'currency' | 'index' | 'stock'`). Unknown values fall back to `'stock'`; `isMainHyperliquid: true` short-circuits to `'crypto'` (main-HL listings aren't annotated). - `./constants/asset-colors` — `ASSET_COLORS` map (symbol → hex) and `DEFAULT_CHART_COLORS` fallback palette. Used by Markets stats charts and any per-asset color rendering. - `./constants/asset-news-keywords` — raw `assetNewsKeywords` map (keyed `${dex}:${symbol.toLowerCase()}` for HIP-3 or `${symbol.toLowerCase()}` for main HL). Use `hasNewsKeywords` for the membership predicate. - `./to-asset-key` — `toAssetKey({ dex, symbol })` canonicalizes the lookup key used by `asset-metadata`, `asset-news-keywords`, and related maps - `./asset` — `SpotAsset`, `PerpsAsset`, `Asset` types (the normalized, display-ready asset shapes used across Markets) - `./asset-indices` — `SPOT_INDEX_OFFSET`, `HIP3_ASSET_INDEX_BASE`, `HIP3_DEX_INDEX_MULTIPLIER` (Hyperliquid asset-ID encoding constants) - `./parse-perps-hyperliquid-symbol` — `parsePerpsHyperliquidSymbol({ hyperliquidSymbol })` splits a `dex:symbol` perp name into `{ dex, symbol }` - `./to-trade-url-symbol` — `toTradeUrlSymbol({ dex?, symbol })` is the SINGLE definition of the `/trade/…` url name: the DISPLAY name, lowercased, with any `dex:` prefix stripped (`mkts:US500` → `us500`, `WTIOIL` → `wtioil`). Idempotent, so it is safe to apply to an already-canonical `tradeUrlSymbol` — `ROUTES.TRADE_SYMBOL` does exactly that, and passes no `dex`. Urls carry no dex except for the listings in `PREFIXED_URL_SYMBOLS`, which are the ones whose ticker is live on a SECOND dex we surface: SanDisk trades on both `xyz` and `io`, so `xyz` keeps `/trade/sndk` and the `io` listing is `/trade/io:sndk`. Pass `dex` from anything normalizing a universe; add a listing to that set (never a blanket prefix rule) when a new collision ships. - `./get-dex-name-from-meta` — `getDexNameFromMeta({ meta })` detects a DEX name from a perp meta's universe item prefixes (`''` for main HL) - `./normalize-perps-meta` — `normalizePerpsMeta({ perpsMeta, perpsAnnotations? })` returns `NormalizedPerpsMeta` (each universe item gets a normalized `asset`). Display symbols are resolved by `./to-display-symbol` (shared with pulse-shared's `buildTickerUniverse`): `PERP_SYMBOL_OVERRIDES` (static, wins) over the live `perpsAnnotations` over the raw cleaned name, with a blank annotation treated as no name rather than as the empty string. - `./perp-symbol-overrides` — `PERP_SYMBOL_OVERRIDES` curated static perp display-symbol map; an additive layer over the live `perpConciseAnnotations` API, not a replacement ## Internal Structure ``` src/ _/ asset-metadata.ts # Static metadata map (dex:symbol -> { oracleSymbol, oracleUrl }) types.ts # LocaleMessages type constants/ asset-colors.ts # ASSET_COLORS + DEFAULT_CHART_COLORS asset-news-keywords.ts # assetNewsKeywords (dex:symbol -> keyword list) icons/ <SYMBOL>.svg # Per-asset SVG token icons (served at /icons/<SYMBOL>.svg) get-asset-metadata.ts get-asset-name.ts # English description_short lookup, BARE-symbol keyed (backend / non-localized) get-token-icon-url.ts to-asset-key.ts # toAssetKey helper for `${dex}:${symbol}` lookups (dex-keyed maps ONLY) to-display-symbol.ts # What a listing is CALLED — shared by normalizePerpsMeta + Pulse's buildTickerUniverse normalize-asset-category.ts # normalizeAssetCategory + AssetCategory union scripts/ fetch-hip3-icons.ts # `pnpm icons:fetch` — downloads HIP-3 SVG icons from Hyperliquid messages/ {locale}.json # 17 locale files with per-asset descriptions ``` ## Patterns - Pure data package — no React, no hooks, no side effects - Symbol lookups are case-insensitive (lowercased before map access) - Missing symbols return `undefined` fields rather than throwing - Locale messages are statically imported (not loaded at runtime) - Per-asset lookup keys come in TWO families, and using the wrong one fails SILENTLY (the lookup just returns `undefined` and callers fall back to the raw symbol). Check which map you are reading before you build a key: - **Dex-keyed** — `src/_/asset-metadata.ts` (oracle) and `constants/asset-news-keywords.ts`: `${dex}:${symbol.toLowerCase()}` for HIP-3 listings, `${symbol.toLowerCase()}` for main HL. Use `toAssetKey` to canonicalize. Its only production caller is `hasNewsKeywords`. - **Bare-keyed** — the locale `messages/*.json` `asset-metadata` map read by `getAssetName`: `${symbol.toLowerCase()}`, no dex dimension at all (all 121 entries, zero colons; `apps/markets` reads it the same way in five places). **Do not apply `toAssetKey` to it.** Doing so is what made `getAssetName` return `undefined` for every HIP-3 equity and index — so briefs said "NVDA reports" instead of "NVIDIA reports" — while main-HL crypto kept working because its keys are bare anyway. ## Adding New Exports 1. Create source file in `src/` 2. Add subpath export to `package.json` under `"exports"` ## Adding a New Asset 1. Add entry in `src/_/asset-metadata.ts` keyed by `${dex}:${symbol.toLowerCase()}` (e.g. `'mkts:nvda'`) 2. Add `description_medium` and `description_short` to each locale JSON under `messages/` 3. If Pulse should classify news against this asset, add a keyword list to `src/constants/asset-news-keywords.ts`