/** * Synor Oracle Service * Multi-source price aggregation with TWAP support * * Port: 17500 * Endpoints: * GET /health - Health check * GET /prices - All current prices * GET /price/:symbol - Price for specific symbol * GET /twap/:symbol - Time-weighted average price */ const http = require('http'); const PORT = process.env.PORT || 17500; const UPDATE_INTERVAL = parseInt(process.env.UPDATE_INTERVAL_MS) || 1000; const STALE_THRESHOLD = parseInt(process.env.STALE_THRESHOLD_MS) || 60000; // Price state (in-memory cache) const prices = new Map(); const priceHistory = new Map(); const MAX_HISTORY = 3600; // 1 hour of 1-second updates // Initialize default prices (testnet) const DEFAULT_PRICES = { 'BTC/USD': 45000, 'ETH/USD': 2500, 'SYNOR/USD': 1.50, 'SOL/USD': 95, 'ATOM/USD': 8.50, 'OSMO/USD': 0.75, }; // Initialize prices Object.entries(DEFAULT_PRICES).forEach(([symbol, price]) => { prices.set(symbol, { symbol, price, timestamp: Date.now(), sources: ['simulator'], confidence: 1.0, }); priceHistory.set(symbol, [{ price, timestamp: Date.now() }]); }); // Calculate TWAP function calculateTWAP(symbol, windowMs = 300000) { // 5 minute default const history = priceHistory.get(symbol) || []; const cutoff = Date.now() - windowMs; const relevantPrices = history.filter(p => p.timestamp >= cutoff); if (relevantPrices.length === 0) return null; const sum = relevantPrices.reduce((acc, p) => acc + p.price, 0); return sum / relevantPrices.length; } // Update price with simulated volatility function updatePrice(symbol, basePrice, volatility = 0.001) { const change = (Math.random() - 0.5) * 2 * volatility * basePrice; const newPrice = Math.max(basePrice + change, 0.01); const timestamp = Date.now(); prices.set(symbol, { symbol, price: newPrice, timestamp, sources: ['simulator'], confidence: 1.0, }); // Update history const history = priceHistory.get(symbol) || []; history.push({ price: newPrice, timestamp }); if (history.length > MAX_HISTORY) history.shift(); priceHistory.set(symbol, history); return newPrice; } // Periodic price updates setInterval(() => { Object.entries(DEFAULT_PRICES).forEach(([symbol, basePrice]) => { const current = prices.get(symbol); updatePrice(symbol, current?.price || basePrice); }); }, UPDATE_INTERVAL); // HTTP Server const server = http.createServer((req, res) => { res.setHeader('Content-Type', 'application/json'); res.setHeader('Access-Control-Allow-Origin', '*'); const url = new URL(req.url, `http://localhost:${PORT}`); const path = url.pathname; // Health check if (path === '/health') { res.writeHead(200); res.end(JSON.stringify({ status: 'healthy', timestamp: Date.now() })); return; } // All prices if (path === '/prices') { const allPrices = {}; prices.forEach((data, symbol) => { const isStale = Date.now() - data.timestamp > STALE_THRESHOLD; allPrices[symbol] = { ...data, stale: isStale }; }); res.writeHead(200); res.end(JSON.stringify({ prices: allPrices, timestamp: Date.now() })); return; } // Single price if (path.startsWith('/price/')) { const symbol = decodeURIComponent(path.slice(7)).toUpperCase(); const data = prices.get(symbol); if (data) { const isStale = Date.now() - data.timestamp > STALE_THRESHOLD; res.writeHead(200); res.end(JSON.stringify({ ...data, stale: isStale })); } else { res.writeHead(404); res.end(JSON.stringify({ error: 'Symbol not found', symbol })); } return; } // TWAP if (path.startsWith('/twap/')) { const symbol = decodeURIComponent(path.slice(6)).toUpperCase(); const window = parseInt(url.searchParams.get('window')) || 300000; const twap = calculateTWAP(symbol, window); if (twap !== null) { res.writeHead(200); res.end(JSON.stringify({ symbol, twap, windowMs: window, timestamp: Date.now() })); } else { res.writeHead(404); res.end(JSON.stringify({ error: 'No price history for symbol', symbol })); } return; } // Default res.writeHead(404); res.end(JSON.stringify({ error: 'Not found' })); }); server.listen(PORT, '0.0.0.0', () => { console.log(`Oracle Service running on port ${PORT}`); console.log(`Tracking ${prices.size} price feeds`); console.log('Endpoints:'); console.log(' GET /health - Health check'); console.log(' GET /prices - All current prices'); console.log(' GET /price/:symbol - Price for specific symbol'); console.log(' GET /twap/:symbol - Time-weighted average price'); });