{"version":3,"file":"price-oracle.mjs","names":[],"sources":["../../../src/services/price-oracle.ts"],"sourcesContent":["/**\n * Multi-Source Price Oracle — cross-validated price feeds.\n *\n * Queries multiple price sources in parallel:\n * - DexScreener (current primary)\n * - CoinGecko\n * - CoinMarketCap\n * - DeFiLlama\n *\n * Cross-validates: flags if sources disagree by >2% (possible stale data\n * or manipulation). Returns the median price for robustness.\n */\n\nimport { getTokenPriceUsd, getEthPriceUsd, type DexPairData } from './dexscreener-service.js';\nimport { guardedFetch } from './endpoint-allowlist.js';\nimport { getCredentialVault } from './credential-vault.js';\n\n// ── Types ───────────────────────────────────────────────────────────────────\n\nexport interface PriceResult {\n  token: string;\n  chain: string;\n  priceUsd: number;\n  sources: PriceSource[];\n  confidence: 'high' | 'medium' | 'low';\n  divergencePercent: number; // max % difference between sources\n  warning?: string;\n}\n\nexport interface PriceSource {\n  name: string;\n  priceUsd: number;\n  timestamp: number;\n  error?: string;\n}\n\nexport interface PriceOracleConfig {\n  /** Sources to use. Default: all available. */\n  sources?: string[];\n  /** Divergence threshold in %. Flag if sources differ by more. Default: 2. */\n  divergenceThreshold?: number;\n  /** Per-source timeout in ms. Default: 3000. */\n  timeoutMs?: number;\n  /** CoinGecko API key (optional, increases rate limit). */\n  coingeckoApiKey?: string;\n  /** CoinMarketCap API key (required for CMC). */\n  cmcApiKey?: string;\n  /** Birdeye API key (required for Birdeye). */\n  birdeyeApiKey?: string;\n}\n\n// ── Token ID Mappings ───────────────────────────────────────────────────────\n\n// Well-known tokens → CoinGecko IDs\nconst COINGECKO_IDS: Record<string, string> = {\n  ETH: 'ethereum', WETH: 'ethereum',\n  BTC: 'bitcoin', WBTC: 'bitcoin',\n  USDC: 'usd-coin', USDT: 'tether', DAI: 'dai',\n  LINK: 'chainlink', UNI: 'uniswap', AAVE: 'aave',\n  ARB: 'arbitrum', OP: 'optimism', MATIC: 'matic-network',\n  CRV: 'curve-dao-token', MKR: 'maker', SNX: 'synthetix-network-token',\n  COMP: 'compound-governance-token', LDO: 'lido-dao', RPL: 'rocket-pool',\n  PEPE: 'pepe', SHIB: 'shiba-inu', DOGE: 'dogecoin',\n};\n\n// Well-known tokens → Birdeye chain-specific addresses (Base and Ethereum)\nconst BIRDEYE_ADDRESSES: Record<string, Record<string, string>> = {\n  ETH: { base: 'So11111111111111111111111111111111111111112', ethereum: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2' },\n  WETH: { base: '0x4200000000000000000000000000000000000006', ethereum: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2' },\n  USDC: { base: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', ethereum: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' },\n  USDT: { base: '0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2', ethereum: '0xdAC17F958D2ee523a2206206994597C13D831ec7' },\n  DAI: { ethereum: '0x6B175474E89094C44Da98b954EedeAC495271d0F' },\n};\n\nconst BIRDEYE_CHAIN_MAP: Record<string, string> = {\n  base: 'base', ethereum: 'ethereum', arbitrum: 'arbitrum',\n  optimism: 'optimism', polygon: 'polygon',\n};\n\n// ── Individual Source Fetchers ───────────────────────────────────────────────\n\nasync function fetchCoinGecko(\n  token: string,\n  timeoutMs: number,\n  apiKey?: string,\n): Promise<PriceSource> {\n  const cgId = COINGECKO_IDS[token.toUpperCase()];\n  if (!cgId) {\n    return { name: 'CoinGecko', priceUsd: 0, timestamp: Date.now(), error: 'Unknown token' };\n  }\n\n  try {\n    const baseUrl = apiKey\n      ? 'https://pro-api.coingecko.com/api/v3'\n      : 'https://api.coingecko.com/api/v3';\n    const headers: Record<string, string> = {};\n    if (apiKey) headers['x-cg-pro-api-key'] = apiKey;\n\n    const resp = await guardedFetch(\n      `${baseUrl}/simple/price?ids=${cgId}&vs_currencies=usd`,\n      { headers, signal: AbortSignal.timeout(timeoutMs) },\n    );\n\n    if (!resp.ok) throw new Error(`HTTP ${resp.status}`);\n    const data = (await resp.json()) as Record<string, { usd?: number }>;\n    const price = data[cgId]?.usd ?? 0;\n\n    return { name: 'CoinGecko', priceUsd: price, timestamp: Date.now() };\n  } catch (err) {\n    return { name: 'CoinGecko', priceUsd: 0, timestamp: Date.now(), error: (err as Error).message };\n  }\n}\n\nasync function fetchDeFiLlama(\n  token: string,\n  chain: string,\n  tokenAddress: string | undefined,\n  timeoutMs: number,\n): Promise<PriceSource> {\n  try {\n    // DeFiLlama uses {chain}:{address} format\n    let query: string;\n    if (tokenAddress) {\n      const llChain = chain === 'base' ? 'base' : chain === 'ethereum' ? 'ethereum' : chain;\n      query = `${llChain}:${tokenAddress}`;\n    } else {\n      // Fallback: use coingecko ID\n      const cgId = COINGECKO_IDS[token.toUpperCase()];\n      if (!cgId) {\n        return { name: 'DeFiLlama', priceUsd: 0, timestamp: Date.now(), error: 'Unknown token' };\n      }\n      query = `coingecko:${cgId}`;\n    }\n\n    const resp = await guardedFetch(\n      `https://coins.llama.fi/prices/current/${query}`,\n      { signal: AbortSignal.timeout(timeoutMs) },\n    );\n\n    if (!resp.ok) throw new Error(`HTTP ${resp.status}`);\n    const data = (await resp.json()) as { coins: Record<string, { price?: number; confidence?: number }> };\n    const coin = Object.values(data.coins)[0];\n\n    return {\n      name: 'DeFiLlama',\n      priceUsd: coin?.price ?? 0,\n      timestamp: Date.now(),\n    };\n  } catch (err) {\n    return { name: 'DeFiLlama', priceUsd: 0, timestamp: Date.now(), error: (err as Error).message };\n  }\n}\n\nasync function fetchCoinMarketCap(\n  token: string,\n  timeoutMs: number,\n  apiKey?: string,\n): Promise<PriceSource> {\n  if (!apiKey) {\n    return { name: 'CoinMarketCap', priceUsd: 0, timestamp: Date.now(), error: 'No API key' };\n  }\n\n  try {\n    const resp = await guardedFetch(\n      `https://pro-api.coinmarketcap.com/v1/cryptocurrency/quotes/latest?symbol=${token.toUpperCase()}`,\n      {\n        headers: {\n          'X-CMC_PRO_API_KEY': apiKey,\n          Accept: 'application/json',\n        },\n        signal: AbortSignal.timeout(timeoutMs),\n      },\n    );\n\n    if (!resp.ok) throw new Error(`HTTP ${resp.status}`);\n    const data = (await resp.json()) as any;\n    const entry = Object.values(data.data ?? {})[0] as any;\n    const price = entry?.quote?.USD?.price ?? 0;\n\n    return { name: 'CoinMarketCap', priceUsd: price, timestamp: Date.now() };\n  } catch (err) {\n    return { name: 'CoinMarketCap', priceUsd: 0, timestamp: Date.now(), error: (err as Error).message };\n  }\n}\n\nasync function fetchBirdeye(\n  token: string,\n  chain: string,\n  tokenAddress: string | undefined,\n  timeoutMs: number,\n  apiKey?: string,\n): Promise<PriceSource> {\n  if (!apiKey) {\n    return { name: 'Birdeye', priceUsd: 0, timestamp: Date.now(), error: 'No API key (BIRDEYE_API_KEY)' };\n  }\n\n  try {\n    // Resolve address: use provided address, or look up from well-known table\n    const address = tokenAddress ?? BIRDEYE_ADDRESSES[token.toUpperCase()]?.[chain];\n    if (!address) {\n      return { name: 'Birdeye', priceUsd: 0, timestamp: Date.now(), error: 'Unknown token address for Birdeye' };\n    }\n\n    const birdeyeChain = BIRDEYE_CHAIN_MAP[chain] ?? 'base';\n    const resp = await guardedFetch(\n      `https://public-api.birdeye.so/defi/price?address=${address}`,\n      {\n        headers: {\n          'X-API-KEY': apiKey,\n          'x-chain': birdeyeChain,\n          Accept: 'application/json',\n        },\n        signal: AbortSignal.timeout(timeoutMs),\n      },\n    );\n\n    if (!resp.ok) throw new Error(`HTTP ${resp.status}`);\n    const data = (await resp.json()) as any;\n    const price = data.data?.value ?? 0;\n\n    return { name: 'Birdeye', priceUsd: price, timestamp: Date.now() };\n  } catch (err) {\n    return { name: 'Birdeye', priceUsd: 0, timestamp: Date.now(), error: (err as Error).message };\n  }\n}\n\nasync function fetchDexScreenerPrice(\n  token: string,\n  chain: string,\n  timeoutMs: number,\n): Promise<PriceSource> {\n  try {\n    const { priceUsd } = await getTokenPriceUsd(token, chain);\n    return { name: 'DexScreener', priceUsd, timestamp: Date.now() };\n  } catch (err) {\n    return { name: 'DexScreener', priceUsd: 0, timestamp: Date.now(), error: (err as Error).message };\n  }\n}\n\n// ── Price Oracle ────────────────────────────────────────────────────────────\n\nexport class PriceOracle {\n  private config: Required<PriceOracleConfig>;\n\n  constructor(userConfig: PriceOracleConfig = {}) {\n    this.config = {\n      sources: userConfig.sources ?? ['DexScreener', 'CoinGecko', 'DeFiLlama', 'CoinMarketCap', 'Birdeye'],\n      divergenceThreshold: userConfig.divergenceThreshold ?? 2,\n      timeoutMs: userConfig.timeoutMs ?? 3000,\n      coingeckoApiKey: userConfig.coingeckoApiKey ?? getCredentialVault().getSecret('price.coingecko.apiKey', 'price-oracle') ?? '',\n      cmcApiKey: userConfig.cmcApiKey ?? getCredentialVault().getSecret('price.cmc.apiKey', 'price-oracle') ?? '',\n      birdeyeApiKey: userConfig.birdeyeApiKey ?? getCredentialVault().getSecret('price.birdeye.apiKey', 'price-oracle') ?? '',\n    };\n  }\n\n  /**\n   * Get a cross-validated price for a token.\n   * Queries all enabled sources in parallel, computes median, checks divergence.\n   */\n  async getPrice(\n    token: string,\n    chain = 'base',\n    tokenAddress?: string,\n  ): Promise<PriceResult> {\n    const fetchers: Array<Promise<PriceSource>> = [];\n    const enabled = this.config.sources;\n\n    if (enabled.includes('DexScreener')) {\n      fetchers.push(fetchDexScreenerPrice(token, chain, this.config.timeoutMs));\n    }\n    if (enabled.includes('CoinGecko')) {\n      fetchers.push(fetchCoinGecko(token, this.config.timeoutMs, this.config.coingeckoApiKey || undefined));\n    }\n    if (enabled.includes('DeFiLlama')) {\n      fetchers.push(fetchDeFiLlama(token, chain, tokenAddress, this.config.timeoutMs));\n    }\n    if (enabled.includes('CoinMarketCap')) {\n      fetchers.push(fetchCoinMarketCap(token, this.config.timeoutMs, this.config.cmcApiKey || undefined));\n    }\n    if (enabled.includes('Birdeye')) {\n      fetchers.push(fetchBirdeye(token, chain, tokenAddress, this.config.timeoutMs, this.config.birdeyeApiKey || undefined));\n    }\n\n    const sources = await Promise.all(fetchers);\n    const valid = sources.filter((s) => !s.error && s.priceUsd > 0);\n\n    if (valid.length === 0) {\n      return {\n        token,\n        chain,\n        priceUsd: 0,\n        sources,\n        confidence: 'low',\n        divergencePercent: 0,\n        warning: 'No sources returned a valid price.',\n      };\n    }\n\n    // Calculate median price\n    const prices = valid.map((s) => s.priceUsd).sort((a, b) => a - b);\n    const median = prices.length % 2 === 0\n      ? (prices[prices.length / 2 - 1]! + prices[prices.length / 2]!) / 2\n      : prices[Math.floor(prices.length / 2)]!;\n\n    // Calculate max divergence from median\n    const maxDivergence = valid.reduce((max, s) => {\n      const div = Math.abs(s.priceUsd - median) / median * 100;\n      return Math.max(max, div);\n    }, 0);\n\n    // Determine confidence\n    let confidence: 'high' | 'medium' | 'low';\n    let warning: string | undefined;\n\n    if (valid.length >= 3 && maxDivergence < this.config.divergenceThreshold) {\n      confidence = 'high';\n    } else if (valid.length >= 2 && maxDivergence < this.config.divergenceThreshold * 2) {\n      confidence = 'medium';\n    } else {\n      confidence = 'low';\n    }\n\n    if (maxDivergence >= this.config.divergenceThreshold) {\n      const divergentSources = valid\n        .filter((s) => Math.abs(s.priceUsd - median) / median * 100 >= this.config.divergenceThreshold)\n        .map((s) => `${s.name}: $${s.priceUsd.toFixed(4)}`);\n      warning = `Price sources disagree by ${maxDivergence.toFixed(1)}%: ${divergentSources.join(', ')}. ` +\n        `Using median: $${median.toFixed(4)}. Possible stale data or manipulation.`;\n    }\n\n    return {\n      token,\n      chain,\n      priceUsd: median,\n      sources,\n      confidence,\n      divergencePercent: Math.round(maxDivergence * 100) / 100,\n      warning,\n    };\n  }\n\n  /**\n   * Get ETH price from multiple sources.\n   */\n  async getEthPrice(): Promise<PriceResult> {\n    return this.getPrice('ETH', 'base');\n  }\n\n  /**\n   * Batch price lookup for multiple tokens.\n   */\n  async getPrices(\n    tokens: Array<{ symbol: string; chain?: string; address?: string }>,\n  ): Promise<PriceResult[]> {\n    return Promise.all(\n      tokens.map((t) => this.getPrice(t.symbol, t.chain ?? 'base', t.address)),\n    );\n  }\n}\n\n// ── Singleton ───────────────────────────────────────────────────────────────\n\nlet _instance: PriceOracle | null = null;\n\nexport function getPriceOracle(config?: PriceOracleConfig): PriceOracle {\n  if (!_instance) {\n    _instance = new PriceOracle(config);\n  }\n  return _instance;\n}\n\nexport function resetPriceOracle(): void {\n  _instance = null;\n}\n"],"mappings":"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"}