diff --git a/scripts/discover-drift-markets.ts b/scripts/discover-drift-markets.ts index 48d5faf..5e93445 100644 --- a/scripts/discover-drift-markets.ts +++ b/scripts/discover-drift-markets.ts @@ -11,7 +11,6 @@ */ import { initializeDriftService, getDriftService } from '../lib/drift/client' -import { OracleSource } from '@drift-labs/sdk' // Helper to decode market name from bytes (Drift stores names as 32-byte arrays) function decodeMarketName(nameBytes: number[] | Uint8Array | undefined): string { @@ -23,27 +22,87 @@ function decodeMarketName(nameBytes: number[] | Uint8Array | undefined): string return name.trim() } -// Map oracle source enum to human-readable string -function getOracleSourceName(oracleSource: OracleSource | number): string { - // OracleSource enum values from Drift SDK - const sourceMap: Record = { - 0: 'Pyth', - 1: 'Switchboard', - 2: 'QuoteAsset', - 3: 'Pyth1K', - 4: 'Pyth1M', - 5: 'PythStableCoin', - 6: 'Prelaunch', - 7: 'PythPull', - 8: 'Pyth1KPull', - 9: 'Pyth1MPull', - 10: 'PythStableCoinPull', - 11: 'SwitchboardOnDemand', +/** + * Extract numeric value from Anchor-style enum objects + * + * Drift SDK uses Anchor which represents enums as objects like: + * - { pyth: {} } for OracleSource.Pyth + * - { active: {} } for MarketStatus.Active + * + * This helper safely extracts the key name and maps it to a numeric index, + * or handles direct numeric values for backwards compatibility. + * + * @param enumValue - The enum value (object or number) + * @param keyMap - Map of enum key names to their string representations + * @returns The string representation of the enum value + */ +function getEnumName>( + enumValue: unknown, + keyMap: T +): string { + // Handle null/undefined + if (enumValue === null || enumValue === undefined) { + return 'Unknown' } - // Handle both enum and number types - const sourceValue = typeof oracleSource === 'number' ? oracleSource : Object.values(oracleSource)[0] - return sourceMap[sourceValue as number] || `Unknown(${sourceValue})` + // Handle direct numeric values (legacy support) + if (typeof enumValue === 'number') { + const values = Object.values(keyMap) + return values[enumValue] || `Unknown(${enumValue})` + } + + // Handle Anchor-style enum objects like { pyth: {} } or { active: {} } + if (typeof enumValue === 'object') { + const keys = Object.keys(enumValue as object) + if (keys.length === 1) { + const key = keys[0].toLowerCase() + // Find matching key in keyMap (case-insensitive) + for (const [mapKey, mapValue] of Object.entries(keyMap)) { + if (mapKey.toLowerCase() === key) { + return mapValue + } + } + // Return the key name capitalized if no map match + return keys[0].charAt(0).toUpperCase() + keys[0].slice(1) + } + } + + return `Unknown(${JSON.stringify(enumValue)})` +} + +// Oracle source name mappings (Anchor enum key to display name) +const ORACLE_SOURCE_MAP: Record = { + pyth: 'Pyth', + switchboard: 'Switchboard', + quoteAsset: 'QuoteAsset', + pyth1K: 'Pyth1K', + pyth1M: 'Pyth1M', + pythStableCoin: 'PythStableCoin', + prelaunch: 'Prelaunch', + pythPull: 'PythPull', + pyth1KPull: 'Pyth1KPull', + pyth1MPull: 'Pyth1MPull', + pythStableCoinPull: 'PythStableCoinPull', + switchboardOnDemand: 'SwitchboardOnDemand', +} + +// Market status name mappings +const MARKET_STATUS_MAP: Record = { + initialized: 'Initialized', + active: 'Active', + fundingPaused: 'FundingPaused', + ammPaused: 'AmmPaused', + fillPaused: 'FillPaused', + withdrawPaused: 'WithdrawPaused', + reduceOnly: 'ReduceOnly', + settlement: 'Settlement', + delisted: 'Delisted', +} + +// Contract type name mappings +const CONTRACT_TYPE_MAP: Record = { + perpetual: 'Perpetual', + future: 'Future', } // Format number with appropriate decimals @@ -137,31 +196,16 @@ async function discoverMarkets(searchSymbol?: string): Promise { ? Number(market.imfFactor) / 1e6 : 0 - // Market status - const statusMap: Record = { - 0: 'Initialized', - 1: 'Active', - 2: 'FundingPaused', - 3: 'AmmPaused', - 4: 'FillPaused', - 5: 'WithdrawPaused', - 6: 'ReduceOnly', - 7: 'Settlement', - 8: 'Delisted', - } - const status = statusMap[Object.values(market.status)[0] as number] || 'Unknown' + // Market status - use robust enum extraction + const status = getEnumName(market.status, MARKET_STATUS_MAP) - // Contract type - const contractTypeMap: Record = { - 0: 'Perpetual', - 1: 'Future', - } - const contractType = contractTypeMap[Object.values(market.contractType)[0] as number] || 'Unknown' + // Contract type - use robust enum extraction + const contractType = getEnumName(market.contractType, CONTRACT_TYPE_MAP) const marketInfo: MarketInfo = { index: i, symbol, - oracleSource: getOracleSourceName(oracleSource), + oracleSource: getEnumName(oracleSource, ORACLE_SOURCE_MAP), minOrderSize, orderStepSize, tickSize,