UNPKG

dop-stick

Version:

Source control tooling for versionable-upgradeable smart contracts

1,012 lines 41.9 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TypechainProcessor = void 0; const abi_1 = require("@ethersproject/abi"); const fs_1 = require("fs"); const fs_2 = require("fs"); const path_1 = __importDefault(require("path")); const logger_1 = require("../logsAndMetrics/core/logger"); const types_1 = require("./types"); const abi_cache_1 = require("../cache/abi-cache"); class TypechainProcessor { constructor(typechainPath = 'typechain-types', enableCache = true) { this.initialized = false; this.initializationPromise = null; this.eventCache = new Map(); this.eventCacheTimestamps = new Map(); this.enableCache = enableCache; this.typechainPath = path_1.default.join(process.cwd(), typechainPath); this.abiCache = abi_cache_1.AbiCache.getInstance(); this.selectorLookupCache = new Map(); this.facetsCache = new Map(); this.factoryMap = new Map(); this.eventHandlers = new Map(); this.optimizedLookup = { selectorToFacet: new Map(), facetToSelectors: new Map(), metadata: { lastUpdate: new Date(), version: TypechainProcessor.VERSION, entries: 0 } }; if (!(0, fs_2.existsSync)(this.typechainPath)) { throw new types_1.TypechainProcessorError(`Typechain directory not found at: ${this.typechainPath}`, 'TYPECHAIN_DIR_NOT_FOUND'); } } static getInstance(typechainPath, enableCache = true) { if (!TypechainProcessor.instance) { TypechainProcessor.instance = new TypechainProcessor(typechainPath, enableCache); } return TypechainProcessor.instance; } async ensureInitialized() { if (this.initializationPromise) { await this.initializationPromise; return; } if (!this.initialized) { this.initializationPromise = this.initialize(); await this.initializationPromise; } } async initialize() { if (this.initialized) return; if (this.initializationPromise) return this.initializationPromise; this.initializationPromise = (async () => { try { const startTime = performance.now(); // Try to load from cache first if (this.enableCache) { const loaded = await this.loadFromCache(); if (loaded) { // Rebuild event cache from facetsCache for (const [contractName, processed] of this.facetsCache) { if (processed.facetInfo.events.size > 0) { const cleanedName = this.cleanContractName(contractName); this.eventCache.set(cleanedName, processed.facetInfo.events); this.eventCacheTimestamps.set(cleanedName, Date.now()); logger_1.Logger.debug(`Restored ${processed.facetInfo.events.size} events for facet: ${cleanedName}`); } } this.initialized = true; logger_1.Logger.info('Initialized from cache successfully'); return; } } // If no cache or cache disabled, build from scratch this.eventCache.clear(); this.eventCacheTimestamps.clear(); // First load factory mappings await this.loadFactoryMappings(); logger_1.Logger.debug(`Loaded ${this.factoryMap.size} factory mappings`); // Then build the optimized lookup await this.buildOptimizedLookup(); // Save to cache if enabled if (this.enableCache) { await this.saveToCache(); } const duration = ((performance.now() - startTime) / 1000).toFixed(1); this.initialized = true; logger_1.Logger.info(`TypechainProcessor initialized in ${duration}s`); } catch (error) { logger_1.Logger.error('Failed to initialize TypechainProcessor:', error); throw error; } finally { this.initializationPromise = null; } })(); return this.initializationPromise; } async loadFactoryMappings() { try { const files = await this.findTypechainFiles(); for (const file of files) { const content = await fs_1.promises.readFile(file, 'utf8'); const matches = Array.from(content.matchAll(TypechainProcessor.FACTORY_PATTERN)); for (const match of matches) { const [_, contractName, importPath] = match; if (contractName && importPath) { // Clean the contract name before storing const cleanedName = this.cleanContractName(contractName); this.factoryMap.set(cleanedName, importPath); } } } logger_1.Logger.debug(`Loaded ${this.factoryMap.size} factory mappings`); } catch (error) { logger_1.Logger.error('Failed to load factory mappings:', error); throw error; } } async findTypechainFiles() { const files = []; async function walk(dir) { const entries = await fs_1.promises.readdir(dir, { withFileTypes: true }); for (const entry of entries) { const fullPath = path_1.default.join(dir, entry.name); if (entry.isDirectory()) { await walk(fullPath); } else if (entry.isFile() && entry.name.endsWith('.ts')) { files.push(fullPath); } } } await walk(this.typechainPath); return files; } async getFactoryInfo(contractName) { await this.ensureInitialized(); return this.factoryMap.get(contractName) || null; } async listFactories() { await this.ensureInitialized(); return new Map(this.factoryMap); } async buildOptimizedLookup() { try { const startTime = performance.now(); const total = this.factoryMap.size; let processed = 0; let errors = 0; const entries = Array.from(this.factoryMap.entries()); const chunks = entries.reduce((acc, curr, i) => { const chunkIndex = Math.floor(i / TypechainProcessor.CONCURRENCY_LIMIT); acc[chunkIndex] = [...(acc[chunkIndex] || []), curr]; return acc; }, []); for (const chunk of chunks) { await Promise.all(chunk.map(async ([contractName, factoryPath]) => { try { logger_1.Logger.debug(`Processing facet: ${contractName} from ${factoryPath}`); const facetInfo = await this.processFacet(contractName, factoryPath); if (facetInfo) { const cleanedName = this.cleanContractName(contractName); this.updateLookupStructures(cleanedName, facetInfo); // Store in facetsCache with events this.facetsCache.set(cleanedName, { facetInfo: { ...facetInfo, events: facetInfo.events }, timestamp: Date.now(), metadata: { lastProcessed: new Date(), version: TypechainProcessor.VERSION, functionCount: facetInfo.functions.size, eventCount: facetInfo.events.size } }); // Cache events if (facetInfo.events.size > 0) { this.eventCache.set(cleanedName, facetInfo.events); this.eventCacheTimestamps.set(cleanedName, Date.now()); logger_1.Logger.debug(`Cached ${facetInfo.events.size} events for facet: ${cleanedName}`); } processed++; if (processed % Math.max(1, Math.floor(total / 10)) === 0) { const progress = (processed / total * 100).toFixed(1); logger_1.Logger.info(`Processing progress: ${progress}% (${processed}/${total})`); } } else { errors++; logger_1.Logger.warn(`No facet info returned for ${contractName}`); } } catch (error) { errors++; logger_1.Logger.error(`Error processing ${contractName} from ${factoryPath}:`, error); } })); } const duration = ((performance.now() - startTime) / 1000).toFixed(1); logger_1.Logger.info(`Built optimized lookup in ${duration}s: ${processed}/${total} facets processed (${errors} errors)`); if (errors > 0) { logger_1.Logger.warn(`Failed to process ${errors} facets`); } } catch (error) { logger_1.Logger.error('Failed to build optimized lookup:', error); throw error; } } updateLookupStructures(contractName, facetInfo) { if (!contractName || !facetInfo) { logger_1.Logger.warn('Invalid input to updateLookupStructures'); return; } try { // Update selector to facet mapping for (const [selector, functionInfo] of facetInfo.enhancedFunctions) { if (!selector || !functionInfo) continue; this.optimizedLookup.selectorToFacet.set(selector, { facetName: contractName, functionInfo }); } // Update facet to selectors mapping this.optimizedLookup.facetToSelectors.set(contractName, new Set(facetInfo.enhancedFunctions.keys())); // Update metadata this.optimizedLookup.metadata.entries = this.optimizedLookup.selectorToFacet.size; this.optimizedLookup.metadata.lastUpdate = new Date(); } catch (error) { logger_1.Logger.error(`Error updating lookup structures for ${contractName}:`, error); } } async processFacet(contractName, factoryPath) { try { // Clean up the contract name by removing any function names and factory suffixes const cleanedContractName = this.cleanContractName(contractName); const fullPath = path_1.default.join(this.typechainPath, factoryPath.endsWith('.ts') ? factoryPath : `${factoryPath}.ts`); if (!(0, fs_2.existsSync)(fullPath)) { logger_1.Logger.warn(`Factory file not found: ${fullPath}`); return null; } const content = await fs_1.promises.readFile(fullPath, 'utf8'); const abiString = await this.extractAndCleanAbi(content); if (!abiString) return null; const abi = JSON.parse(abiString); const iface = new abi_1.Interface(abi); // Process functions and events in parallel const [enhancedFunctions, enhancedEvents] = await Promise.all([ this.processContractFunctions(abi, iface), this.processContractEvents(abi) ]); const facetInfo = { name: cleanedContractName, path: factoryPath, abi, selectors: new Set(enhancedFunctions.keys()), functions: new Map(), events: new Map(), enhancedFunctions, enhancedEvents }; // Convert enhanced functions to legacy format using Map for (const [selector, func] of enhancedFunctions) { facetInfo.functions.set(selector, { name: func.name, signature: func.signature, fragment: func.fragment }); } // Convert events to Map format for (const [eventName, event] of enhancedEvents) { facetInfo.events.set(eventName, event.fragment); } return facetInfo; } catch (error) { logger_1.Logger.error(`Failed to process facet ${contractName}:`, error); return null; } } cleanContractName(name) { // Remove common suffixes and function names return name .replace(/__factory$/, '') // Remove factory suffix .replace(/Factory$/, '') // Also remove 'Factory' suffix without underscores .replace(/^get/, '') // Remove 'get' prefix if it exists .replace(/([A-Z])/g, ' $1') // Add space before capital letters .trim() // Remove extra spaces .replace(/\s+/g, '') // Remove all spaces .replace(/_+/g, '') // Remove underscores .replace(/Function$/, '') // Remove 'Function' suffix .replace(/Method$/, ''); // Remove 'Method' suffix } async extractAndCleanAbi(content) { try { // First try to match the const _abi declaration with 'as const' const abiMatch = content.match(/const\s+_abi\s*=\s*(\[[\s\S]*?\])\s*as\s*const/); if (abiMatch && abiMatch[1]) { let abiString = abiMatch[1]; // Clean up the string abiString = abiString .replace(/\s+/g, ' ') // Normalize whitespace .replace(/,(\s*[}\]])/g, '$1') // Remove trailing commas .trim(); // Remove leading/trailing whitespace try { // Parse to validate const abi = JSON.parse(abiString); if (Array.isArray(abi) && abi.length > 0) { // Validate ABI structure const isValid = abi.every(item => typeof item === 'object' && item !== null && ((item.type === 'function' && 'inputs' in item && 'outputs' in item) || (item.type === 'event' && 'inputs' in item && 'name' in item) || (item.type === 'constructor' && 'inputs' in item) || (item.type === 'fallback') || (item.type === 'receive'))); if (isValid) { return JSON.stringify(abi); } } } catch (parseError) { logger_1.Logger.debug('Initial parse failed, attempting cleanup:', parseError); // Additional cleanup for common typechain formatting abiString = abiString .replace(/(\w+):/g, '"$1":') // Quote unquoted property names .replace(/'/g, '"') // Replace single quotes with double quotes .replace(/,(\s*[}\]])/g, '$1'); // Remove trailing commas again after modifications try { const cleanedAbi = JSON.parse(abiString); if (Array.isArray(cleanedAbi) && cleanedAbi.length > 0) { return JSON.stringify(cleanedAbi); } } catch (secondError) { logger_1.Logger.debug('Cleanup parse failed:', secondError); } } } logger_1.Logger.debug('No valid ABI found in content'); logger_1.Logger.debug('Content preview:', content.substring(0, 200) + '...'); return null; } catch (error) { logger_1.Logger.error('Error extracting ABI:', error); return null; } } findCompleteJsonStructure(text) { try { let stack = []; let start = text.indexOf('[') !== -1 ? text.indexOf('[') : text.indexOf('{'); if (start === -1) return null; let openChar = text[start]; let closeChar = openChar === '[' ? ']' : '}'; for (let i = start; i < text.length; i++) { if (text[i] === openChar) { stack.push(openChar); } else if (text[i] === closeChar) { stack.pop(); if (stack.length === 0) { // Found complete structure return text.substring(start, i + 1); } } } return null; } catch (error) { logger_1.Logger.debug('Error finding complete JSON structure:', error); return null; } } validateAndCleanAbi(abiString) { try { // Remove any trailing commas abiString = abiString.replace(/,(\s*[}\]])/g, '$1'); // Handle 'as const' syntax abiString = abiString.replace(/\s+as\s+const\s*$/, ''); // Parse to validate const abi = JSON.parse(abiString); // Validate it's an array or object with ABI-like structure if (Array.isArray(abi)) { if (abi.length === 0) return null; // Check if array contains ABI-like objects const hasValidEntries = abi.some(entry => typeof entry === 'object' && entry !== null && ('type' in entry || 'name' in entry || 'inputs' in entry || 'outputs' in entry)); if (!hasValidEntries) return null; } else if (typeof abi === 'object' && abi !== null) { // Check if object has ABI-like properties const hasValidProps = Object.values(abi).some(value => typeof value === 'object' && value !== null && ('type' in value || 'name' in value || 'inputs' in value || 'outputs' in value)); if (!hasValidProps) return null; } else { return null; } return JSON.stringify(abi); } catch (error) { logger_1.Logger.debug('Failed to validate and clean ABI:', error); return null; } } async processContractFunctions(abi, iface) { const functions = new Map(); for (const item of abi) { if (item.type !== 'function') continue; if (!this.validateFunctionAbi(item)) { logger_1.Logger.debug(`Skipping invalid function ABI:`, item); continue; } try { const fragment = abi_1.FunctionFragment.from(item); const selector = iface.getSighash(fragment).toLowerCase(); functions.set(selector, { name: fragment.name, fullName: fragment.format('full'), selector, signature: fragment.format(), fragment, inputs: this.processParameters(item.inputs || []), outputs: this.processParameters(item.outputs || []), mutability: item.stateMutability, visibility: item.visibility || 'external' }); } catch (error) { logger_1.Logger.warn(`Failed to process function ${item.name}:`, error); } } return functions; } async processContractEvents(abi) { const events = new Map(); for (const item of abi) { if (item.type !== 'event') continue; try { const fragment = abi_1.EventFragment.from(item); events.set(fragment.name, { name: fragment.name, signature: fragment.format(), inputs: this.processParameters(item.inputs || []), anonymous: !!item.anonymous, fragment }); } catch (error) { logger_1.Logger.warn(`Failed to process event ${item.name}:`, error); } } return events; } // Optimized selector lookup async findContractBySelectors(selectors) { await this.ensureInitialized(); if (selectors.length === 0) return null; // Fast path: check in-memory cache first const firstSelector = selectors[0].toLowerCase(); const cachedMapping = this.selectorLookupCache.get(firstSelector); if (cachedMapping) { // For single selector queries, return immediately if (selectors.length === 1) { return this.createContractInfo(cachedMapping.facetName, [firstSelector]); } // For multiple selectors, verify all belong to the same facet const facetSelectors = this.optimizedLookup.facetToSelectors.get(cachedMapping.facetName); if (facetSelectors && selectors.every(s => facetSelectors.has(s.toLowerCase()))) { return this.createContractInfo(cachedMapping.facetName, selectors); } } // Slower path: check optimized lookup const mapping = this.optimizedLookup.selectorToFacet.get(firstSelector); if (!mapping) return null; // Cache the result for future lookups this.selectorLookupCache.set(firstSelector, mapping); return this.createContractInfo(mapping.facetName, selectors); } // Efficient contract info creation with minimal object copying async createContractInfo(facetName, selectors) { const functions = {}; // Batch process selectors for (const selector of selectors) { const info = this.optimizedLookup.selectorToFacet.get(selector); if (info) { functions[selector] = info.functionInfo; } } return { name: facetName, path: this.factoryMap.get(facetName) || '', functions }; } // Efficient batch processing of parameters processParameters(params) { return params.map(param => ({ name: param.name || '', type: param.type, ...(param.indexed !== undefined && { indexed: param.indexed }), ...(param.components && { components: this.processParameters(param.components) }) })); } // Memory-efficient ABI string cleaning with minimal string operations cleanAbiString(abiString) { const chunks = []; let inString = false; let current = ''; let depth = 0; // Single-pass processing for (let i = 0; i < abiString.length; i++) { const char = abiString[i]; // Handle strings if (char === '"' && abiString[i - 1] !== '\\') { inString = !inString; } // Track nested structures if (!inString) { if (char === '{' || char === '[') depth++; if (char === '}' || char === ']') depth--; } // Efficient string building if (inString || /[^\s,]/.test(char) || depth > 0) { current += char; } // Process complete chunks if (depth === 0 && current.length > 0 && (char === '}' || char === ']')) { chunks.push(current); current = ''; } } return chunks.join(','); } // Fast cache operations getCachedData(key) { const cached = this.facetsCache.get(key); if (!cached) return null; const age = Date.now() - cached.timestamp; if (age > TypechainProcessor.CACHE_DURATION) { this.facetsCache.delete(key); return null; } return cached; } // Efficient batch operations async batchProcessSelectors(selectorsBatch) { await this.ensureInitialized(); // Process all batches in parallel with controlled concurrency const results = await Promise.all(selectorsBatch.map(selectors => this.findContractBySelectors(selectors))); return results; } // Fast contract lookup async getContractInfo(contractName) { await this.ensureInitialized(); const selectors = this.optimizedLookup.facetToSelectors.get(contractName); if (!selectors) return null; const cached = this.getCachedData(contractName); if (cached) return cached.facetInfo; return null; } // Performance metrics getPerformanceMetrics() { return { totalContracts: this.facetsCache.size, totalSelectors: this.optimizedLookup.selectorToFacet.size, cacheSize: JSON.stringify(this.facetsCache).length, lastUpdateTimestamp: this.optimizedLookup.metadata.lastUpdate.getTime() }; } // Memory management clearCache() { this.facetsCache.clear(); this.optimizedLookup.selectorToFacet.clear(); this.optimizedLookup.facetToSelectors.clear(); this.optimizedLookup.metadata.lastUpdate = new Date(); this.optimizedLookup.metadata.entries = 0; } // ABI Validation and Normalization validateAndNormalizeAbi(abi) { try { for (const item of abi) { if (!item.type) { logger_1.Logger.warn('ABI item missing type property'); return false; } if (item.type === 'function') { if (!this.validateFunctionAbi(item)) return false; } else if (item.type === 'event') { if (!this.validateEventAbi(item)) return false; } } return true; } catch (error) { logger_1.Logger.error('ABI validation failed:', error); return false; } } validateFunctionAbi(item) { return !!(item.name && Array.isArray(item.inputs) && Array.isArray(item.outputs) && item.stateMutability); } validateEventAbi(item) { return !!(item.name && Array.isArray(item.inputs) && typeof item.anonymous === 'boolean'); } // Error Recovery async recoverFromError(error, context) { logger_1.Logger.error(`Error in ${context}:`, error); try { // Attempt to recover cached data if (this.enableCache) { await this.loadFromCache(); } // Rebuild necessary indexes await this.rebuildIndexes(); logger_1.Logger.info('Recovery completed successfully'); } catch (recoveryError) { logger_1.Logger.error('Recovery failed:', recoveryError); throw new types_1.TypechainProcessorError('Failed to recover from error', 'RECOVERY_FAILED'); } } // Event Handling async subscribeToEvents(contractName, eventNames, callback) { await this.ensureInitialized(); const facetInfo = await this.getContractInfo(contractName); if (!facetInfo) return false; const events = Array.from(facetInfo.enhancedEvents.entries()) .filter(([name]) => eventNames.includes(name)) .map(([_, event]) => event); if (events.length === 0) return false; // Register event handlers events.forEach(event => { this.eventHandlers.set(event.signature, callback); }); return true; } // Interface Implementation async getFacetInterfaces() { await this.ensureInitialized(); const interfaces = new Map(); for (const [facetName, facetInfo] of this.facetsCache) { try { interfaces.set(facetName, new abi_1.Interface(facetInfo.facetInfo.abi)); } catch (error) { logger_1.Logger.warn(`Failed to create interface for ${facetName}:`, error); } } return interfaces; } // Utility Methods async getFunctionsBySelector(selector) { await this.ensureInitialized(); const mapping = this.optimizedLookup.selectorToFacet.get(selector); return mapping ? { facetName: mapping.facetName, functionInfo: mapping.functionInfo } : null; } async getFacetSelectors(facetName) { await this.ensureInitialized(); return this.optimizedLookup.facetToSelectors.get(facetName) || null; } async validateSelector(selector) { return /^0x[0-9a-f]{8}$/i.test(selector); } // Diagnostic Methods async getDiagnostics() { await this.ensureInitialized(); return { totalFacets: this.facetsCache.size, totalSelectors: this.optimizedLookup.selectorToFacet.size, totalEvents: Array.from(this.facetsCache.values()) .reduce((acc, curr) => acc + curr.facetInfo.enhancedEvents.size, 0), cacheStatus: { enabled: this.enableCache, size: this.facetsCache.size, lastUpdate: this.optimizedLookup.metadata.lastUpdate }, version: TypechainProcessor.VERSION }; } async saveToCache() { try { await this.abiCache.save({ version: TypechainProcessor.VERSION, timestamp: Date.now(), lookup: this.optimizedLookup, facets: this.facetsCache, events: { cache: this.eventCache, timestamps: this.eventCacheTimestamps } }); logger_1.Logger.debug(`Saved cache with ${this.eventCache.size} event entries`); } catch (error) { logger_1.Logger.warn('Failed to save to cache:', error); } } async loadFromCache() { try { const cached = await this.abiCache.load(); if (!cached) return false; this.optimizedLookup = cached.lookup; this.facetsCache = cached.facets; // Restore event cache if (cached.events) { this.eventCache = cached.events.cache; this.eventCacheTimestamps = cached.events.timestamps; logger_1.Logger.debug(`Restored ${this.eventCache.size} event cache entries`); } else { logger_1.Logger.debug('No event cache found in stored cache'); this.eventCache.clear(); this.eventCacheTimestamps.clear(); } return true; } catch (error) { logger_1.Logger.warn('Failed to load from cache:', error); return false; } } async rebuildIndexes() { this.optimizedLookup.selectorToFacet.clear(); this.optimizedLookup.facetToSelectors.clear(); for (const [contractName, processed] of this.facetsCache) { this.updateLookupStructures(contractName, processed.facetInfo); } } async reloadContract(contractName) { try { const factoryPath = this.factoryMap.get(contractName); if (!factoryPath) return false; const facetInfo = await this.processFacet(contractName, factoryPath); if (!facetInfo) return false; this.updateLookupStructures(contractName, facetInfo); return true; } catch (error) { logger_1.Logger.error(`Failed to reload contract ${contractName}:`, error); return false; } } // Cache warmup for frequently used selectors warmupSelectorCache() { // Preload most frequently used selectors into memory const frequentSelectors = Array.from(this.optimizedLookup.selectorToFacet.entries()) .slice(0, 100); // Cache first 100 selectors for (const [selector, mapping] of frequentSelectors) { this.selectorLookupCache.set(selector, mapping); } } // Cache maintenance async performCacheMaintenance() { if (!this.enableCache) return; try { // Clear in-memory selector cache periodically this.selectorLookupCache.clear(); this.warmupSelectorCache(); // Maintain persistent cache await this.abiCache.maintain(); } catch (error) { logger_1.Logger.warn('Cache maintenance failed:', error); } } // Helper method to convert between Maps and objects if needed convertFunctionsToMap(functions) { return new Map(Object.entries(functions)); } convertEventsToMap(events) { return new Map(Object.entries(events)); } // Helper method for the reverse conversion if needed convertMapToObject(map) { return Object.fromEntries(map.entries()); } // Event handling methods async registerEventHandler(contractName, eventNames, callback) { try { await this.ensureInitialized(); const contract = this.facetsCache.get(contractName); if (!contract) { logger_1.Logger.warn(`Contract ${contractName} not found`); return false; } const events = []; for (const eventName of eventNames) { const event = contract.facetInfo.enhancedEvents.get(eventName); if (event) { events.push(event); } else { logger_1.Logger.warn(`Event ${eventName} not found in contract ${contractName}`); } } if (events.length === 0) { logger_1.Logger.warn('No valid events found to register'); return false; } // Register event handlers events.forEach(event => { this.eventHandlers.set(event.signature, callback); }); logger_1.Logger.info(`Registered ${events.length} event handlers for ${contractName}`); return true; } catch (error) { logger_1.Logger.error('Failed to register event handler:', error); return false; } } removeEventHandler(eventSignature) { return this.eventHandlers.delete(eventSignature); } clearEventHandlers() { this.eventHandlers.clear(); } async handleEvent(eventSignature, eventData) { const handler = this.eventHandlers.get(eventSignature); if (!handler) { logger_1.Logger.debug(`No handler found for event: ${eventSignature}`); return; } try { const event = await this.findEventBySignature(eventSignature); if (!event) { logger_1.Logger.warn(`Event info not found for signature: ${eventSignature}`); return; } await handler(event, eventData); } catch (error) { logger_1.Logger.error(`Error handling event ${eventSignature}:`, error); } } async findEventBySignature(signature) { for (const [_, contract] of this.facetsCache) { for (const [_, event] of contract.facetInfo.enhancedEvents) { if (event.signature === signature) { return event; } } } return null; } // Helper method to get all registered event signatures getRegisteredEventSignatures() { return Array.from(this.eventHandlers.keys()); } // Helper method to check if an event has a handler hasEventHandler(eventSignature) { return this.eventHandlers.has(eventSignature); } // Add cache maintenance method async maintainCache() { if (!this.enableCache) return; try { this.cleanEventCache(); await this.abiCache.maintain(); logger_1.Logger.info('Cache maintenance completed'); } catch (error) { logger_1.Logger.error('Cache maintenance failed:', error); } } // Add cache stats method async getCacheStats() { if (!this.enableCache) { return { enabled: false }; } try { return await this.abiCache.getCacheStats(); } catch (error) { logger_1.Logger.error('Failed to get cache stats:', error); return { error: 'Failed to get cache stats' }; } } async getEventsByAddress(address) { await this.ensureInitialized(); try { const contractInfo = this.facetsCache.get(address); if (!contractInfo) { logger_1.Logger.debug(`No contract info found for address: ${address}`); return new Map(); } return contractInfo.facetInfo.events; } catch (error) { logger_1.Logger.error(`Failed to get events for address ${address}:`, error); return new Map(); } } extractContractNameFromPath(filePath) { // Get the base name without extension const baseName = path_1.default.basename(filePath, '.ts'); // Remove common suffixes and pref return this.cleanContractName(baseName); } async getEventsByFacetName(facetName) { try { // Check cache first const cachedEvents = this.eventCache.get(facetName); if (cachedEvents) { // Ensure we're returning a Map even if the cached value isn't one return cachedEvents instanceof Map ? cachedEvents : new Map(Object.entries(cachedEvents)); } // Look through the facetsCache for the matching facet name for (const [_, contract] of this.facetsCache) { if (contract.facetInfo.name === facetName) { const events = contract.facetInfo.events; // Ensure we're returning a Map with proper EventFragment typing const eventMap = events instanceof Map ? events : new Map(Object.entries(events || {}).map(([key, value]) => [ key, value instanceof abi_1.EventFragment ? value : abi_1.EventFragment.from(value) ])); // Cache the result before returning this.eventCache.set(facetName, eventMap); this.eventCacheTimestamps.set(facetName, Date.now()); return eventMap; } } logger_1.Logger.debug(`No events found for facet: ${facetName}`); return new Map(); } catch (error) { logger_1.Logger.error(`Failed to get events for facet ${facetName}:`, error); return new Map(); } } // Add cache cleanup method cleanEventCache() { const now = Date.now(); for (const [facetName, timestamp] of this.eventCacheTimestamps.entries()) { if (now - timestamp > TypechainProcessor.EVENT_CACHE_DURATION) { this.eventCache.delete(facetName); this.eventCacheTimestamps.delete(facetName); } } } } exports.TypechainProcessor = TypechainProcessor; TypechainProcessor.VERSION = '2.0.0'; TypechainProcessor.CACHE_DURATION = 24 * 60 * 60 * 1000; // 24 hours TypechainProcessor.ABI_PATTERNS = [ /const\s+_abi\s*=\s*(\[[\s\S]*?\])\s*as\s*const/, /static\s+readonly\s+abi\s*=\s*(\[[\s\S]*?\])/, /const\s+abi\s*=\s*(\[[\s\S]*?\])/, /export\s+const\s+abi\s*=\s*(\[[\s\S]*?\])/ ]; TypechainProcessor.CONCURRENCY_LIMIT = 5; TypechainProcessor.FACTORY_PATTERN = /export\s+{\s*(\w+)\s*}\s*from\s*["'](.+?)["']/g; TypechainProcessor.EVENT_CACHE_DURATION = 5 * 60 * 1000; // 5 minutes //# sourceMappingURL=typechain-processor.js.map