UNPKG

dop-stick

Version:

Source control tooling for versionable-upgradeable smart contracts

191 lines 7.47 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ConfigValidator = void 0; class ConfigValidator { static async validateConfig(config) { var _a, _b; // Check required fields for (const field of this.REQUIRED_FIELDS) { if (!this.getNestedValue(config, field)) { throw new Error(`Missing required field: ${field}`); } } // Validate standards if present if ((_b = (_a = config.contracts) === null || _a === void 0 ? void 0 : _a.diamond) === null || _b === void 0 ? void 0 : _b.standards) { await this.validateStandards(config); } // Merge with defaults return this.mergeWithDefaults(config); } static async validateStandards(config) { var _a, _b, _c, _d, _e; 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; // Versioning validation if ((_c = standards.versioning) === null || _c === void 0 ? void 0 : _c.enabled) { if (standards.versioning.strategy === 'custom' && !standards.versioning.format) { throw new Error('Custom versioning strategy requires a format string'); } } // Cut validation if ((_e = (_d = standards.cut) === null || _d === void 0 ? void 0 : _d.retryStrategy) === null || _e === void 0 ? void 0 : _e.enabled) { if (standards.cut.retryStrategy.maxAttempts && standards.cut.retryStrategy.maxAttempts < 1) { throw new Error('Retry max attempts must be greater than 0'); } if (standards.cut.batchSize && (standards.cut.batchSize < 1 || standards.cut.batchSize > 100)) { throw new Error('Batch size must be between 1 and 100'); } } // Validate custom functions for each facet this.validateCustomFunctions(standards); } static validateCustomFunctions(standards) { const facets = ['cut', 'loupe', 'init', 'ownership', 'erc165', 'access', 'emergency']; for (const facet of facets) { const facetConfig = standards[facet]; if (!facetConfig) continue; if (facetConfig.customFunctions) { for (const [funcName, funcConfig] of Object.entries(facetConfig.customFunctions)) { if (typeof funcConfig === 'object') { // Validate additional params if (funcConfig.additionalParams) { for (const param of funcConfig.additionalParams) { if (!param.name || !param.type || !param.key || !param.location) { throw new Error(`Invalid additional parameter configuration in ${facet}.${funcName}`); } if (!['config', 'env'].includes(param.location)) { throw new Error(`Invalid parameter location '${param.location}' in ${facet}.${funcName}`); } } } } } } } } static mergeWithDefaults(config) { const baseConfig = { paths: { typechain: config.paths.typechain, upgrades: config.paths.upgrades }, initialization: { enabled: false, ...config.initialization } }; if (config.contracts) { baseConfig.contracts = {}; if (config.contracts.diamond) { baseConfig.contracts.diamond = { ...config.contracts.diamond, standards: config.contracts.diamond.standards ? this.mergeStandards(config.contracts.diamond.standards) : undefined }; } if (config.contracts.upgradeService) { baseConfig.contracts.upgradeService = { ...config.contracts.upgradeService }; } } baseConfig.security = { ownershipValidation: true, selectorCollisionCheck: true, facetAddressValidation: true, estimateGasBeforeUpgrade: true, ...config.security }; if (config.gas) { baseConfig.gas = { ...config.gas }; } return baseConfig; } static mergeStandards(standards) { const defaultStandards = { versioning: { enabled: false, strategy: 'semver' }, loupe: { name: 'DiamondLoupeFacet', customFunctions: { facets: 'facets', facetSelectors: 'facetFunctionSelectors', facetAddresses: 'facetAddresses', facetAddress: 'facetAddress' } }, cut: { name: 'DiamondCutFacet', customFunctions: { diamondCut: 'diamondCut' }, params: { facetCutStructName: 'FacetCut', actionEnumName: 'FacetCutAction' }, actions: { add: 0, replace: 1, remove: 2 } }, ownership: { name: 'OwnershipFacet', customFunctions: { owner: 'owner', transferOwnership: 'transferOwnership' }, type: 'single-step' }, erc165: { name: 'ERC165Facet', customFunctions: { supportsInterface: 'supportsInterface' }, interfaces: { IDiamondCut: '0x1f931c1c', IDiamondLoupe: '0x48e2b093', IERC165: '0x01ffc9a7', IERC173: '0x7f5828d0' } } }; return this.deepMerge(defaultStandards, standards); } static getNestedValue(obj, path) { return path.split('.').reduce((acc, part) => acc && acc[part], obj); } static deepMerge(target, source) { const output = { ...target }; if (isObject(target) && isObject(source)) { Object.keys(source).forEach(key => { if (isObject(source[key])) { if (!(key in target)) { Object.assign(output, { [key]: source[key] }); } else { output[key] = this.deepMerge(target[key], source[key]); } } else { Object.assign(output, { [key]: source[key] }); } }); } return output; } } exports.ConfigValidator = ConfigValidator; ConfigValidator.REQUIRED_FIELDS = [ 'paths.typechain', 'paths.upgrades' ]; function isObject(item) { return item && typeof item === 'object' && !Array.isArray(item); } //# sourceMappingURL=configValidator.js.map