dop-stick
Version:
Source control tooling for versionable-upgradeable smart contracts
772 lines (769 loc) • 35.3 kB
JavaScript
;
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