UNPKG

dop-stick

Version:

Source control tooling for versionable-upgradeable smart contracts

273 lines 10.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.diamondStandards = exports.DiamondStandards = void 0; const ethers_1 = require("ethers"); const configLoader_1 = require("./configLoader"); const hardhatHelpers_1 = require("./hardhatHelpers"); const logger_1 = require("./logsAndMetrics/core/logger"); class DiamondStandards { constructor() { this.standards = {}; } static getInstance() { if (!DiamondStandards.instance) { DiamondStandards.instance = new DiamondStandards(); } return DiamondStandards.instance; } async initialize() { var _a, _b; const config = await configLoader_1.configLoader.loadConfig(); this.standards = { ...DiamondStandards.DEFAULT_STANDARDS, ...(_b = (_a = config.contracts) === null || _a === void 0 ? void 0 : _a.diamond) === null || _b === void 0 ? void 0 : _b.standards }; } getStandard(key) { return this.standards[key] || DiamondStandards.DEFAULT_STANDARDS[key]; } validateVersioningConfig(config) { if (!(config === null || config === void 0 ? void 0 : config.enabled)) return; if (config.strategy && !['semver', 'incremental', 'custom'].includes(config.strategy)) { throw new Error(`Invalid versioning strategy: ${config.strategy}`); } if (config.strategy === 'custom' && !config.format) { throw new Error('Custom versioning strategy requires a format string'); } } validateCutConfig(config) { var _a; if (!config) return; if (config.batchSize && (config.batchSize < 1 || config.batchSize > 100)) { throw new Error('Batch size must be between 1 and 100'); } if ((_a = config.retryStrategy) === null || _a === void 0 ? void 0 : _a.enabled) { if (config.retryStrategy.maxAttempts && config.retryStrategy.maxAttempts < 1) { throw new Error('Max retry attempts must be greater than 0'); } if (config.retryStrategy.backoffMs && config.retryStrategy.backoffMs < 100) { throw new Error('Retry backoff must be at least 100ms'); } } } validateOwnershipConfig(config) { var _a, _b; if (!config) return; if ((_a = config.multiSig) === null || _a === void 0 ? void 0 : _a.enabled) { if (!config.multiSig.threshold || config.multiSig.threshold < 1) { throw new Error('MultiSig threshold must be greater than 0'); } if (!((_b = config.multiSig.owners) === null || _b === void 0 ? void 0 : _b.length) || config.multiSig.owners.length < config.multiSig.threshold) { throw new Error('MultiSig owners list must be >= threshold'); } } } async validateDiamondConfig(config) { var _a, _b, _c, _d; this.validateVersioningConfig(config.versioning); this.validateCutConfig(config.cut); this.validateOwnershipConfig(config.ownership); // Validate interfaces if ERC165 is enabled if ((_b = (_a = config.erc165) === null || _a === void 0 ? void 0 : _a.validation) === null || _b === void 0 ? void 0 : _b.requireSupport) { await this.validateRequiredInterfaces(config.erc165.validation.requireSupport); } // Validate emergency config if (((_c = config.emergency) === null || _c === void 0 ? void 0 : _c.enabled) && ((_d = config.emergency.pausable) === null || _d === void 0 ? void 0 : _d.timeout)) { if (config.emergency.pausable.timeout < 300) { // 5 minutes minimum throw new Error('Emergency pause timeout must be at least 300 seconds'); } } } getFunctionName(config, functionKey) { var _a; if (!(config === null || config === void 0 ? void 0 : config.customFunctions)) { return this.getDefaultFunctionName(functionKey); } const customFunction = config.customFunctions[functionKey]; if (!customFunction) { return this.getDefaultFunctionName(functionKey); } if (typeof customFunction === 'string') { return customFunction; } return ((_a = customFunction.abi) === null || _a === void 0 ? void 0 : _a.toString()) || this.getDefaultFunctionName(functionKey); } getDefaultFunctionName(functionKey) { // Map of default function names const defaults = { facets: 'facets', facetSelectors: 'facetFunctionSelectors', facetAddresses: 'facetAddresses', facetAddress: 'facetAddress', owner: 'owner', transferOwnership: 'transferOwnership', diamondCut: 'diamondCut', init: 'init', supportsInterface: 'supportsInterface', // ... add other defaults as needed }; return defaults[functionKey] || functionKey; } async isVersioned() { var _a, _b, _c, _d, _e, _f, _g; try { const config = await configLoader_1.configLoader.loadConfig(); const versioningEnabled = (_d = (_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.versioning) === null || _d === void 0 ? void 0 : _d.enabled; if (!versioningEnabled) { return false; } const versionFunction = this.getFunctionName((_g = (_f = (_e = config.contracts) === null || _e === void 0 ? void 0 : _e.diamond) === null || _f === void 0 ? void 0 : _f.standards) === null || _g === void 0 ? void 0 : _g.versioning, 'getVersion'); const diamondAddress = process.env.DIAMOND_ADDRESS; if (!diamondAddress) { throw new Error('DIAMOND_ADDRESS environment variable not set'); } const diamond = new ethers_1.Contract(diamondAddress, [`function ${versionFunction}() view returns (string)`], await (0, hardhatHelpers_1.getProvider)()); await diamond[versionFunction](); return true; } catch (_h) { return false; } } async validateRequiredInterfaces(interfaces) { var _a; try { const diamondAddress = process.env.DIAMOND_ADDRESS; if (!diamondAddress) { throw new Error('DIAMOND_ADDRESS environment variable not set'); } const provider = await (0, hardhatHelpers_1.getProvider)(); const erc165Config = (_a = this.standards) === null || _a === void 0 ? void 0 : _a.erc165; const supportsInterfaceFunction = this.getFunctionName(erc165Config, 'supportsInterface'); const diamond = new ethers_1.Contract(diamondAddress, [`function ${supportsInterfaceFunction}(bytes4) view returns (bool)`], provider); for (const iface of interfaces) { const supported = await diamond[supportsInterfaceFunction](iface); if (!supported) { throw new Error(`Required interface ${iface} is not supported`); } } } catch (error) { logger_1.Logger.error('Failed to validate required interfaces'); throw error; } } } exports.DiamondStandards = DiamondStandards; DiamondStandards.DEFAULT_STANDARDS = { versioning: { enabled: false, strategy: 'semver', functions: { getVersion: 'version', getVersionHistory: 'versionHistory' } }, loupe: { name: 'DiamondLoupeFacet', customFunctions: { facets: 'facets', facetSelectors: 'facetFunctionSelectors', facetAddresses: 'facetAddresses', facetAddress: 'facetAddress' }, returnTypes: { facets: 'tuple(address facetAddress, bytes4[] functionSelectors)[]', selectors: 'bytes4[]', addresses: 'address[]', address: 'address' } }, cut: { name: 'DiamondCutFacet', customFunctions: { diamondCut: 'diamondCut' }, params: { facetCutStructName: 'FacetCut', actionEnumName: 'FacetCutAction' }, actions: { add: 0, replace: 1, remove: 2 }, batchSize: 10, retryStrategy: { enabled: false, maxAttempts: 3, backoffMs: 1000 } }, ownership: { name: 'OwnershipFacet', customFunctions: { owner: 'owner', transferOwnership: 'transferOwnership' }, type: 'single-step', multiSig: { enabled: false, threshold: 1, owners: [] } }, init: { name: 'DiamondInit', customFunctions: { init: 'init' }, fallbackAddress: ethers_1.ethers.constants.AddressZero, validation: { validateInit: true, requireSuccess: true } }, erc165: { name: 'ERC165Facet', customFunctions: { supportsInterface: 'supportsInterface' }, interfaces: { IDiamondCut: '0x1f931c1c', IDiamondLoupe: '0x48e2b093', IERC165: '0x01ffc9a7', IERC173: '0x7f5828d0' }, validation: { requireSupport: [] } }, access: { name: 'AccessControlFacet', type: 'ownership', customFunctions: { hasRole: 'hasRole', grantRole: 'grantRole', revokeRole: 'revokeRole' }, roles: { admin: 'ADMIN_ROLE', upgrader: 'UPGRADER_ROLE', pauser: 'PAUSER_ROLE' } }, emergency: { name: 'EmergencyFacet', enabled: false, customFunctions: { pause: 'pause', unpause: 'unpause', isPaused: 'paused' }, pausable: { selective: false, timeout: 0 } } }; exports.diamondStandards = DiamondStandards.getInstance(); //# sourceMappingURL=diamondStandards.js.map