dop-stick
Version:
Source control tooling for versionable-upgradeable smart contracts
394 lines • 20 kB
JavaScript
;
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