1Branch0Tags
GL
glucryptoFix Paragon perp market resolution
251fe3214 days ago5Commits
typescript
import { fetchHyperliquidDexMetaAndAssetCtxs, fetchHyperliquidPerpMarketInfo, fetchHyperliquidResolvedMarketDescriptor, normalizeHyperliquidMetaSymbol, resolveHyperliquidLeverageMode, resolveHyperliquidPerpSymbol, type HyperliquidEnvironment, } from "opentool/adapters/hyperliquid"; const PARAGON_PERP_ALIASES: Record<string, string> = { "BTC-D": "para:BTCD", "BTC.D": "para:BTCD", BTCD: "para:BTCD", OTHERS: "para:OTHERS", TOTAL2: "para:TOTAL2", }; export type PairTradePerpMarketInfo = { symbol: string; price: number; fundingRate: number | null; szDecimals: number; }; function readNumber(value: unknown): number | null { if (typeof value === "number" && Number.isFinite(value)) return value; if (typeof value === "string" && value.trim().length > 0) { const parsed = Number(value); return Number.isFinite(parsed) ? parsed : null; } return null; } function resolveParagonAlias(value: string) { const normalized = value.trim().replace(/\s+/g, "").toUpperCase(); const withoutUsdQuote = normalized.replace(/[-/]USDC$/, ""); return PARAGON_PERP_ALIASES[withoutUsdQuote] ?? value; } export function resolvePairTradePerpSymbol(asset: string): string { return resolveHyperliquidPerpSymbol(resolveParagonAlias(asset)); } export function resolvePairTradePerpLeverageMode( symbol: string, configuredMode: "cross" | "isolated", ): "cross" | "isolated" { return symbol.includes(":") ? resolveHyperliquidLeverageMode(symbol) : configuredMode; } export async function fetchPairTradePerpMarketInfo(params: { environment: HyperliquidEnvironment; symbol: string; }): Promise<PairTradePerpMarketInfo> { const descriptor = await fetchHyperliquidResolvedMarketDescriptor({ environment: params.environment, symbol: params.symbol, }); if (descriptor.kind !== "perp") { throw new Error(`Expected Hyperliquid perp market: ${params.symbol}`); } if (!descriptor.dex) { return fetchHyperliquidPerpMarketInfo({ environment: params.environment, symbol: descriptor.orderSymbol, }); } const data = (await fetchHyperliquidDexMetaAndAssetCtxs( params.environment, descriptor.dex, )) as [ { universe?: Array<{ name?: string; szDecimals?: number | string }> }, Array<Record<string, unknown>>, ]; const universe = Array.isArray(data?.[0]?.universe) ? data[0].universe : []; const contexts = Array.isArray(data?.[1]) ? data[1] : []; const targetSymbol = descriptor.orderSymbol.toUpperCase(); const targetBase = normalizeHyperliquidMetaSymbol(descriptor.orderSymbol).toUpperCase(); const idx = universe.findIndex((entry) => { const name = entry?.name ?? ""; return ( name.toUpperCase() === targetSymbol || normalizeHyperliquidMetaSymbol(name).toUpperCase() === targetBase ); }); if (idx < 0) { throw new Error(`Unknown Hyperliquid dex perp asset: ${descriptor.orderSymbol}`); } const ctx = contexts[idx] ?? null; const price = readNumber(ctx?.markPx ?? ctx?.midPx ?? ctx?.oraclePx); if (!price || price <= 0) { throw new Error(`No perp price available for ${descriptor.orderSymbol}`); } const szDecimals = readNumber(universe[idx]?.szDecimals); if (szDecimals == null) { throw new Error(`No size decimals available for ${descriptor.orderSymbol}`); } return { symbol: descriptor.orderSymbol, price, fundingRate: readNumber(ctx?.funding), szDecimals, }; }