UNPKG

dop-stick

Version:

Source control tooling for versionable-upgradeable smart contracts

394 lines 20 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.mine = void 0; const ethers_1 = require("ethers"); const upgradeProcessor_1 = require("./utils/upgradeProcessor"); const logger_1 = require("./utils/logsAndMetrics/core/logger"); const provider_1 = require("./utils/provider"); const deploymentTimelineAdapter_1 = require("./utils/logsAndMetrics/adapters/deploymentTimelineAdapter"); const parallelDeploymentTimelineAdapter_1 = require("./utils/logsAndMetrics/adapters/parallelDeploymentTimelineAdapter"); const upgrade_1 = require("./upgrade"); const gasUtils_1 = require("./utils/factory-management/typechain/gasUtils"); const batchProcessor_1 = require("./utils/batchProcessor"); const diamondDeployer_1 = require("./utils/diamondDeployer"); const cacheManager_1 = require("./utils/cacheManager"); const upgrade_2 = require("./upgrade"); const miningTimelineAdapter_1 = require("./utils/logsAndMetrics/adapters/miningTimelineAdapter"); async function mine(config) { var _a, _b, _c, _d, _e, _f, _g, _h, _j; const miningLogger = new miningTimelineAdapter_1.MiningTimelineAdapter(); const startTime = Date.now(); try { const processor = new upgradeProcessor_1.UpgradeProcessor(config); const cacheManager = new cacheManager_1.DeploymentCacheManager(config); const cacheConfig = { network: process.env.NETWORK || 'Testnet', rpcUrl: process.env.RPC_URL, paths: { cache: (_a = config.paths) === null || _a === void 0 ? void 0 : _a.cache } }; // Start mining process with network info const provider = await (0, provider_1.getProviderForCacheConfig)(cacheConfig); const network = await provider.getNetwork(); const gasPrice = await provider.getGasPrice(); miningLogger.startMining({ name: network.name, chainId: network.chainId, gasPrice: Number(ethers_1.ethers.utils.formatUnits(gasPrice, 'gwei')) }); // Early check if caching is disabled const isCacheEnabled = ((_b = config.cache) === null || _b === void 0 ? void 0 : _b.enabled) !== false; if (!isCacheEnabled) { miningLogger.logCacheStatus(false, 'not-found'); } // Create cache config // 1. Load upgrade.json and extract module cuts (without validation) const upgradeJson = await processor.loadUpgradeFile(); miningLogger.logUpgradeConfigStatus(true, ((_c = config.paths) === null || _c === void 0 ? void 0 : _c.upgrades) || 'upgrade.json'); const moduleCuts = await processor.extractModuleCuts(upgradeJson); // 2. Extract and process core facets const coreFacetCuts = await extractCoreFacetCuts(config); miningLogger.logCoreFacetsExtraction(coreFacetCuts.length); // 3. Combine all cuts (all as ADD actions) with consolidation const allCuts = consolidateAllCuts(coreFacetCuts, moduleCuts); miningLogger.logModuleCutsProgress(coreFacetCuts.length, moduleCuts.length); let existingCache = null; // 4. Try to load existing cache only if caching is enabled if (isCacheEnabled) { existingCache = await cacheManager.load(); if (existingCache) { miningLogger.logCacheValidation('validating'); const isValid = await cacheManager.isValid(existingCache); if (isValid) { miningLogger.logCacheValidation('valid'); miningLogger.logCacheStatus(true, 'found'); } else { miningLogger.logCacheValidation('invalid'); await cacheManager.clear(); existingCache = null; miningLogger.logCacheStatus(true, 'invalid'); } } else { miningLogger.logCacheStatus(true, 'not-found'); } } const deploymentCache = existingCache || { timestamp: Date.now(), network: network.name, chainId: network.chainId, coreFacets: {}, modules: {}, cuts: allCuts, diamondAddress: undefined }; // 5. Deploy facets using existing strategy const gasIsZero = await gasUtils_1.GasUtils.isGasPriceZero(); const undeployedCuts = filterUndeployedCuts(allCuts, deploymentCache); miningLogger.logUndeployedCuts(undeployedCuts.length); // Process modules and get results let moduleResults; if (((_e = (_d = config.parallelization) === null || _d === void 0 ? void 0 : _d.parallelDeployment) === null || _e === void 0 ? void 0 : _e.enabled) && gasIsZero) { miningLogger.logDeploymentMode('parallel'); const parallelLogger = new parallelDeploymentTimelineAdapter_1.ParallelDeploymentTimelineAdapter(); moduleResults = await (0, upgrade_1.processModuleGroupsParallel)(undeployedCuts, config, parallelLogger); } else { miningLogger.logDeploymentMode('sequential'); const sequentialLogger = new deploymentTimelineAdapter_1.DeploymentTimelineAdapter(); moduleResults = await (0, upgrade_1.processModuleGroups)(undeployedCuts, config, sequentialLogger); } // Update cache with new results only if caching is enabled for (const result of moduleResults) { // Find the matching cut to get the originalFacetName const matchingCut = undeployedCuts.find(cut => cut.moduleName === result.moduleName); const isCoreFacet = (_f = matchingCut === null || matchingCut === void 0 ? void 0 : matchingCut.originalFacetName) === null || _f === void 0 ? void 0 : _f.startsWith('core/'); if (isCoreFacet && (matchingCut === null || matchingCut === void 0 ? void 0 : matchingCut.originalFacetName)) { deploymentCache.coreFacets[matchingCut.originalFacetName] = { address: result.deployedAddress, deploymentHash: result.deploymentHash }; } else { deploymentCache.modules[result.moduleName] = { address: result.deployedAddress, deploymentHash: result.deploymentHash }; } // Save cache after each successful deployment if caching is enabled if (isCacheEnabled) { await cacheManager.save(deploymentCache); miningLogger.logCacheSave(true); } } // 6. Prepare for diamond deployment const standardType = ((_j = (_h = (_g = config.contracts) === null || _g === void 0 ? void 0 : _g.diamond) === null || _h === void 0 ? void 0 : _h.standards) === null || _j === void 0 ? void 0 : _j.type) || 'standard-type-1'; miningLogger.logDiamondDeployment(standardType); miningLogger.updateDiamondStatus('pending'); // 7. Deploy diamond logger_1.Logger.info('Initiating diamond deployment...'); const diamondDeployer = new diamondDeployer_1.DiamondDeployer(config, deploymentCache, standardType); const diamondAddress = await diamondDeployer.deploy(); // 8. Verify deployment success if (!diamondAddress) { miningLogger.updateDiamondStatus('failed'); throw new Error('Diamond deployment failed: No address returned'); } miningLogger.updateDiamondStatus('success', diamondAddress); // Check for post-deployment needs const needsPostDeploy = await needsPostDeploymentUpgrade(config, allCuts); miningLogger.logPostDeployment(needsPostDeploy); if (needsPostDeploy) { try { // Map deployed addresses to cuts const postDeploymentCuts = filterPostDeploymentCuts(allCuts, config).map(cut => { var _a, _b; const matchingResult = moduleResults.find(result => result.moduleName === cut.moduleName); // If it's a core facet, look it up in deploymentCache.coreFacets if ((_a = cut.originalFacetName) === null || _a === void 0 ? void 0 : _a.startsWith('core/')) { const coreFacet = deploymentCache.coreFacets[cut.originalFacetName]; return { ...cut, facetAddress: (coreFacet === null || coreFacet === void 0 ? void 0 : coreFacet.address) || cut.facetAddress }; } // For regular modules, use moduleResults or deploymentCache.modules const moduleAddress = (matchingResult === null || matchingResult === void 0 ? void 0 : matchingResult.deployedAddress) || ((_b = deploymentCache.modules[cut.moduleName]) === null || _b === void 0 ? void 0 : _b.address); return { ...cut, facetAddress: moduleAddress || cut.facetAddress }; }); if (postDeploymentCuts.length > 0) { // Set diamond address for upgrade context process.env.DIAMOND_ADDRESS = diamondAddress; const context = { startTime: Date.now(), moduleResults: [], successfulModules: 0, successfulSelectors: 0, failedUpgrades: [], totalGasUsed: ethers_1.ethers.BigNumber.from(0) }; await (0, upgrade_2.executeUpgradeInBatches)(postDeploymentCuts, config, context); miningLogger.updatePostDeploymentStatus('complete'); } } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); miningLogger.updatePostDeploymentStatus('failed', errorMessage); throw error; } } else { miningLogger.updatePostDeploymentStatus('skipped'); } // 10. Log deployment summary miningLogger.logDeploymentSummary({ diamondAddress, totalFacets: allCuts.length, coreFacets: Object.keys(deploymentCache.coreFacets).length, modules: Object.keys(deploymentCache.modules).length, standardType, network: network.name, chainId: network.chainId }); // 11. Return success message with diamond address miningLogger.completeMining(Date.now() - startTime); return diamondAddress; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); miningLogger.logError(`Mining process failed: ${errorMessage}`); throw error; } } exports.mine = mine; function filterUndeployedCuts(cuts, cache) { return cuts.filter(cut => { var _a; const isCoreFacet = (_a = cut.originalFacetName) === null || _a === void 0 ? void 0 : _a.startsWith('core/'); const deployedModule = isCoreFacet && cut.originalFacetName // Add type guard ? cache.coreFacets[cut.originalFacetName] : cache.modules[cut.moduleName]; return !deployedModule || !deployedModule.address; }); } async function extractCoreFacetCuts(config) { var _a, _b; const coreFacets = []; const standards = (_b = (_a = config.contracts) === null || _a === void 0 ? void 0 : _a.diamond) === null || _b === void 0 ? void 0 : _b.standards; if (!standards) { return coreFacets; } // Initialize BatchProcessor const batchProcessor = new batchProcessor_1.BatchProcessor(config); // Process default core facets const defaultFacets = [ 'cut', 'loupe', 'init', 'ownership', 'erc165', 'access', 'emergency' ]; try { for (const defaultFacetName of defaultFacets) { // Type-safe access to standards configuration const facetConfig = standards[defaultFacetName]; if (facetConfig && typeof facetConfig === 'object' && 'name' in facetConfig) { const implementationName = facetConfig.name; if (typeof implementationName === 'string') { // Use processUpgradeEntry with the implementation name const batchedFunctions = await batchProcessor.processUpgradeEntry(implementationName, config.mode || 'copilot-beta'); coreFacets.push({ facetAddress: ethers_1.ethers.constants.AddressZero, action: 0, functionSelectors: batchedFunctions.functionSignatures.map(sig => ethers_1.ethers.utils.id(sig).slice(0, 10)), functionSignatures: batchedFunctions.functionSignatures, moduleName: implementationName, originalFacetName: `core/${defaultFacetName}` // Use core/cut, core/loupe, etc. }); } } } // Process additional core facets if (standards.additionalCoreFacets) { for (const [facetName, facetConfig] of Object.entries(standards.additionalCoreFacets)) { if (facetConfig && typeof facetConfig === 'object') { const implementationName = facetConfig.name || facetName; const batchedFunctions = await batchProcessor.processUpgradeEntry(implementationName, config.mode || 'copilot-beta'); coreFacets.push({ facetAddress: ethers_1.ethers.constants.AddressZero, action: 0, functionSelectors: batchedFunctions.functionSignatures.map(sig => ethers_1.ethers.utils.id(sig).slice(0, 10)), functionSignatures: batchedFunctions.functionSignatures, moduleName: implementationName, originalFacetName: `core/${facetName}` // Use core/additionalFacetName }); } } } return coreFacets; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); logger_1.Logger.error(`Failed to extract core facet cuts: ${errorMessage}`); throw error; } } async function needsPostDeploymentUpgrade(config, deployedCuts) { var _a, _b, _c, _d; // Check diamond standard type first const standardType = (_c = (_b = (_a = config.contracts) === null || _a === void 0 ? void 0 : _a.diamond) === null || _b === void 0 ? void 0 : _b.standards) === null || _c === void 0 ? void 0 : _c.type; // For standard type 3, always need post-deployment upgrade if (standardType === 'standard-type-3') { return true; } // For custom initialization if ((_d = config.initialization) === null || _d === void 0 ? void 0 : _d.enabled) { // If initialization is set for post-deployment return !config.initialization.initializerArgs; } // For standard types 1 & 2 without custom initialization return false; } function filterPostDeploymentCuts(cuts, config) { var _a, _b, _c, _d; const standardType = (_c = (_b = (_a = config.contracts) === null || _a === void 0 ? void 0 : _a.diamond) === null || _b === void 0 ? void 0 : _b.standards) === null || _c === void 0 ? void 0 : _c.type; const filteredCuts = [...cuts]; // For standard type 3, remove DiamondCut facet if (standardType === 'standard-type-3') { return filteredCuts.filter(cut => { var _a; return !((_a = cut.originalFacetName) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes('core/cut')) && !cut.moduleName.toLowerCase().includes('diamondcut'); }); } // For custom initialization if (((_d = config.initialization) === null || _d === void 0 ? void 0 : _d.enabled) && config.initialization.initializerArgs) { // If initialization was done via constructor, return empty array return []; } return filteredCuts; } /** * Consolidates core facets and module cuts to remove any duplicates * @param coreFacetCuts Core facet cuts * @param moduleCuts Module cuts * @returns Consolidated array of cuts without duplicates */ function consolidateAllCuts(coreFacetCuts, moduleCuts) { var _a, _b; const consolidatedMap = new Map(); const duplicatesFound = new Set(); // Process core facets first (they take precedence) for (const cut of coreFacetCuts) { consolidatedMap.set(cut.moduleName, { ...cut, functionSelectors: [...cut.functionSelectors], functionSignatures: cut.functionSignatures ? [...cut.functionSignatures] : [] }); } // Process module cuts, merging with existing core facets if needed for (const cut of moduleCuts) { if (consolidatedMap.has(cut.moduleName)) { // Track duplicate for logging duplicatesFound.add(cut.moduleName); // Merge with existing cut const existing = consolidatedMap.get(cut.moduleName); const originalSelectorCount = existing.functionSelectors.length; const originalSignatureCount = ((_a = existing.functionSignatures) === null || _a === void 0 ? void 0 : _a.length) || 0; // Merge selectors (unique) cut.functionSelectors.forEach(selector => { if (!existing.functionSelectors.includes(selector)) { existing.functionSelectors.push(selector); } }); // Merge signatures if they exist (unique) if (cut.functionSignatures) { if (!existing.functionSignatures) { existing.functionSignatures = []; } cut.functionSignatures.forEach(signature => { if (!existing.functionSignatures.includes(signature)) { existing.functionSignatures.push(signature); } }); } // Log consolidation details const newSelectorCount = existing.functionSelectors.length; const newSignatureCount = ((_b = existing.functionSignatures) === null || _b === void 0 ? void 0 : _b.length) || 0; if (newSelectorCount > originalSelectorCount || newSignatureCount > originalSignatureCount) { logger_1.Logger.info(`Consolidated duplicate module "${cut.moduleName}": ` + `Added ${newSelectorCount - originalSelectorCount} new selectors, ` + `${newSignatureCount - originalSignatureCount} new signatures`); } else { logger_1.Logger.info(`Found duplicate module "${cut.moduleName}" with no new functions to add`); } // Update in map consolidatedMap.set(cut.moduleName, existing); } else { // New module cut consolidatedMap.set(cut.moduleName, { ...cut, functionSelectors: [...cut.functionSelectors], functionSignatures: cut.functionSignatures ? [...cut.functionSignatures] : [] }); } } // Summary log if any duplicates were found if (duplicatesFound.size > 0) { logger_1.Logger.info(`\nConsolidation Summary:\n` + `Found and consolidated ${duplicatesFound.size} duplicate module(s): ` + `${Array.from(duplicatesFound).join(', ')}\n`); } return Array.from(consolidatedMap.values()); } //# sourceMappingURL=mine.js.map