@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
JavaScript
;
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