UNPKG

@matterlabs/hardhat-zksync-solc

Version:
363 lines 17.4 kB
"use strict"; 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.getLibraryLink = exports.generateFQN = exports.getLatestEraVersion = exports.getZkVmNormalizedVersion = exports.saveDataToFile = exports.getLatestRelease = exports.download = exports.writeLibrariesToFile = exports.mapMissingLibraryDependencies = exports.findMissingLibraries = exports.generateSolcJSExecutableCode = exports.isVersionForDeprecation = exports.getVersionComponents = exports.pluralize = exports.getZkVmSolcUrl = exports.getZksolcUrl = exports.saltFromUrl = exports.sha1 = exports.isURL = exports.zeroxlify = exports.isBreakableCompilerVersion = exports.updateBreakableCompilerConfig = exports.updateDefaultCompilerConfig = exports.filterSupportedOutputSelections = void 0; const semver_1 = __importDefault(require("semver")); const crypto_1 = __importDefault(require("crypto")); const fs_extra_1 = __importDefault(require("fs-extra")); const proper_lockfile_1 = __importDefault(require("proper-lockfile")); const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const util_1 = __importDefault(require("util")); const chalk_1 = __importDefault(require("chalk")); const constants_1 = require("./constants"); const errors_1 = require("./errors"); const config_update_1 = require("./config-update"); const TEMP_FILE_PREFIX = 'tmp-'; function filterSupportedOutputSelections(outputSelection, zkCompilerVersion) { const filteredOutputSelection = {}; const versionComponents = getVersionComponents(zkCompilerVersion); let supportedOutputSelections; switch (true) { case versionComponents[0] <= 1 && versionComponents[1] <= 3 && versionComponents[2] <= 5: supportedOutputSelections = constants_1.ZKSOLC_COMPILERS_SELECTOR_MAP['1.3.5']; break; default: supportedOutputSelections = [...constants_1.ZKSOLC_COMPILERS_SELECTOR_MAP['1.3.5'], 'metadata', 'userdoc', 'devdoc']; break; } for (const [file, contractSelection] of Object.entries(outputSelection)) { filteredOutputSelection[file] = {}; for (const [contract, outputs] of Object.entries(contractSelection)) { filteredOutputSelection[file][contract] = outputs.filter((output) => supportedOutputSelections.includes(output)); } } return filteredOutputSelection; } exports.filterSupportedOutputSelections = filterSupportedOutputSelections; function updateDefaultCompilerConfig(solcConfigData, zksolc) { const compiler = solcConfigData.compiler; const settings = compiler.settings || {}; // Override the default solc optimizer settings with zksolc optimizer settings. compiler.settings = { ...settings, optimizer: { ...zksolc.settings.optimizer } }; zksolc.settings.enableEraVMExtensions = zksolc.settings.enableEraVMExtensions || zksolc.settings.isSystem || false; zksolc.settings.forceEVMLA = zksolc.settings.forceEVMLA || zksolc.settings.forceEvmla || false; if (zksolc.settings.isSystem !== undefined) { console.warn(chalk_1.default.blue(constants_1.COMPILER_ZKSOLC_IS_SYSTEM_USE)); delete zksolc.settings.isSystem; } if (zksolc.settings.forceEvmla !== undefined) { console.warn(chalk_1.default.blue(constants_1.COMPILER_ZKSOLC_FORCE_EVMLA_USE)); delete zksolc.settings.forceEvmla; } const [major, minor] = getVersionComponents(compiler.version); if (major === 0 && minor < 8) { console.warn(chalk_1.default.blue(constants_1.COMPILER_ZKSOLC_NEED_EVM_CODEGEN)); compiler.settings.forceEVMLA = true; } // Remove metadata settings from solidity settings. delete compiler.settings.metadata; // Override the solc metadata settings with zksolc metadata settings. if (zksolc.settings.metadata) { compiler.settings.metadata = { ...zksolc.settings.metadata }; } // zkSolc supports only a subset of solc output selections compiler.settings.outputSelection = filterSupportedOutputSelections(compiler.settings.outputSelection, zksolc.version); } exports.updateDefaultCompilerConfig = updateDefaultCompilerConfig; const solcUpdaters = [ new config_update_1.OverrideCompilerSolcUserConfigUpdater(), new config_update_1.CompilerSolcUserConfigUpdater(), ]; function updateBreakableCompilerConfig(solcConfigData, zksolc, latestEraVersion, userConfigCompilers) { const compiler = solcConfigData.compiler; if (isBreakableCompilerVersion(zksolc.version)) { compiler.settings.detectMissingLibraries = false; compiler.settings.forceEVMLA = zksolc.settings.forceEVMLA; compiler.settings.enableEraVMExtensions = zksolc.settings.enableEraVMExtensions; compiler.settings.codegen = zksolc.settings.codegen; compiler.settings.LLVMOptions = zksolc.settings.LLVMOptions; } solcUpdaters .find((updater) => updater.suituble(userConfigCompilers, solcConfigData.file)) ?.update(compiler, latestEraVersion, zksolc, userConfigCompilers, solcConfigData.file); if (zksolc.version !== 'latest' && compiler.eraVersion && semver_1.default.lt(zksolc.version, constants_1.ZKSOLC_COMPILER_VERSION_MIN_VERSION_WITH_ZKVM_COMPILER)) { console.warn(chalk_1.default.blue(constants_1.COMPILER_ZKSOLC_VERSION_WITH_ZKVM_SOLC_WARN)); compiler.eraVersion = undefined; } } exports.updateBreakableCompilerConfig = updateBreakableCompilerConfig; function isBreakableCompilerVersion(zksolcVersion) { return zksolcVersion === 'latest' || semver_1.default.gte(zksolcVersion, constants_1.ZKSOLC_COMPILER_MIN_VERSION_BREAKABLE_CHANGE); } exports.isBreakableCompilerVersion = isBreakableCompilerVersion; function zeroxlify(hex) { hex = hex.toLowerCase(); return hex.slice(0, 2) === '0x' ? hex : `0x${hex}`; } exports.zeroxlify = zeroxlify; function isURL(url) { try { const locator = new URL(url); return locator.protocol === 'http:' || locator.protocol === 'https:'; } catch (e) { return false; } } exports.isURL = isURL; function sha1(str) { return crypto_1.default.createHash('sha1').update(str).digest('hex'); } exports.sha1 = sha1; function saltFromUrl(url) { return sha1(url); } exports.saltFromUrl = saltFromUrl; function getZksolcUrl(repo, version) { // @ts-ignore const platform = { darwin: 'macosx', linux: 'linux', win32: 'windows' }[process.platform]; const toolchain = semver_1.default.lt(version, constants_1.COMPILER_MIN_LINUX_VERSION_WITH_GNU_TOOLCHAIN) ? // @ts-ignore { linux: '-musl', win32: '-gnu', darwin: '' }[process.platform] : // @ts-ignore { linux: '-gnu', win32: '-gnu', darwin: '' }[process.platform]; const arch = process.arch === 'x64' ? 'amd64' : process.arch; const ext = process.platform === 'win32' ? '.exe' : ''; return `${repo}/releases/download/${version}/zksolc-${platform}-${arch}${toolchain}-v${version}${ext}`; } exports.getZksolcUrl = getZksolcUrl; function getZkVmSolcUrl(repo, version) { // @ts-ignore const platform = { darwin: 'macosx', linux: 'linux', win32: 'windows' }[process.platform]; // @ts-ignore const arch = process.arch === 'x64' ? 'amd64' : process.arch; const ext = process.platform === 'win32' ? '.exe' : ''; return `${repo}/releases/download/${version}/solc-${platform}-${arch}-${version}${ext}`; } exports.getZkVmSolcUrl = getZkVmSolcUrl; function pluralize(n, singular, plural) { if (n === 1) { return singular; } if (plural !== undefined) { return plural; } return `${singular}s`; } exports.pluralize = pluralize; function getVersionComponents(version) { const versionComponents = version.split('.'); return [parseInt(versionComponents[0], 10), parseInt(versionComponents[1], 10), parseInt(versionComponents[2], 10)]; } exports.getVersionComponents = getVersionComponents; function isVersionForDeprecation(version) { return semver_1.default.lt(version, '1.4.0'); } exports.isVersionForDeprecation = isVersionForDeprecation; // Generate SolcJS executable code function generateSolcJSExecutableCode(solcJsPath, workingDir) { return constants_1.SOLCJS_EXECUTABLE_CODE.replace(/SOLCJS_PATH/g, solcJsPath).replace(/WORKING_DIR/g, workingDir); } exports.generateSolcJSExecutableCode = generateSolcJSExecutableCode; // Find all the libraries that are missing from the contracts function findMissingLibraries(zkSolcOutput) { const missingLibraries = new Set(); for (const filePath in zkSolcOutput.contracts) { if (!filePath) continue; for (const contractName in zkSolcOutput.contracts[filePath]) { if (!contractName) continue; const contract = zkSolcOutput.contracts[filePath][contractName]; if (contract.missingLibraries && contract.missingLibraries.length > 0) { contract.missingLibraries.forEach((library) => { missingLibraries.add(library); }); } } } return missingLibraries; } exports.findMissingLibraries = findMissingLibraries; function mapMissingLibraryDependencies(zkSolcOutput, missingLibraries) { const dependencyMap = new Array(); missingLibraries.forEach((library) => { const [libFilePath, libContractName] = library.split(':'); if (zkSolcOutput.contracts[libFilePath] && zkSolcOutput.contracts[libFilePath][libContractName]) { const contract = zkSolcOutput.contracts[libFilePath][libContractName]; if (contract.missingLibraries) { dependencyMap.push({ contractName: libContractName, contractPath: libFilePath, missingLibraries: contract.missingLibraries, }); } } }); return dependencyMap; } exports.mapMissingLibraryDependencies = mapMissingLibraryDependencies; // Get or create the libraries file. If the file doesn't exist, create it with an empty array const getOrCreateLibraries = async (filePath) => { // Ensure the file exists if (!(await fs_extra_1.default.pathExists(filePath))) { await fs_extra_1.default.outputFile(filePath, '[]'); // Initialize with an empty array } // Return the file's content return await fs_extra_1.default.readJSON(filePath); }; // Write missing libraries to file and lock the file while writing const writeLibrariesToFile = async (filePath, libraries) => { try { let existingLibraries = await getOrCreateLibraries(filePath); // Ensure that the file exists await proper_lockfile_1.default.lock(filePath, { retries: { retries: 10, maxTimeout: 1000 } }); existingLibraries = await getOrCreateLibraries(filePath); // Read again after locking const combinedLibraries = [...existingLibraries, ...libraries]; fs_extra_1.default.outputFileSync(filePath, JSON.stringify(combinedLibraries, null, 4)); } catch (e) { throw new errors_1.ZkSyncSolcPluginError(`Failed to write missing libraries file: ${e}`); } finally { await proper_lockfile_1.default.unlock(filePath); } }; exports.writeLibrariesToFile = writeLibrariesToFile; function resolveTempFileName(filePath) { const { dir, ext, name } = path_1.default.parse(filePath); return path_1.default.format({ dir, ext, name: `${TEMP_FILE_PREFIX}${name}`, }); } async function download(url, filePath, userAgent, timeoutMillis = 10000, extraHeaders = {}) { const { pipeline } = await Promise.resolve().then(() => __importStar(require('stream'))); const { getGlobalDispatcher, request } = await Promise.resolve().then(() => __importStar(require('undici'))); const streamPipeline = util_1.default.promisify(pipeline); const dispatcher = getGlobalDispatcher(); // Fetch the url const response = await request(url, { dispatcher, headersTimeout: timeoutMillis, maxRedirections: 10, method: 'GET', headers: { ...extraHeaders, 'User-Agent': `${userAgent}`, }, }); if (response.statusCode >= 200 && response.statusCode <= 299) { const tmpFilePath = resolveTempFileName(filePath); await fs_extra_1.default.ensureDir(path_1.default.dirname(filePath)); await streamPipeline(response.body, fs_1.default.createWriteStream(tmpFilePath)); return fs_extra_1.default.move(tmpFilePath, filePath, { overwrite: true }); } // undici's response bodies must always be consumed to prevent leaks const text = await response.body.text(); // eslint-disable-next-line throw new Error(`Failed to download ${url} - ${response.statusCode} received. ${text}`); } exports.download = download; async function getLatestRelease(owner, repo, userAgent, defaultValue, tagPrefix = 'v', timeout = constants_1.DEFAULT_TIMEOUT_MILISECONDS) { const url = `https://github.com/${owner}/${repo}/releases/latest`; const redirectUrlPattern = `https://github.com/${owner}/${repo}/releases/tag/${tagPrefix}`; const { request } = await Promise.resolve().then(() => __importStar(require('undici'))); try { const response = await request(url, { headersTimeout: timeout, maxRedirections: 0, method: 'GET', headers: { 'User-Agent': `${userAgent}`, }, }); // Check if the response is a redirect if (response.statusCode >= 300 && response.statusCode < 400) { // Get the URL from the 'location' header if (response.headers.location && typeof response.headers.location === 'string') { // Check if the redirect URL matches the expected pattern if (response.headers.location.startsWith(redirectUrlPattern)) { // Extract the tag from the redirect URL return response.headers.location.substring(redirectUrlPattern.length); } throw new errors_1.ZkSyncSolcPluginError(`Unexpected redirect URL: ${response.headers.location} for URL: ${url}`); } else { // Throw an error if the 'location' header is missing in a redirect response throw new errors_1.ZkSyncSolcPluginError(`Redirect location not found for URL: ${url}`); } } else { // Throw an error for non-redirect responses throw new errors_1.ZkSyncSolcPluginError(`Unexpected response status: ${response.statusCode} for URL: ${url}`); } } catch { return defaultValue; } } exports.getLatestRelease = getLatestRelease; async function saveDataToFile(data, targetPath) { await fs_extra_1.default.ensureDir(path_1.default.dirname(targetPath)); await fs_extra_1.default.writeJSON(targetPath, data, { spaces: 2 }); } exports.saveDataToFile = saveDataToFile; function getZkVmNormalizedVersion(solcVersion, zkVmSolcVersion) { return `zkVM-${solcVersion}-${zkVmSolcVersion}`; } exports.getZkVmNormalizedVersion = getZkVmNormalizedVersion; async function getLatestEraVersion() { return (await getLatestRelease(constants_1.ZKSOLC_BIN_OWNER, constants_1.ZKVM_SOLC_BIN_REPOSITORY_NAME, constants_1.USER_AGENT, constants_1.fallbackLatestEraCompilerVersion, '')).split('-')[1]; } exports.getLatestEraVersion = getLatestEraVersion; function generateFQN(sourceName, contractName) { return `${sourceName}:${contractName}`; } exports.generateFQN = generateFQN; async function getLibraryLink(hre, libraries, contractZbinPath) { if (libraries === undefined || Object.keys(libraries).length === 0) { return { contractZbinPath, }; } const populatedLibraries = {}; await Promise.all(Object.entries(libraries).map(async (libraryInfo) => { const artifact = await hre.artifacts.readArtifact(libraryInfo[0]); populatedLibraries[generateFQN(artifact.sourceName, artifact.contractName)] = libraryInfo[1]; })); return { contractZbinPath, libraries: populatedLibraries, }; } exports.getLibraryLink = getLibraryLink; //# sourceMappingURL=utils.js.map