UNPKG

@mraicodedev/bsc-token-detector

Version:

🚀 Real-time BSC token detector with advanced filtering, health monitoring, and comprehensive event system. Detect new tokens on Binance Smart Chain as they are created!

1,196 lines (1,034 loc) • 36.7 kB
import { EventEmitter } from 'events'; import { ErrorFactory } from './utils/Errors.js'; import { ConfigurationManager } from './utils/ConfigurationManager.js'; import { APIDocumentation } from './utils/APIDocumentation.js'; import { Logger } from './utils/Logger.js'; import { ErrorRecoveryManager } from './utils/ErrorRecovery.js'; import { HealthMonitor, HealthStatus } from './utils/HealthMonitor.js'; import { RPCConnectionPool } from './core/RPCConnectionPool.js'; import { BlockScanner } from './core/BlockScanner.js'; import { MempoolMonitor } from './core/MempoolMonitor.js'; import { EventMonitor } from './core/EventMonitor.js'; import { TokenAnalyzer } from './core/TokenAnalyzer.js'; import { FilterEngine } from './core/FilterEngine.js'; import { ResultHandler } from './core/ResultHandler.js'; /** * Main BSC Token Detector class * Orchestrates all components to detect new tokens on Binance Smart Chain */ export class BSCTokenDetector extends EventEmitter { constructor(config) { super(); // Initialize configuration manager this.configManager = new ConfigurationManager(); // Initialize and validate configuration this.config = this.configManager.initialize(config || {}); // Initialize logger this.logger = new Logger().child('BSCTokenDetector'); // Initialize error recovery system this.errorRecovery = new ErrorRecoveryManager({ enableCircuitBreaker: true, enableRetry: true, circuitBreakerConfig: { failureThreshold: this.config.errorHandling?.circuitBreakerThreshold || 5, resetTimeout: this.config.errorHandling?.circuitBreakerResetTime || 60000 }, retryConfig: { maxAttempts: this.config.errorHandling?.maxRetryAttempts || 3, initialDelay: this.config.errorHandling?.retryDelay || 1000, maxDelay: this.config.errorHandling?.maxRetryDelay || 30000 } }); // Initialize health monitoring system this.healthMonitor = new HealthMonitor({ checkInterval: this.config.monitoring?.healthCheckInterval || 30000, enableAutoCheck: this.config.monitoring?.enableHealthCheck !== false, alertThresholds: { consecutiveFailures: 3, failureRate: 50, responseTime: 5000 } }); // Initialize state this.isRunning = false; this.startTime = 0; this.components = {}; // Error handling state this.errorStats = { totalErrors: 0, recoveredErrors: 0, criticalErrors: 0, lastError: null, errorsByType: new Map() }; // Initialize all components this.initializeComponents(); // Setup component event handlers this.setupEventHandlers(); // Setup error handling and health monitoring this.setupErrorHandling(); this.setupHealthMonitoring(); this.logger.info('BSC Token Detector initialized successfully with enhanced error handling'); } /** * Start the token detection process with error recovery */ async start() { if (this.isRunning) { throw new Error('Detector is already running'); } this.logger.info('Starting BSC Token Detector with enhanced error handling...'); try { // Start with error recovery await this.errorRecovery.executeWithRecovery(async () => { this.isRunning = true; this.startTime = Date.now(); // Start health monitoring first this.healthMonitor.start(); // Start components in order with circuit breaker protection await this.errorRecovery.executeWithRecovery( () => this.components.blockScanner.start(), { operationName: 'startBlockScanner', circuitBreakerName: 'blockScanner', context: { component: 'blockScanner' } } ); if (this.config.monitoring.enableMempool) { await this.errorRecovery.executeWithRecovery( () => this.components.mempoolMonitor.start(), { operationName: 'startMempoolMonitor', circuitBreakerName: 'mempoolMonitor', context: { component: 'mempoolMonitor' } } ); } // Perform initial health check await this.healthCheck(); return true; }, { operationName: 'startDetector', context: { operation: 'start' } }); this.logger.info('BSC Token Detector started successfully'); this.emit('statusChanged', this.getSystemStatus()); } catch (error) { this.logger.error('Failed to start BSC Token Detector:', error); this.isRunning = false; // Stop health monitoring if it was started this.healthMonitor.stop(); // Handle the startup error await this.handleError(error, 'startup'); throw error; } } /** * Stop the token detection process gracefully */ async stop() { if (!this.isRunning) { return; } this.logger.info('Stopping BSC Token Detector gracefully...'); this.isRunning = false; try { // Stop components gracefully with timeout protection const stopOperations = []; stopOperations.push( this.errorRecovery.executeWithRecovery( () => this.components.blockScanner.stop(), { operationName: 'stopBlockScanner', enableCircuitBreaker: false, // Don't use circuit breaker for shutdown context: { component: 'blockScanner', operation: 'stop' } } ) ); stopOperations.push( this.errorRecovery.executeWithRecovery( () => this.components.mempoolMonitor.stop(), { operationName: 'stopMempoolMonitor', enableCircuitBreaker: false, context: { component: 'mempoolMonitor', operation: 'stop' } } ) ); // Wait for all components to stop (with timeout) await Promise.race([ Promise.allSettled(stopOperations), this.createTimeoutPromise(10000, 'Component shutdown timeout') ]); // Stop health monitoring this.healthMonitor.stop(); // Reset error recovery state this.errorRecovery.resetAllCircuitBreakers(); this.logger.info('BSC Token Detector stopped successfully'); this.emit('statusChanged', this.getSystemStatus()); } catch (error) { this.logger.error('Error stopping BSC Token Detector:', error); await this.handleError(error, 'shutdown'); throw error; } } /** * Update configuration at runtime */ configure(newConfig, options = {}) { try { const result = this.configManager.updateConfiguration(newConfig, options); const oldConfig = result.oldConfig; this.config = result.newConfig; // Update component configurations if (newConfig.filters && this.components?.filterEngine) { this.components.filterEngine.updateFilters(newConfig.filters); } if (newConfig.performance && this.components?.resultHandler) { this.components.resultHandler.config = { ...this.components.resultHandler.config, ...newConfig.performance }; } // Update RPC pool configuration if needed if (newConfig.rpcProviders && this.components?.rpcPool) { this.components.rpcPool.updateProviders(newConfig.rpcProviders); } // Update monitoring configuration if needed if (newConfig.monitoring) { if (this.components?.blockScanner && typeof this.components.blockScanner.updateConfig === 'function') { this.components.blockScanner.updateConfig(newConfig.monitoring); } if (this.components?.mempoolMonitor && typeof this.components.mempoolMonitor.updateConfig === 'function') { this.components.mempoolMonitor.updateConfig(newConfig.monitoring); } } this.logger.info('Configuration updated successfully'); this.emit('configurationChanged', { oldConfig, newConfig: this.config }); return result; } catch (error) { this.logger.error('Failed to update configuration:', error); throw error; } } /** * Get current detector status */ getStatus() { const resultStats = this.components.resultHandler.getStats(); return { isRunning: this.isRunning, currentBlock: this.components.blockScanner.getCurrentBlock(), confirmedBlock: this.components.blockScanner.getConfirmedBlock(), tokensDetected: resultStats.totalDetected, pendingTokens: resultStats.totalPending, uptime: this.isRunning ? Date.now() - this.startTime : 0, rpcHealth: this.components.rpcPool.getStatus(), components: { blockScanner: this.components.blockScanner.getStatus(), mempoolMonitor: this.components.mempoolMonitor.getStatus(), eventMonitor: { isHealthy: true, lastCheck: Date.now(), errorCount: 0 }, tokenAnalyzer: this.components.tokenAnalyzer.getCacheStats(), filterEngine: this.components.filterEngine.getFilterStats(), resultHandler: resultStats }, config: { enableMempool: this.config.monitoring.enableMempool, pollInterval: this.config.monitoring.pollInterval, blockConfirmations: this.config.performance.blockConfirmations, activeFilters: this.components.filterEngine.hasActiveFilters() } }; } /** * Get list of detected tokens */ getDetectedTokens() { return this.components.resultHandler.getResults(); } /** * Get pending tokens */ getPendingTokens() { return this.components.resultHandler.getPendingResults(); } /** * Get all results (detected + pending) */ getAllResults() { return this.components.resultHandler.getAllResults(); } /** * Search tokens by criteria */ searchTokens(criteria) { return this.components.resultHandler.searchResults(criteria); } /** * Clear all results */ clearResults() { this.components.resultHandler.clearResults(); this.logger.info('All results cleared'); } /** * Register callback for token detection events */ onTokenDetected(callback) { if (typeof callback !== 'function') { throw new Error('Callback must be a function'); } this.on('tokenDetected', callback); return this; // Enable method chaining } /** * Register callback for pending token detection events */ onPendingToken(callback) { if (typeof callback !== 'function') { throw new Error('Callback must be a function'); } this.on('pendingToken', callback); return this; } /** * Register callback for error events */ onError(callback) { if (typeof callback !== 'function') { throw new Error('Callback must be a function'); } this.on('error', callback); return this; } /** * Register callback for enhanced error events */ onEnhancedError(callback) { if (typeof callback !== 'function') { throw new Error('Callback must be a function'); } this.on('enhancedError', callback); return this; } /** * Register callback for status change events */ onStatusChanged(callback) { if (typeof callback !== 'function') { throw new Error('Callback must be a function'); } this.on('statusChanged', callback); return this; } /** * Register callback for configuration change events */ onConfigurationChanged(callback) { if (typeof callback !== 'function') { throw new Error('Callback must be a function'); } this.on('configurationChanged', callback); return this; } /** * Register callback for health status change events */ onHealthStatusChanged(callback) { if (typeof callback !== 'function') { throw new Error('Callback must be a function'); } this.on('healthStatusChanged', callback); return this; } /** * Register callback for health alert events */ onHealthAlert(callback) { if (typeof callback !== 'function') { throw new Error('Callback must be a function'); } this.on('healthAlert', callback); return this; } /** * Register callback for circuit breaker activation events */ onCircuitBreakerActivated(callback) { if (typeof callback !== 'function') { throw new Error('Callback must be a function'); } this.on('circuitBreakerActivated', callback); return this; } /** * Register callback for provider recovery events */ onProviderRecovered(callback) { if (typeof callback !== 'function') { throw new Error('Callback must be a function'); } this.on('providerRecovered', callback); return this; } /** * Register multiple callbacks at once */ registerCallbacks(callbacks) { if (!callbacks || typeof callbacks !== 'object') { throw new Error('Callbacks must be an object'); } const validEvents = [ 'tokenDetected', 'pendingToken', 'error', 'enhancedError', 'statusChanged', 'configurationChanged', 'healthStatusChanged', 'healthAlert', 'circuitBreakerActivated', 'providerRecovered' ]; for (const [event, callback] of Object.entries(callbacks)) { if (!validEvents.includes(event)) { throw new Error(`Invalid event: ${event}. Valid events are: ${validEvents.join(', ')}`); } if (typeof callback !== 'function') { throw new Error(`Callback for event '${event}' must be a function`); } this.on(event, callback); } return this; } /** * Remove specific callback for an event */ removeCallback(event, callback) { this.removeListener(event, callback); return this; } /** * Remove all callbacks for a specific event */ removeAllCallbacks(event) { this.removeAllListeners(event); return this; } /** * Get list of registered events and their listener counts */ getRegisteredEvents() { const events = this.eventNames(); const eventInfo = {}; for (const event of events) { eventInfo[event] = { listenerCount: this.listenerCount(event), maxListeners: this.getMaxListeners() }; } return eventInfo; } /** * Initialize all components */ initializeComponents() { try { // Initialize RPC connection pool this.components.rpcPool = new RPCConnectionPool( this.config.rpcProviders, this.config.performance ); // Initialize block scanner this.components.blockScanner = new BlockScanner( this.components.rpcPool, this.config.monitoring ); // Initialize mempool monitor this.components.mempoolMonitor = new MempoolMonitor( this.components.rpcPool, { pollInterval: this.config.monitoring.mempoolPollInterval, enabled: this.config.monitoring.enableMempool } ); // Initialize event monitor this.components.eventMonitor = new EventMonitor(this.components.rpcPool); // Initialize token analyzer this.components.tokenAnalyzer = new TokenAnalyzer(this.components.rpcPool); // Initialize filter engine this.components.filterEngine = new FilterEngine(this.config.filters); // Initialize result handler this.components.resultHandler = new ResultHandler({ maxResults: this.config.performance.cacheSize, enableDeduplication: true }); this.logger.debug('All components initialized successfully'); } catch (error) { this.logger.error('Failed to initialize components:', error); throw error; } } /** * Setup event handlers between components with error recovery */ setupEventHandlers() { // Block scanner events with error recovery this.components.blockScanner.on('newBlock', async (blockData) => { await this.errorRecovery.executeWithRecovery(async () => { await this.components.eventMonitor.analyzeBlock(blockData); }, { operationName: 'analyzeBlock', circuitBreakerName: 'eventMonitor', context: { blockNumber: blockData.number } }).catch(error => { this.handleError(error, 'blockAnalysis'); }); }); // Mempool monitor events with error recovery this.components.mempoolMonitor.on('pendingTransaction', async (txData) => { await this.errorRecovery.executeWithRecovery(async () => { // Apply pending filters first if (this.components.filterEngine.applyPendingFilters(txData)) { await this.components.eventMonitor.analyzePendingTx(txData); } }, { operationName: 'analyzePendingTransaction', circuitBreakerName: 'eventMonitor', context: { txHash: txData.hash } }).catch(error => { this.handleError(error, 'pendingTxAnalysis'); }); }); this.components.mempoolMonitor.on('pendingTokenDetected', async (tokenInfo) => { await this.errorRecovery.executeWithRecovery(async () => { const processedPending = this.components.resultHandler.processPendingToken(tokenInfo); if (processedPending) { this.emit('pendingToken', processedPending); } }, { operationName: 'processPendingToken', enableCircuitBreaker: false, // Result processing shouldn't use circuit breaker context: { contractAddress: tokenInfo.contractAddress } }).catch(error => { this.handleError(error, 'pendingTokenProcessing'); }); }); // Event monitor events with error recovery this.components.eventMonitor.on('tokenCreated', async (tokenInfo) => { await this.errorRecovery.executeWithRecovery(async () => { // Get detailed token information with error recovery const detailedToken = await this.errorRecovery.executeWithRecovery( () => this.components.tokenAnalyzer.getTokenInfo(tokenInfo.contractAddress), { operationName: 'getTokenInfo', circuitBreakerName: 'tokenAnalyzer', context: { contractAddress: tokenInfo.contractAddress } } ); // Merge with creation info const completeTokenInfo = { ...detailedToken, ...tokenInfo, creationBlock: tokenInfo.creationBlock, creationTimestamp: tokenInfo.creationTimestamp, transactionHash: tokenInfo.transactionHash }; // Apply filters if (this.components.filterEngine.applyFilters(completeTokenInfo)) { const processedToken = this.components.resultHandler.processDetectedToken(completeTokenInfo); if (processedToken) { this.emit('tokenDetected', processedToken); } } }, { operationName: 'processDetectedToken', enableCircuitBreaker: false, context: { contractAddress: tokenInfo.contractAddress } }).catch(error => { this.handleError(error, 'tokenProcessing'); }); }); this.components.eventMonitor.on('pendingTokenCreation', async (pendingInfo) => { await this.errorRecovery.executeWithRecovery(async () => { const analyzedPending = await this.errorRecovery.executeWithRecovery( () => this.components.tokenAnalyzer.analyzePendingToken(pendingInfo), { operationName: 'analyzePendingToken', circuitBreakerName: 'tokenAnalyzer', context: { txHash: pendingInfo.transactionHash } } ); if (analyzedPending) { const processedPending = this.components.resultHandler.processPendingToken(analyzedPending); if (processedPending) { this.emit('pendingToken', processedPending); } } }, { operationName: 'processPendingTokenCreation', enableCircuitBreaker: false, context: { txHash: pendingInfo.transactionHash } }).catch(error => { this.handleError(error, 'pendingTokenCreationProcessing'); }); }); // Component error handling with enhanced error processing const components = ['blockScanner', 'mempoolMonitor', 'rpcPool']; components.forEach(componentName => { if (this.components[componentName]) { this.components[componentName].on('error', async (error) => { await this.handleError(error, componentName); }); // Handle circuit breaker events from RPC pool if (componentName === 'rpcPool') { this.components[componentName].on('circuitBreakerActivated', (event) => { this.logger.warn(`Circuit breaker activated for RPC provider`, event); this.emit('circuitBreakerActivated', event); }); this.components[componentName].on('providerRecovered', (event) => { this.logger.info(`RPC provider recovered`, event); this.emit('providerRecovered', event); }); } } }); this.logger.debug('Enhanced event handlers setup completed'); } /** * Create timeout promise utility */ createTimeoutPromise(timeout, message = 'Operation timeout') { return new Promise((_, reject) => { setTimeout(() => { reject(new Error(message)); }, timeout); }); } /** * Sleep utility */ sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } /** * Get error recovery status */ getErrorRecoveryStatus() { return this.errorRecovery.getStats(); } /** * Get health monitoring status */ getHealthMonitoringStatus() { return this.healthMonitor.getMetrics(); } /** * Force circuit breaker state for testing/debugging */ setCircuitBreakerState(name, state) { this.errorRecovery.setCircuitBreakerState(name, state); this.logger.info(`Circuit breaker '${name}' forced to state: ${state}`); } /** * Reset all circuit breakers */ resetCircuitBreakers() { this.errorRecovery.resetAllCircuitBreakers(); this.logger.info('All circuit breakers reset'); } /** * Enable/disable health check for specific component */ setHealthCheckEnabled(component, enabled) { return this.healthMonitor.setHealthCheckEnabled(component, enabled); } /** * Get recent health alerts */ getHealthAlerts(limit = 50) { return this.healthMonitor.getRecentAlerts(limit); } /** * Clear health alert history */ clearHealthAlerts() { this.healthMonitor.clearAlerts(); } /** * Get component by name */ getComponent(name) { return this.components[name]; } /** * Get current configuration */ getConfig() { return this.configManager.getCurrentConfiguration(); } /** * Get configuration section */ getConfigSection(sectionName) { return this.configManager.getConfigurationSection(sectionName); } /** * Reset configuration to defaults */ resetConfig() { try { const result = this.configManager.resetToDefaults(); this.config = result; this.logger.info('Configuration reset to defaults'); this.emit('configurationReset', { newConfig: this.config }); return result; } catch (error) { this.logger.error('Failed to reset configuration:', error); throw error; } } /** * Export configuration */ exportConfig(format = 'json') { return this.configManager.exportConfiguration(format); } /** * Import configuration */ importConfig(configString, options = {}) { try { const result = this.configManager.importConfiguration(configString, options); this.config = result.newConfig; this.logger.info('Configuration imported successfully'); this.emit('configurationImported', result); return result; } catch (error) { this.logger.error('Failed to import configuration:', error); throw error; } } /** * Get configuration history */ getConfigHistory(limit = 10) { return this.configManager.getConfigurationHistory(limit); } /** * Get configuration schema */ getConfigSchema() { return this.configManager.getSchema(); } /** * Get default configuration */ getDefaultConfig() { return this.configManager.getDefaults(); } /** * Validate configuration without applying it */ validateConfig(config) { return this.configManager.validateOnly(config); } /** * Get configuration statistics */ getConfigStats() { return this.configManager.getConfigurationStats(); } /** * Get API documentation */ static getAPIDocumentation() { return APIDocumentation.getFullDocumentation(); } /** * Generate API documentation in markdown format */ static generateAPIDocumentation() { return APIDocumentation.generateMarkdown(); } /** * Get detailed statistics */ getDetailedStats() { return { detector: this.getStatus(), rpcPool: this.components.rpcPool.getStatus(), blockScanner: this.components.blockScanner.getStatus(), mempoolMonitor: this.components.mempoolMonitor.getStatus(), tokenAnalyzer: this.components.tokenAnalyzer.getCacheStats(), filterEngine: this.components.filterEngine.getDetailedStatus(), resultHandler: this.components.resultHandler.getStats() }; } /** * Export results in various formats */ exportResults(format = 'json') { return this.components.resultHandler.exportResults(format); } /** * Wait for a specific transaction to be confirmed and analyzed */ async waitForTokenConfirmation(txHash, maxWaitTime = 300000) { return this.components.tokenAnalyzer.waitForConfirmation(txHash, maxWaitTime); } /** * Register callback for token confirmation */ onTokenConfirmation(txHash, callback) { this.components.tokenAnalyzer.onConfirmation(txHash, callback); } /** * Cleanup old data and optimize performance */ cleanup() { try { this.components.tokenAnalyzer.cleanupPendingTokens(); // Clear old error statistics if (this.errorStats.errorsByType.size > 100) { this.errorStats.errorsByType.clear(); this.errorStats.totalErrors = 0; this.errorStats.recoveredErrors = 0; this.errorStats.criticalErrors = 0; } // Clear old health alerts this.healthMonitor.clearAlerts(); this.logger.debug('Cleanup completed successfully'); } catch (error) { this.logger.error('Error during cleanup:', error); } } /** * Destroy detector and cleanup all resources */ async destroy() { this.logger.info('Destroying BSC Token Detector...'); try { // Stop if running if (this.isRunning) { await this.stop(); } // Destroy health monitor this.healthMonitor.destroy(); // Destroy error recovery this.errorRecovery.destroy(); // Destroy configuration manager if (this.configManager) { this.configManager.destroy(); } // Destroy RPC pool if (this.components.rpcPool) { this.components.rpcPool.destroy(); } // Clear all event listeners this.removeAllListeners(); this.logger.info('BSC Token Detector destroyed successfully'); } catch (error) { this.logger.error('Error during destruction:', error); throw error; } } /** * Setup comprehensive error handling */ setupErrorHandling() { // Handle unhandled errors this.on('error', (error) => { this.handleError(error, 'detector'); }); // Setup error recovery event handlers this.errorRecovery.on = this.errorRecovery.on || (() => {}); // Setup configuration manager event handlers if (this.configManager && typeof this.configManager.on === 'function') { this.configManager.on('configurationUpdated', (event) => { this.emit('configurationChanged', event); }); this.configManager.on('configurationReset', (event) => { this.emit('configurationReset', event); }); this.configManager.on('configurationImported', (event) => { this.emit('configurationImported', event); }); this.configManager.on('configurationUpdateFailed', (event) => { this.emit('configurationUpdateFailed', event); }); } this.logger.debug('Error handling system initialized'); } /** * Setup health monitoring for all components */ setupHealthMonitoring() { // Register health checks for each component this.healthMonitor.registerHealthCheck('rpcPool', async () => { const status = this.components.rpcPool.getStatus(); return { status: status.pool.healthyProviders > 0 ? HealthStatus.HEALTHY : HealthStatus.UNHEALTHY, message: `${status.pool.healthyProviders}/${status.pool.totalProviders} providers healthy`, details: status }; }, { critical: true, timeout: 5000 }); this.healthMonitor.registerHealthCheck('blockScanner', async () => { const status = this.components.blockScanner.getStatus(); return { status: status.isHealthy ? HealthStatus.HEALTHY : HealthStatus.UNHEALTHY, message: status.isHealthy ? 'Block scanner operational' : 'Block scanner unhealthy', details: status }; }, { critical: true, timeout: 3000 }); this.healthMonitor.registerHealthCheck('mempoolMonitor', async () => { const status = this.components.mempoolMonitor.getStatus(); return { status: status.isHealthy ? HealthStatus.HEALTHY : HealthStatus.UNHEALTHY, message: status.isHealthy ? 'Mempool monitor operational' : 'Mempool monitor unhealthy', details: status }; }, { critical: false, timeout: 3000 }); this.healthMonitor.registerHealthCheck('tokenAnalyzer', async () => { const stats = this.components.tokenAnalyzer.getCacheStats(); const isHealthy = stats.errorRate < 0.1; // Less than 10% error rate return { status: isHealthy ? HealthStatus.HEALTHY : HealthStatus.DEGRADED, message: `Token analyzer error rate: ${(stats.errorRate * 100).toFixed(1)}%`, details: stats }; }, { critical: false, timeout: 2000 }); this.healthMonitor.registerHealthCheck('filterEngine', async () => { const stats = this.components.filterEngine.getFilterStats(); return { status: HealthStatus.HEALTHY, message: 'Filter engine operational', details: stats }; }, { critical: false, timeout: 1000 }); // Setup health monitor event handlers this.healthMonitor.on('statusChanged', (event) => { this.logger.info(`System health status changed: ${event.previousStatus} -> ${event.currentStatus}`, { message: event.message }); this.emit('healthStatusChanged', event); }); this.healthMonitor.on('alert', (alert) => { this.logger.warn(`Health alert: ${alert.message}`, alert); this.emit('healthAlert', alert); }); this.logger.debug('Health monitoring system initialized'); } /** * Handle errors with recovery mechanisms */ async handleError(error, context = 'unknown') { // Update error statistics this.errorStats.totalErrors++; this.errorStats.lastError = { error: error.message, context, timestamp: new Date().toISOString() }; // Track error by type const errorType = error.constructor.name; const currentCount = this.errorStats.errorsByType.get(errorType) || 0; this.errorStats.errorsByType.set(errorType, currentCount + 1); // Wrap unknown errors const wrappedError = ErrorFactory.wrapUnknownError(error, { context }); // Log error with appropriate level if (wrappedError.isRecoverable()) { this.logger.warn(`Recoverable error in ${context}:`, wrappedError.toJSON()); } else { this.logger.error(`Critical error in ${context}:`, wrappedError.toJSON()); this.errorStats.criticalErrors++; } // Attempt recovery if error is recoverable if (wrappedError.isRecoverable()) { try { await this.attemptErrorRecovery(wrappedError, context); this.errorStats.recoveredErrors++; } catch (recoveryError) { this.logger.error(`Error recovery failed for ${context}:`, recoveryError); } } // Emit error event with enhanced information this.emit('enhancedError', { originalError: error, wrappedError, context, recoverable: wrappedError.isRecoverable(), retryable: wrappedError.isRetryable(), stats: this.getErrorStats() }); } /** * Attempt error recovery based on error type and context */ async attemptErrorRecovery(error, context) { switch (context) { case 'rpcPool': // RPC pool errors - trigger health check and failover await this.components.rpcPool.performHealthCheck(); break; case 'blockScanner': // Block scanner errors - restart if critical if (error.code === 'CRITICAL_ERROR') { this.logger.info('Attempting to restart block scanner due to critical error'); await this.components.blockScanner.stop(); await this.sleep(5000); // Wait 5 seconds await this.components.blockScanner.start(); } break; case 'tokenAnalyzer': // Token analyzer errors - clear cache if corrupted if (error.code === 'PARSE_ERROR') { this.logger.info('Clearing token analyzer cache due to parse error'); this.components.tokenAnalyzer.clearCache(); } break; default: this.logger.debug(`No specific recovery mechanism for context: ${context}`); } } /** * Get error statistics */ getErrorStats() { return { ...this.errorStats, errorsByType: Object.fromEntries(this.errorStats.errorsByType), recoveryRate: this.errorStats.totalErrors > 0 ? (this.errorStats.recoveredErrors / this.errorStats.totalErrors) * 100 : 0 }; } /** * Health check for all components (enhanced version) */ async healthCheck() { try { return await this.healthMonitor.performHealthChecks(); } catch (error) { this.logger.error('Health check failed:', error); return { status: HealthStatus.CRITICAL, message: `Health check failed: ${error.message}`, lastCheck: new Date().toISOString(), components: {}, metrics: {} }; } } /** * Get comprehensive system status including health and error information */ getSystemStatus() { const baseStatus = this.getStatus(); const healthStatus = this.healthMonitor.getSystemHealth(); const errorStats = this.getErrorStats(); const recoveryStats = this.errorRecovery.getStats(); return { ...baseStatus, health: healthStatus, errorHandling: { stats: errorStats, recovery: recoveryStats, circuitBreakers: recoveryStats.circuitBreakers }, monitoring: this.healthMonitor.getMetrics() }; } }