UNPKG

@gnus.ai/upgrade-safe-transpiler-diamond

Version:

Solidity preprocessor used to generate OpenZeppelin Contracts Upgrade Safe using Diamond Pattern (EIP-2535).

190 lines (184 loc) 8.3 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.addDiamondStorage = void 0; const utils_1 = require("solidity-ast/utils"); const ast_utils_1 = require("../solc/ast-utils"); const path_1 = __importDefault(require("path")); const upgrades_overrides_1 = require("../utils/upgrades-overrides"); const rename_1 = require("../rename"); const new_function_position_1 = require("./utils/new-function-position"); const get_identifiers_used_1 = require("./utils/get-identifiers-used"); function* findUserDefinedTypes(node) { const seen = new Set(); for (const id of (0, utils_1.findAll)(['UserDefinedTypeName'], node)) { if ('pathNode' in id && id.pathNode !== undefined && !seen.has(id)) { seen.add(id.pathNode); yield id; } } } function addDiamondStorage(newFiles) { return function* (sourceUnit, tools) { const { resolver } = tools; const contracts = [...(0, utils_1.findAll)('ContractDefinition', sourceUnit)]; if (!contracts.some(c => c.contractKind === 'contract')) { return; } let buffer = ''; let contractNeedsStorage = false; const contractScopes = new Map(); const contractPaths = new Map(); const thisContractSet = new Set(); contractPaths.set(sourceUnit.absolutePath, thisContractSet); for (const contract of contracts) { const varDecls = [...(0, utils_1.findAll)('VariableDeclaration', contract)]; const variableNodes = varDecls.filter(v => v.stateVariable && !v.constant && !(0, upgrades_overrides_1.hasOverride)(v, 'state-variable-assignment') && !(0, upgrades_overrides_1.hasOverride)(v, 'state-variable-immutable')); if ((contract.contractKind === 'contract') && (variableNodes.length > 0)) { contractNeedsStorage = true; if (!thisContractSet.has(contract.name)) { thisContractSet.add(contract.name); } // move comments for each variable to this map const commentMap = new Map(); for (const varNode of variableNodes) { (0, get_identifiers_used_1.addVariableScopedContract)(contractPaths, varNode, tools.resolver); const vBounds = (0, ast_utils_1.getNodeBounds)(varNode); // grab first line of contract. const cStart = (0, new_function_position_1.newFunctionPosition)(contract, tools); const contractCode = tools.originalSource; const subContractCode = contractCode.substring(cStart, vBounds.start); const commentSplit = extractComments(subContractCode); let newSource = commentSplit[1].replace('/**', '/*'); newSource = newSource.replace(/[\t ]+$/, ''); commentMap.set(varNode.id, newSource); yield { start: vBounds.start - commentSplit[1].length, length: commentSplit[1].length, kind: 'remove-var-states-comments', text: '', }; } const referencedTypeDeclarations = (0, get_identifiers_used_1.getUniqueIdentifierVarsUsed)(contract, tools); for (const [_, identifierVar] of referencedTypeDeclarations) { const { varDecl } = identifierVar; const nodeInfo = resolver.resolveScope(varDecl.scope); if (nodeInfo.scopeNode && (nodeInfo.scopeNode.id !== sourceUnit.id)) { (0, get_identifiers_used_1.addVariableScopedContract)(contractPaths, varDecl, tools.resolver); } } buffer = makeStorageLib(contract.name, variableNodes, commentMap, buffer); } } if (contractNeedsStorage) { const newBuffer = `// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; ${(0, rename_1.getContractsImportPath)(contractPaths, path_1.default.parse(sourceUnit.absolutePath).dir)} ${buffer} `; const { dir, name, ext } = path_1.default.parse(sourceUnit.absolutePath); const newpath = path_1.default.format({ dir, ext, name: name + 'Storage' }); newFiles.push({ source: newBuffer, fileName: name + 'Storage', path: newpath }); } }; } exports.addDiamondStorage = addDiamondStorage; function extractComments(source) { let CommentType; (function (CommentType) { CommentType[CommentType["none"] = 0] = "none"; CommentType[CommentType["doubleSlash"] = 1] = "doubleSlash"; CommentType[CommentType["slashAsterisk"] = 2] = "slashAsterisk"; })(CommentType || (CommentType = {})); const whiteSpace = '\t \n'; let lastNonCommentIndex = -1; let commentType = CommentType.none; let sLen = source.length; for (let i = 0; i < sLen; i++) { // not currently processing comment if (commentType === CommentType.none) { // need to look ahead for comment start if (i < sLen - 1) { if ((source[i] === '/') && (source[i + 1] === '/')) { commentType = CommentType.doubleSlash; } else if ((source[i] === '/') && (source[i + 1] === '*')) { commentType = CommentType.slashAsterisk; } } } else { if ((commentType === CommentType.slashAsterisk)) { if ((source[i - 1] === '*') && (source[i] === '/')) { commentType = CommentType.none; continue; } } else if (commentType === CommentType.doubleSlash) { if (source[i] === '\n') { commentType = CommentType.none; continue; } } } if ((commentType === CommentType.none) && !whiteSpace.includes(source[i])) { lastNonCommentIndex = i; } } // keep line ending of non-comment character with it if ((lastNonCommentIndex < sLen - 1) && (source[lastNonCommentIndex + 1] === '\n')) { lastNonCommentIndex++; } return [source.substring(0, lastNonCommentIndex + 1), source.substring(lastNonCommentIndex + 1)]; } function makeStorageLib(name, variables, comments, buffer) { buffer += ` library ${name}Storage { struct Layout { ${variables.map(v => { var _a; let typeString = v.typeDescriptions.typeString || ''; if (((_a = v.typeName) === null || _a === void 0 ? void 0 : _a.nodeType) === 'UserDefinedTypeName') { const varTypeStrings = typeString.split(' ', 2); if (varTypeStrings.length == 2) { typeString = (0, rename_1.renamePath)(varTypeStrings[1]); } } typeString = filterIdentifierPaths(typeString); return comments.get(v.id) + ' ' + typeString + ' ' + v.name + ';'; }).join('\n')} } bytes32 internal constant STORAGE_SLOT = keccak256('openzeppelin.contracts.storage.${name}'); function layout() internal pure returns (Layout storage l) { bytes32 slot = STORAGE_SLOT; assembly { l.slot := slot } } } `; return buffer; } function renameIdentifierPath(sourceStr) { const matchStrings = sourceStr.split(/^[ \t]*([A-Za-z_][A-Za-z0-9_.]*)/, 3); let retString = matchStrings[1]; retString = (0, rename_1.renamePath)(retString); if (matchStrings.length > 2) { retString += matchStrings[2]; } return retString; } // Filter the identifier paths (remove 'struct ', 'enum ' and append function filterIdentifierPaths(sourceStr) { let splitStrings = sourceStr.split(/(enum |struct |contract )/s); let retString = splitStrings[0]; for (let i = 2; i < splitStrings.length; i += 2) { retString += renameIdentifierPath(splitStrings[i]); } return retString; } //# sourceMappingURL=add-diamond-storage.js.map