UNPKG

dop-stick

Version:

Source control tooling for versionable-upgradeable smart contracts

772 lines (769 loc) 35.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.BaseDiamondInfo = void 0; const ethers_1 = require("ethers"); const logger_1 = require("./utils/logsAndMetrics/core/logger"); const loupeFunctionManager_1 = require("./utils/loupeFunctionManager"); const provider_1 = require("./utils/provider"); const typechain_processor_1 = require("./utils/info-processors/typechain-processor"); const abi_1 = require("@ethersproject/abi"); const readme_generator_1 = require("./utils/documentation/readme-generator"); const parallelTypes_1 = require("./types/parallelTypes"); const infoTimelineAdapter_1 = require("./utils/logsAndMetrics/adapters/infoTimelineAdapter"); class BaseDiamondInfo { constructor(config, dopStickConfig, isCliCall = false) { var _a, _b, _c; this.isCliCall = isCliCall; try { const { provider, diamondAddress } = (0, provider_1.createProviderAndGetAddress)(config); this.provider = provider; this.diamondAddress = diamondAddress; this.functions = new loupeFunctionManager_1.LoupeFunctionManager(dopStickConfig); this.dopStickConfig = dopStickConfig; this.networkName = config.network || process.env.NETWORK || 'unknown'; this.facets = new Map(); const iface = this.functions.getInterface(); logger_1.Logger.debug('Contract interface functions:', iface.functions); this.loupe = new ethers_1.ethers.Contract(this.diamondAddress, iface, this.provider); // Initialize typechain processor using getInstance with cache setting const typechainPath = ((_a = dopStickConfig === null || dopStickConfig === void 0 ? void 0 : dopStickConfig.paths) === null || _a === void 0 ? void 0 : _a.typechain) || 'typechain-types'; const enableCache = (_c = (_b = dopStickConfig === null || dopStickConfig === void 0 ? void 0 : dopStickConfig.cache) === null || _b === void 0 ? void 0 : _b.enabled) !== null && _c !== void 0 ? _c : true; // Default to true if not specified this.typechainProcessor = typechain_processor_1.TypechainProcessor.getInstance(typechainPath, enableCache); // Initialize immediately this.initializeProcessor().catch(error => { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; logger_1.Logger.warning('Failed to initialize typechain processor:', errorMessage); }); this.infoLogger = new infoTimelineAdapter_1.InfoTimelineAdapter(); } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; logger_1.Logger.error('Failed to initialize DiamondInfo:', errorMessage); throw error; } } async initializeProcessor() { try { await this.typechainProcessor.initialize(); logger_1.Logger.debug('TypechainProcessor initialized successfully'); } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; logger_1.Logger.error('Failed to initialize TypechainProcessor:', errorMessage); throw error; } } /** * Get basic diamond information */ async getBasicInfo() { try { const [facets, owner] = await Promise.all([ this.functions.callFunction(this.loupe, 'facets'), this.functions.callFunction(this.loupe, 'owner') ]); let version; try { version = await this.functions.callFunction(this.loupe, 'version'); } catch (_a) { // Version function might not exist } return { owner, totalFacets: facets.length, totalFunctions: facets.reduce((acc, facet) => acc + (facet.functionSelectors || facet.selectors || []).length, 0), implementationVersion: version }; } catch (error) { if (error instanceof Error) { throw new Error(`Failed to get basic diamond info: ${error.message}`); } throw new Error('Failed to get basic diamond info: Unknown error'); } } /** * Get all facets with their selectors */ async getFacets() { try { const facets = await this.functions.callFunction(this.loupe, 'facets'); return facets.map(facet => ({ address: facet.facetAddress, functionSelectors: facet.functionSelectors || facet.selectors || [] })); } catch (error) { if (error instanceof Error) { throw new Error(`Failed to get facets: ${error.message}`); } throw new Error('Failed to get facets: Unknown error'); } } /** * Get detailed information about a specific facet */ async getFacetInfo(facetAddress) { try { const selectors = await this.functions.callFunction(this.loupe, 'facetSelectors', facetAddress); // Try to get contract code to determine deployment block const code = await this.provider.getCode(facetAddress); let deploymentBlock; if (code !== '0x') { // Search for deployment block (binary search could be implemented for efficiency) const currentBlock = await this.provider.getBlockNumber(); for (let i = currentBlock; i >= 0; i -= 1000) { const historicalCode = await this.provider.getCode(facetAddress, i); if (historicalCode === '0x') { deploymentBlock = i + 1; break; } } } return { address: facetAddress, functionSelectors: selectors, functionCount: selectors.length, deploymentBlock }; } catch (error) { if (error instanceof Error) { throw new Error(`Failed to get facet info for ${facetAddress}: ${error.message}`); } throw new Error(`Failed to get facet info for ${facetAddress}: Unknown error`); } } /** * Get all function selectors */ async getAllFunctionSelectors() { try { const facets = await this.functions.callFunction(this.loupe, 'facets'); return facets.reduce((acc, facet) => [...acc, ...(facet.functionSelectors || facet.selectors || [])], []); } catch (error) { logger_1.Logger.error('Failed to get all function selectors:', error); throw error; } } /** * Get complete diamond structure */ async getDiamondStructure() { try { const facets = await this.functions.callFunction(this.loupe, 'facets'); const detailedFacets = []; for (const facet of facets) { const detailedInfo = await this.getFacetInfo(facet.facetAddress); detailedFacets.push(detailedInfo); } const uniqueAddresses = [...new Set(detailedFacets.map(f => f.address))]; const totalSelectors = detailedFacets.reduce((acc, facet) => acc + facet.functionSelectors.length, 0); return { facets: detailedFacets, totalSelectors, uniqueAddresses }; } catch (error) { if (error instanceof Error) { throw new Error(`Failed to get diamond structure: ${error.message}`); } throw new Error('Failed to get diamond structure: Unknown error'); } } /** * Get all non-event based information in one call */ async getAllStaticInfo() { try { const [basic, structure] = await Promise.all([ this.getBasicInfo(), this.getDiamondStructure() ]); return { basic, structure }; } catch (error) { if (error instanceof Error) { throw new Error(`Failed to get all static info: ${error.message}`); } throw new Error('Failed to get all static info: Unknown error'); } } calculateOptimalBatchSize(totalItems) { const config = this.getInfoProcessingConfig(); if (!config.adaptiveBatching) { return config.selectorBatchSize; } let optimalSize = Math.ceil(totalItems / config.maxConcurrentBatches); optimalSize = Math.min(config.maxBatchSize, Math.max(config.minBatchSize, optimalSize)); logger_1.Logger.debug(`Calculated optimal batch size: ${optimalSize} for ${totalItems} items`); return optimalSize; } async getDetailedFacetsInfo() { var _a, _b; try { await this.typechainProcessor.initialize(); const facetsConfig = this.functions.getFunctionConfig('facets'); let facets = await this.functions.callFunction(this.loupe, 'facets'); const detailedFacets = new Map(); // Process blockchain data const rawBlockchainData = { facets, totalFacets: facets.length, totalSelectors: facets.reduce((total, facet) => total + (facet.functionSelectors || facet.selectors || []).length, 0), selectorsPerFacet: facets.map((facet) => ({ facetAddress: facet.facetAddress || facet.moduleAddress, selectorCount: (facet.functionSelectors || facet.selectors || []).length })) }; // Get parallelization config const parallelConfig = (_a = this.dopStickConfig) === null || _a === void 0 ? void 0 : _a.parallelization; const useParallel = (_b = parallelConfig === null || parallelConfig === void 0 ? void 0 : parallelConfig.enabled) !== null && _b !== void 0 ? _b : false; if (useParallel) { // Calculate total selectors for optimal batch sizing const totalSelectors = facets.reduce((total, facet) => total + ((facet.functionSelectors || facet.selectors || []).length), 0); const config = this.getInfoProcessingConfig(); const batchSize = this.calculateOptimalBatchSize(totalSelectors); const maxConcurrent = config.maxConcurrentBatches; logger_1.Logger.info(`Processing ${totalSelectors} selectors with batch size ${batchSize} and max ${maxConcurrent} concurrent batches`); // Process facets with controlled concurrency const processFacetBatch = async (facets) => { await Promise.all(facets.map(async (facet) => { const facetAddress = facet.facetAddress || facet.moduleAddress; if (!facetAddress || !ethers_1.ethers.utils.isAddress(facetAddress)) { logger_1.Logger.warning(`Invalid facet address: ${facetAddress}`); return; } const selectors = facet.functionSelectors || facet.selectors; if (!Array.isArray(selectors)) { logger_1.Logger.warning(`Invalid selectors for facet ${facetAddress}`); return; } const normalizedSelectors = selectors.map(s => s.toLowerCase().startsWith('0x') ? s.toLowerCase() : `0x${s.toLowerCase()}`); // Process selectors in smaller batches const selectorBatches = this.chunk(normalizedSelectors, batchSize); for (const batch of selectorBatches) { await Promise.all(batch.map(async (selector) => { const functionInfo = await this.typechainProcessor.getFunctionsBySelector(selector); this.updateDetailedFacets(detailedFacets, facetAddress, selector, functionInfo); })); } })); }; // Process facets in controlled batches const facetBatches = this.chunk(facets, maxConcurrent); for (const batch of facetBatches) { await processFacetBatch(batch); } } else { // Sequential processing (existing logic) for (const facet of facets) { const facetAddress = facet.facetAddress || facet.moduleAddress; if (!facetAddress || !ethers_1.ethers.utils.isAddress(facetAddress)) { logger_1.Logger.warning(`Invalid facet address: ${facetAddress}`); continue; } const selectors = facet.functionSelectors || facet.selectors; if (!Array.isArray(selectors)) { logger_1.Logger.warning(`Invalid selectors for facet ${facetAddress}`); continue; } const normalizedSelectors = selectors.map(s => s.toLowerCase().startsWith('0x') ? s.toLowerCase() : `0x${s.toLowerCase()}`); for (const selector of normalizedSelectors) { const functionInfo = await this.typechainProcessor.getFunctionsBySelector(selector); this.updateDetailedFacets(detailedFacets, facetAddress, selector, functionInfo); } } } return { processedFacets: Array.from(detailedFacets.values()) .sort((a, b) => a.name.localeCompare(b.name)), rawBlockchainData }; } catch (error) { logger_1.Logger.error('Failed to get detailed facets info:', error); throw error; } } // Helper function to chunk arrays chunk(array, size) { return Array.from({ length: Math.ceil(array.length / size) }, (_, i) => array.slice(i * size, i * size + size)); } // Helper function to update detailed facets updateDetailedFacets(detailedFacets, facetAddress, selector, functionInfo) { if (!functionInfo) { if (!detailedFacets.has(facetAddress)) { detailedFacets.set(facetAddress, { name: `Unknown Facet (${facetAddress})`, address: facetAddress, selectors: [], functions: [] }); } const facetData = detailedFacets.get(facetAddress); facetData.selectors.push(selector); facetData.functions.push({ name: `Unknown Function (${selector})`, selector: selector, signature: selector, params: [], returns: [], stateMutability: 'nonpayable', facetAddress: facetAddress }); return; } if (!detailedFacets.has(facetAddress)) { detailedFacets.set(facetAddress, { name: functionInfo.facetName, address: facetAddress, selectors: [], functions: [] }); } const facetData = detailedFacets.get(facetAddress); if (facetData.name.startsWith('Unknown Facet')) { facetData.name = functionInfo.facetName; } facetData.selectors.push(selector); facetData.functions.push({ name: functionInfo.functionInfo.name, selector: selector, signature: functionInfo.functionInfo.signature, params: functionInfo.functionInfo.inputs, returns: functionInfo.functionInfo.outputs, stateMutability: functionInfo.functionInfo.mutability, facetAddress: facetAddress }); } convertToParams(inputs) { return (inputs !== null && inputs !== void 0 ? inputs : []).map(input => ({ name: input.name || '', type: input.type, indexed: input.indexed || false })); } convertToReturns(outputs) { return (outputs !== null && outputs !== void 0 ? outputs : []).map(output => ({ type: output.type, name: output.name || undefined })); } // Helper method to find deployment block with timeout async findDeploymentBlock(address) { const currentBlock = await this.provider.getBlockNumber(); let left = 0; let right = currentBlock; const timeout = setTimeout(() => { throw new Error('Deployment block search timed out'); }, 10000); // 10 second timeout try { while (left <= right) { const mid = Math.floor((left + right) / 2); const historicalCode = await this.provider.getCode(address, mid); if (historicalCode === '0x') { left = mid + 1; } else { right = mid - 1; } } return left; } finally { clearTimeout(timeout); } } // Helper method to get function fragment async getFunctionFragment(address, selector) { try { // Try to get the function signature from the contract const contract = new ethers_1.ethers.Contract(address, [`function ${selector}`], this.provider); return contract.interface.getFunction(selector); } catch (error) { logger_1.Logger.debug(`Failed to get function fragment for selector ${selector}:`, error); return null; } } /** * Get a human-readable report of the diamond structure */ async getFacetsReport() { try { const { processedFacets } = await this.getDetailedFacetsInfo(); const uniqueAddresses = new Set(); let totalSelectors = 0; let unknownSelectors = 0; const facets = processedFacets.map((facet) => { uniqueAddresses.add(facet.address); // Count total and unknown selectors totalSelectors += facet.functions.length; unknownSelectors += facet.functions.filter((f) => f.name.startsWith('Unknown Function')).length; // Transform functions to get signatures const functionSignatures = facet.functions .filter((f) => !f.name.startsWith('Unknown Function')) .map((f) => f.signature); const detailedFacet = { address: facet.address, functionSelectors: facet.selectors, functionSignatures: functionSignatures, functionCount: functionSignatures.length, facetName: facet.name }; return detailedFacet; }); const report = { facets: facets.sort((a, b) => (a.facetName || '').localeCompare(b.facetName || '')), totalSelectors, uniqueAddresses: Array.from(uniqueAddresses) }; logger_1.Logger.debug(`Generated facets report: - Total Facets: ${facets.length} - Unique Addresses: ${uniqueAddresses.size} - Total Selectors: ${totalSelectors} - Unknown Selectors: ${unknownSelectors} `); return report; } catch (error) { const message = error instanceof Error ? error.message : 'Unknown error'; logger_1.Logger.error('Failed to generate facets report:', error); throw new Error(`Failed to generate facets report: ${message}`); } } async getFacetName(facetAddress) { // Default name using address const defaultName = `Facet (${facetAddress.slice(0, 6)}...${facetAddress.slice(-4)})`; try { const contract = new ethers_1.ethers.Contract(facetAddress, [ 'function name() view returns (string)', 'function getName() external view returns (string)', 'function facetName() external view returns (string)', 'function moduleName() external view returns (string)', 'function NAME() view returns (string)', ], this.provider); const namingMethods = ['name', 'getName', 'facetName', 'moduleName', 'NAME']; for (const method of namingMethods) { try { const name = await contract[method](); if (name && typeof name === 'string' && name.trim()) { return name; } } catch (e) { continue; } } return defaultName; } catch (error) { return defaultName; } } // Add new method to get events by facet name async getEventsByFacetName(facetName) { try { const events = await this.typechainProcessor.getEventsByFacetName(facetName); return events; } catch (error) { logger_1.Logger.warn(`Failed to get events for facet ${facetName}:`, error); return new Map(); } } static decodeTimeHash(hash) { try { const timeStr = Buffer.from(hash, 'base64').toString(); const timestamp = parseInt(timeStr, 10); return new Date(timestamp); } catch (error) { throw new Error(`Invalid time hash: ${hash}`); } } async generateDocumentation() { try { this.infoLogger.startInfoProcess(this.diamondAddress, this.networkName); // Get both processed and raw blockchain data const { processedFacets, rawBlockchainData } = await this.getDetailedFacetsInfo(); // Generate the complete analysis const analysisData = await this.generateAnalysisData(processedFacets, rawBlockchainData); this.infoLogger.logFacetsAnalysis(analysisData.statistics.totalFacets, analysisData.statistics.totalSelectors, analysisData.statistics.totalUnknownSelectors); // Process facets for contract info (needed for existing functionality) const facetsMap = new Map(); const unknownSelectors = []; // Process each facet for (const facet of processedFacets) { this.infoLogger.logFacetProcessing(facet.name, facet.address); const functionsMap = new Map(); // Process functions for (const func of facet.functions) { if (func.name.startsWith('Unknown Function')) { unknownSelectors.push({ facet: func.facetAddress, selector: func.selector, reason: 'Function signature could not be determined' }); continue; } functionsMap.set(func.selector, { name: func.name, signature: func.signature, fragment: abi_1.FunctionFragment.from({ name: func.name, type: 'function', inputs: func.params.map(param => ({ name: param.name, type: param.type, indexed: param.indexed })), outputs: func.returns.map(ret => ({ name: ret.name || '', type: ret.type })), stateMutability: func.stateMutability }) }); } // Get events using facet name instead of address const events = await this.getEventsByFacetName(facet.name); this.infoLogger.logFacetEvents(facet.name, events.size); if (functionsMap.size > 0 || events.size > 0) { facetsMap.set(facet.address, { name: facet.name, abi: [], selectors: new Set(Array.from(functionsMap.keys())), functions: functionsMap, events }); } } // Create the readme generator const generator = new readme_generator_1.ReadmeGenerator(this.networkName, this.diamondAddress, this.dopStickConfig); // Generate documentation with both the traditional data and the new analysis const { markdownPath, jsonPath, hash } = await generator.generateDocumentation(facetsMap, unknownSelectors, analysisData); this.infoLogger.logDocumentationGeneration(markdownPath, jsonPath, hash, BaseDiamondInfo.decodeTimeHash(hash).toISOString()); this.infoLogger.logAnalysisSummary({ totalFacets: analysisData.statistics.totalFacets, totalSelectors: analysisData.statistics.totalSelectors, unknownSelectors: analysisData.statistics.totalUnknownSelectors, discrepancies: analysisData.comparisonAnalysis.unmatchedSelectors }); this.infoLogger.completeInfoProcess(); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); this.infoLogger.logError('Failed to generate documentation', errorMessage); throw error; } } logDocumentationResults(unknownSelectors, markdownPath, jsonPath) { // Group unknown selectors by reason const groupedSelectors = unknownSelectors.reduce((acc, curr) => { const reason = curr.reason || 'Unknown reason'; if (!acc[reason]) acc[reason] = []; acc[reason].push(curr); return acc; }, {}); logger_1.Logger.info(` Diamond analysis documentation generated successfully! Files generated: - Markdown: ${markdownPath} - JSON: ${jsonPath} You can find: - Latest analysis: ./dop-stick-info/latest/ - All analyses: ./dop-stick-info/ ${unknownSelectors.length > 0 ? ` Unknown Selectors Analysis: Total unknown selectors: ${unknownSelectors.length} Breakdown by reason: ${Object.entries(groupedSelectors).map(([reason, selectors]) => ` ${reason}: ${selectors.map(s => `- ${s.selector} (in facet ${s.facet})`).join('\n')}`).join('\n')} ` : ''} `); } getInfoProcessingConfig() { var _a, _b, _c, _d, _e, _f, _g, _h, _j; const defaultConfig = parallelTypes_1.DEFAULT_INFO_PROCESSING_CONFIG; const userConfig = ((_b = (_a = this.dopStickConfig) === null || _a === void 0 ? void 0 : _a.parallelization) === null || _b === void 0 ? void 0 : _b.infoProcessing) || {}; return { minBatchSize: (_c = userConfig.minBatchSize) !== null && _c !== void 0 ? _c : defaultConfig.minBatchSize, maxBatchSize: (_d = userConfig.maxBatchSize) !== null && _d !== void 0 ? _d : defaultConfig.maxBatchSize, maxConcurrentBatches: (_e = userConfig.maxConcurrentBatches) !== null && _e !== void 0 ? _e : defaultConfig.maxConcurrentBatches, adaptiveBatching: (_f = userConfig.adaptiveBatching) !== null && _f !== void 0 ? _f : defaultConfig.adaptiveBatching, selectorBatchSize: (_g = userConfig.selectorBatchSize) !== null && _g !== void 0 ? _g : defaultConfig.selectorBatchSize, processingTimeout: (_h = userConfig.processingTimeout) !== null && _h !== void 0 ? _h : defaultConfig.processingTimeout, retryDelay: (_j = userConfig.retryDelay) !== null && _j !== void 0 ? _j : defaultConfig.retryDelay }; } async generateAnalysisData(processedFacets, rawBlockchainData) { const blockchainSelectors = new Set(); const processedSelectors = new Set(); const facetAnalysis = []; let totalUnknownSelectors = 0; // Process blockchain data rawBlockchainData.facets.forEach(facet => { const selectors = facet.functionSelectors || facet.selectors || []; selectors.forEach(selector => blockchainSelectors.add(selector.toLowerCase())); }); // Process each facet for (const facet of processedFacets) { const foundSelectors = []; const unknownSelectors = []; facet.functions.forEach(func => { const selector = func.selector.toLowerCase(); processedSelectors.add(selector); if (func.name.startsWith('Unknown Function')) { unknownSelectors.push({ selector, reason: 'Function signature could not be determined' }); } else { foundSelectors.push({ selector, name: func.name, signature: func.signature, mutability: func.stateMutability }); } }); totalUnknownSelectors += unknownSelectors.length; // Get events for this facet const events = await this.getEventsByFacetName(facet.name); const processedEvents = Array.from(events.values()).map((event) => ({ name: event.name, signature: event instanceof abi_1.EventFragment ? event.format() : `${event.name}(${(event.inputs || []) .map((input) => `${input.type}${input.indexed ? ' indexed' : ''}`) .join(',')})` })); // Add facet analysis with events facetAnalysis.push({ name: facet.name, address: facet.address, statistics: { totalSelectors: facet.functions.length, foundSelectors: foundSelectors.length, unknownSelectors: unknownSelectors.length }, selectors: { found: foundSelectors, unknown: unknownSelectors }, events: processedEvents }); logger_1.Logger.debug(`Processed ${processedEvents.length} events for facet ${facet.name}`); } // Generate comparison analysis const matchedSelectors = new Set([...blockchainSelectors].filter(x => processedSelectors.has(x))); const discrepancies = []; // Find missing selectors (in blockchain but not processed) for (const selector of blockchainSelectors) { if (!processedSelectors.has(selector)) { discrepancies.push({ type: 'missing', selector, facetAddress: this.getFacetAddressForSelector(rawBlockchainData.facets, selector), details: 'Selector found in blockchain but not in processed data' }); } } // Find extra selectors (in processed but not in blockchain) for (const selector of processedSelectors) { if (!blockchainSelectors.has(selector)) { discrepancies.push({ type: 'extra', selector, facetAddress: this.getFacetAddressForProcessedSelector(processedFacets, selector), details: 'Selector found in processed data but not in blockchain' }); } } return { metadata: { version: await this.getVersion(), timestamp: new Date().toISOString(), network: this.networkName, diamondAddress: this.diamondAddress }, statistics: { totalFacets: processedFacets.length, totalSelectors: processedSelectors.size, totalUnknownSelectors, uniqueAddresses: new Set(processedFacets.map(f => f.address)).size }, blockchainData: { totalFacets: rawBlockchainData.totalFacets, totalSelectors: rawBlockchainData.totalSelectors, facetsBreakdown: rawBlockchainData.facets.map(facet => { const address = facet.facetAddress || facet.moduleAddress; return { address: address || 'unknown', selectors: facet.functionSelectors || facet.selectors || [], selectorCount: (facet.functionSelectors || facet.selectors || []).length }; }) }, facets: facetAnalysis, unknownSelectors: { total: totalUnknownSelectors, breakdown: this.generateUnknownSelectorsBreakdown(facetAnalysis) }, comparisonAnalysis: { matchedSelectors: matchedSelectors.size, unmatchedSelectors: discrepancies.length, discrepancies } }; } generateUnknownSelectorsBreakdown(facets) { return facets .filter(facet => facet.selectors.unknown.length > 0) .map(facet => ({ facetAddress: facet.address, facetName: facet.name, selectors: facet.selectors.unknown })); } getFacetAddressForSelector(facets, selector) { for (const facet of facets) { const selectors = facet.functionSelectors || facet.selectors || []; if (selectors.includes(selector)) { const address = facet.facetAddress || facet.moduleAddress; return address || 'unknown'; } } return 'unknown'; } getFacetAddressForProcessedSelector(facets, selector) { for (const facet of facets) { if (facet.functions.some(f => f.selector.toLowerCase() === selector.toLowerCase())) { return facet.address; } } return 'unknown'; } async getVersion() { try { return await this.functions.callFunction(this.loupe, 'version') || 'unknown'; } catch (_a) { return 'unknown'; } } } exports.BaseDiamondInfo = BaseDiamondInfo; //# sourceMappingURL=info.js.map