dop-stick
Version:
Source control tooling for versionable-upgradeable smart contracts
264 lines • 12.5 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ReviewAdapter = void 0;
const readline_1 = __importDefault(require("readline"));
const logger_1 = require("../core/logger");
const ethers = __importStar(require("ethers"));
const box_1 = require("../core/box");
var DiamondCutAction;
(function (DiamondCutAction) {
DiamondCutAction[DiamondCutAction["ADD"] = 0] = "ADD";
DiamondCutAction[DiamondCutAction["REPLACE"] = 1] = "REPLACE";
DiamondCutAction[DiamondCutAction["REMOVE"] = 2] = "REMOVE";
})(DiamondCutAction || (DiamondCutAction = {}));
class Terminal {
static write(message) {
process.stdout.write(message + '\n');
}
static async prompt(question) {
const rl = readline_1.default.createInterface({
input: process.stdin,
output: process.stdout
});
try {
return await new Promise(resolve => {
rl.question(question, resolve);
});
}
finally {
rl.close();
}
}
}
class ReviewAdapter {
constructor(mode) {
this.mode = mode;
}
async processValidationResults(validatedModules) {
const summary = this.prepareSummary(validatedModules);
this.displaySummary(summary);
if (this.mode === 'auto-pilot-beta') {
logger_1.Logger.info('Auto-pilot mode: automatically accepting changes');
return true;
}
return await this.handleUserCommands(summary);
}
prepareSummary(modules) {
const summary = {
totalModules: modules.length,
criticalIssues: 0,
warnings: 0,
suggestions: 0,
invalidActions: 0,
moduleResults: []
};
modules.forEach(module => {
// Create module result
const moduleResult = {
moduleName: module.moduleName,
missingFunctions: module.pendingMissingFunctions || [],
collisions: Array.from(module.collisions.entries())
.filter(([_, hasCollision]) => hasCollision)
.map(([selector]) => {
// Find the signature for this selector
const signature = Array.from(module.originalSignatures.keys())
.find(sig => ethers.utils.id(sig).slice(0, 10) === selector);
return signature || selector;
}),
suggestedChanges: Array.from(module.suggestedChanges.entries())
.filter(([signature, suggestedAction]) => {
// Only include changes where the suggested action is different
const originalAction = module.originalSignatures.get(signature);
return originalAction !== suggestedAction;
})
.map(([signature, suggestedAction]) => ({
signature,
from: module.originalSignatures.get(signature),
to: suggestedAction
})),
invalidRemovals: module.pendingRemovals.map(r => r.signature),
originalSignatures: module.originalSignatures
};
summary.moduleResults.push(moduleResult);
// Update summary counts
summary.criticalIssues += moduleResult.missingFunctions.length;
summary.warnings += moduleResult.collisions.length;
summary.suggestions += moduleResult.suggestedChanges.length; // Now only counts actual changes
summary.invalidActions += moduleResult.invalidRemovals.length;
});
return summary;
}
displaySummary(summary) {
Terminal.write('\n✨ VALIDATION REVIEW');
Terminal.write('────────────────');
summary.moduleResults.forEach((module, index) => {
Terminal.write(`\n[${index + 1}/${summary.totalModules}] ${module.moduleName}`);
// Display critical issues (missing functions) if any
if (module.missingFunctions.length > 0) {
Terminal.write(`🔴 ${module.missingFunctions.length} missing function${module.missingFunctions.length === 1 ? '' : 's'}`);
}
// Display invalid removals if any
if (module.invalidRemovals.length > 0) {
Terminal.write(`🟠 ${module.invalidRemovals.length} invalid removal${module.invalidRemovals.length === 1 ? '' : 's'}`);
}
// Display collisions if any
if (module.collisions.length > 0) {
Terminal.write(`🟡 ${module.collisions.length} selector collision${module.collisions.length === 1 ? '' : 's'}`);
}
// Display suggested changes if any - now in purple
if (module.suggestedChanges.length > 0) {
Terminal.write(`\x1b[35m↪ ${module.suggestedChanges.length} action change${module.suggestedChanges.length === 1 ? '' : 's'} suggested\x1b[0m`);
}
});
// Display summary totals
Terminal.write('\nSUMMARY');
Terminal.write('───────');
const totalIssues = summary.criticalIssues + summary.warnings + summary.suggestions + summary.invalidActions;
Terminal.write(`${totalIssues} issue${totalIssues === 1 ? '' : 's'} found across ${summary.totalModules} module${summary.totalModules === 1 ? '' : 's'}`);
if (summary.criticalIssues > 0) {
Terminal.write(`• ${summary.criticalIssues} critical issue${summary.criticalIssues === 1 ? '' : 's'} 🔴`);
}
if (summary.warnings > 0) {
Terminal.write(`• ${summary.warnings} selector collision${summary.warnings === 1 ? '' : 's'} 🟡`);
}
if (summary.suggestions > 0) {
Terminal.write(`\x1b[35m• ${summary.suggestions} action change${summary.suggestions === 1 ? '' : 's'} ↪\x1b[0m`);
}
if (summary.invalidActions > 0) {
Terminal.write(`• ${summary.invalidActions} invalid removal${summary.invalidActions === 1 ? '' : 's'} 🟠`);
}
}
async handleUserCommands(summary) {
if (this.mode === 'strict' && (summary.criticalIssues > 0 || summary.invalidActions > 0)) {
logger_1.Logger.error('Strict mode: Cannot proceed with critical issues or invalid actions');
return false;
}
while (true) {
Terminal.write('\n\x1b[2mUse \'details <module>\' for more information');
Terminal.write('\'apply\' to accept all changes, \'exit\' to cancel\x1b[0m');
const command = await Terminal.prompt('\nEnter command (apply/details/exit): ');
Terminal.write('\n');
if (command === 'apply') {
const confirm = await Terminal.prompt('Are you sure you want to apply all changes? (y/N): ');
if (confirm.toLowerCase() === 'y') {
return true;
}
}
else if (command === 'exit') {
return false;
}
else if (command.startsWith('details ')) {
const moduleName = command.substring(8);
const found = this.displayModuleDetails(summary, moduleName);
if (!found) {
Terminal.write(`\nModule '${moduleName}' not found. Available modules:`);
summary.moduleResults.forEach(m => Terminal.write(` • ${m.moduleName}`));
}
}
else {
Terminal.write('\nAvailable commands:');
Terminal.write(' • details <module> - Show detailed module review');
Terminal.write(' • apply - Apply all suggested changes');
Terminal.write(' • exit - Cancel upgrade process');
}
}
}
displayModuleDetails(summary, moduleName) {
const moduleResult = summary.moduleResults.find(m => m.moduleName.toLowerCase() === moduleName.toLowerCase() ||
m.moduleName === moduleName);
if (!moduleResult) {
return false;
}
new box_1.Box(`${moduleResult.moduleName} Details`, {
style: 'double',
color: 'muted',
padding: 1,
margin: 0,
align: 'left'
}).render();
// Show module overview
const totalFunctions = moduleResult.originalSignatures.size;
Terminal.write(`\n\x1b[2m• ${totalFunctions} total function${totalFunctions === 1 ? '' : 's'}\x1b[0m`);
// Show critical issues (missing functions)
if (moduleResult.missingFunctions.length > 0) {
Terminal.write('\n\x1b[31m🔴 MISSING FUNCTIONS\x1b[0m');
moduleResult.missingFunctions.forEach(fn => {
Terminal.write(`• ${fn}`);
Terminal.write(` \x1b[31m→ Function not found in contract\x1b[0m`);
});
}
// Show invalid removals
if (moduleResult.invalidRemovals.length > 0) {
Terminal.write('\n\x1b[33m🟠 INVALID REMOVALS\x1b[0m');
moduleResult.invalidRemovals.forEach(fn => {
Terminal.write(`• ${fn}`);
Terminal.write(` \x1b[33m→ Function does not exist onchain\x1b[0m`);
});
}
// Show collisions
if (moduleResult.collisions.length > 0) {
Terminal.write('\n\x1b[33m🟡 SELECTOR COLLISIONS\x1b[0m');
moduleResult.collisions.forEach(fn => {
Terminal.write(`• ${fn}`);
Terminal.write(` \x1b[33m→ Selector collision detected\x1b[0m`);
});
}
// Show suggested changes - now in purple
if (moduleResult.suggestedChanges.length > 0) {
Terminal.write('\n\x1b[35m↪ SUGGESTED ACTION CHANGES\x1b[0m');
moduleResult.suggestedChanges.forEach(change => {
Terminal.write(`• ${change.signature}`);
Terminal.write(` \x1b[35m→ Change ${this.getActionName(change.from)} to ${this.getActionName(change.to)}\x1b[0m`);
});
}
// Show unchanged functions
const unchangedFunctions = Array.from(moduleResult.originalSignatures.entries())
.filter(([sig]) => !moduleResult.suggestedChanges.find(change => change.signature === sig))
.filter(([sig]) => !moduleResult.missingFunctions.includes(sig))
.filter(([sig]) => !moduleResult.invalidRemovals.includes(sig));
if (unchangedFunctions.length > 0) {
Terminal.write('\n\x1b[32m✓ UNCHANGED FUNCTIONS\x1b[0m');
unchangedFunctions.forEach(([signature, action]) => {
Terminal.write(`• ${signature}`);
Terminal.write(` \x1b[32m→ ${this.getActionName(action)}\x1b[0m`);
});
}
return true;
}
getActionName(action) {
switch (action) {
case 0: return 'ADD';
case 1: return 'REPLACE';
case 2: return 'REMOVE';
default: return `UNKNOWN(${action})`;
}
}
}
exports.ReviewAdapter = ReviewAdapter;
//# sourceMappingURL=reviewAdapter.js.map