@matterlabs/hardhat-zksync-solc
Version:
Hardhat plugin to compile smart contracts for the ZKsync network
370 lines (369 loc) • 18.4 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.saltFromUrl = exports.ZKSOLC_BIN_REPOSITORY = exports.getZksolcUrl = void 0;
const task_names_1 = require("hardhat/builtin-tasks/task-names");
const config_env_1 = require("hardhat/internal/core/config/config-env");
const global_dir_1 = require("hardhat/internal/util/global-dir");
require("./type-extensions");
const artifacts_1 = require("hardhat/internal/artifacts");
const await_semaphore_1 = require("hardhat/internal/vendor/await-semaphore");
const fs_1 = __importDefault(require("fs"));
const chalk_1 = __importDefault(require("chalk"));
const semver_1 = __importDefault(require("semver"));
const debug_1 = __importDefault(require("debug"));
const compile_1 = require("./compile");
const utils_1 = require("./utils");
Object.defineProperty(exports, "getZksolcUrl", { enumerable: true, get: function () { return utils_1.getZksolcUrl; } });
Object.defineProperty(exports, "saltFromUrl", { enumerable: true, get: function () { return utils_1.saltFromUrl; } });
const constants_1 = require("./constants");
Object.defineProperty(exports, "ZKSOLC_BIN_REPOSITORY", { enumerable: true, get: function () { return constants_1.ZKSOLC_BIN_REPOSITORY; } });
const downloader_1 = require("./compile/downloader");
const zkvm_solc_downloader_1 = require("./compile/zkvm-solc-downloader");
const config_extractor_1 = require("./config-extractor");
const plugin_1 = require("./plugin");
require("@matterlabs/hardhat-zksync-telemetry");
const logDebug = (0, debug_1.default)('hardhat:core:tasks:compile');
const extractors = [
new config_extractor_1.SolcStringUserConfigExtractor(),
new config_extractor_1.SolcSoloUserConfigExtractor(),
new config_extractor_1.SolcMultiUserConfigExtractor(),
];
const zkVmSolcCompilerDownloaderMutex = new await_semaphore_1.Mutex();
const zkSolcCompilerDownloaderMutex = new await_semaphore_1.Mutex();
(0, config_env_1.extendConfig)((config, userConfig) => {
if (userConfig.zksolc?.settings?.compilerPath) {
constants_1.defaultZkSolcConfig.version = constants_1.ZKSOLC_COMPILER_PATH_VERSION;
}
config.zksolc = { ...constants_1.defaultZkSolcConfig, ...userConfig?.zksolc };
config.zksolc.settings = { ...constants_1.defaultZkSolcConfig.settings, ...userConfig?.zksolc?.settings };
config.zksolc.settings.optimizer = {
...constants_1.defaultZkSolcConfig.settings.optimizer,
...userConfig?.zksolc?.settings?.optimizer,
};
config.zksolc.settings.libraries = {
...constants_1.defaultZkSolcConfig.settings.libraries,
...userConfig?.zksolc?.settings?.libraries,
};
});
(0, config_env_1.extendEnvironment)((hre) => {
if (hre.network.config.zksync) {
hre.network.zksync = hre.network.config.zksync;
let artifactsPath = hre.config.paths.artifacts;
if (!artifactsPath.endsWith('-zk')) {
artifactsPath = `${artifactsPath}-zk`;
}
let cachePath = hre.config.paths.cache;
if (!cachePath.endsWith('-zk')) {
cachePath = `${cachePath}-zk`;
}
// Forcibly update the artifacts object.
hre.config.paths.artifacts = artifactsPath;
hre.config.paths.cache = cachePath;
hre.artifacts = new artifacts_1.Artifacts(artifactsPath);
hre.config.solidity.compilers.forEach(async (compiler) => (0, utils_1.updateDefaultCompilerConfig)({ compiler }, hre.config.zksolc));
for (const [file, compiler] of Object.entries(hre.config.solidity.overrides)) {
(0, utils_1.updateDefaultCompilerConfig)({ compiler, file }, hre.config.zksolc);
}
}
});
(0, config_env_1.task)(task_names_1.TASK_COMPILE).setAction(async (compilationArgs, hre, runSuper) => {
if (hre.network.zksync) {
await hre.run(constants_1.TASK_DOWNLOAD_ZKSOLC);
await hre.run(constants_1.TASK_UPDATE_SOLIDITY_COMPILERS);
}
await runSuper(compilationArgs);
});
(0, config_env_1.subtask)(constants_1.TASK_COMPILE_LINK)
.addParam('sourceName', 'Source name of the artifact')
.addParam('contractName', 'Contract name of the artifact')
.addOptionalParam('libraries', undefined, undefined, config_env_1.types.any)
.addFlag('withoutError', undefined)
.setAction(plugin_1.compileLink);
(0, config_env_1.subtask)(constants_1.TASK_DOWNLOAD_ZKSOLC, async (_args, hre) => {
if (!hre.network.zksync) {
return;
}
const compilersCache = await (0, global_dir_1.getCompilersDir)();
await zkSolcCompilerDownloaderMutex.use(async () => {
const zksolcDownloader = await downloader_1.ZksolcCompilerDownloader.getDownloaderWithVersionValidated(hre.config.zksolc.version, hre.config.zksolc.settings.compilerPath ?? '', compilersCache);
const isZksolcDownloaded = await zksolcDownloader.isCompilerDownloaded();
if (!isZksolcDownloaded) {
await zksolcDownloader.downloadCompiler();
}
hre.config.zksolc.settings.compilerPath = zksolcDownloader.getCompilerPath();
hre.config.zksolc.version = zksolcDownloader.getVersion();
});
});
(0, config_env_1.subtask)(constants_1.TASK_UPDATE_SOLIDITY_COMPILERS, async (_args, hre) => {
if (!hre.network.zksync) {
return;
}
const userSolidityConfig = hre.userConfig.solidity;
const extractedConfigs = extractors
.find((extractor) => extractor.suitable(userSolidityConfig))
?.extract(userSolidityConfig);
hre.config.solidity.compilers.forEach(async (compiler) => (0, utils_1.updateBreakableCompilerConfig)({ compiler }, hre.config.zksolc, semver_1.default.gte(hre.config.zksolc.version, '1.5.13') ? '1.0.2' : '1.0.1', extractedConfigs?.compilers ?? []));
for (const [file, compiler] of Object.entries(hre.config.solidity.overrides)) {
(0, utils_1.updateBreakableCompilerConfig)({ compiler, file }, hre.config.zksolc, semver_1.default.gte(hre.config.zksolc.version, '1.5.13') ? '1.0.2' : '1.0.1', extractedConfigs?.overides ?? new Map());
}
});
(0, config_env_1.subtask)(task_names_1.TASK_COMPILE_SOLIDITY_GET_SOURCE_NAMES, async (args, hre, runSuper) => {
if (hre.network.zksync !== true) {
return await runSuper(args);
}
if (hre.config.zksolc.settings.forceContractsToCompile) {
return hre.config.zksolc.settings.forceContractsToCompile;
}
const contractsToCompile = hre.config.zksolc.settings.contractsToCompile;
if (!contractsToCompile || contractsToCompile.length === 0) {
return await runSuper(args);
}
const sourceNames = await runSuper(args);
return sourceNames.filter((sourceName) => contractsToCompile.some((contractToCompile) => sourceName.includes(contractToCompile)));
});
// This override is needed to invalidate cache when zksolc config is changed.
(0, config_env_1.subtask)(task_names_1.TASK_COMPILE_SOLIDITY_GET_COMPILATION_JOBS, async (args, hre, runSuper) => {
const { jobs, errors } = await runSuper(args);
if (hre.network.zksync !== true || hre.config.zksolc.compilerSource !== 'binary') {
return { jobs, errors };
}
jobs.forEach((job) => {
job.solidityConfig.zksolc = hre.config.zksolc;
job.solidityConfig.zksolc.settings.compilerPath = hre.config.zksolc.settings.compilerPath;
if (hre.config.zksolc.settings.libraries) {
job.solidityConfig.settings.libraries = hre.config.zksolc.settings.libraries;
}
});
return { jobs, errors };
});
(0, config_env_1.subtask)(task_names_1.TASK_COMPILE_SOLIDITY_GET_ARTIFACT_FROM_COMPILATION_OUTPUT, async ({ sourceName, contractName, contractOutput, }, hre) => {
if (hre.network.zksync !== true) {
return (0, artifacts_1.getArtifactFromContractOutput)(sourceName, contractName, contractOutput);
}
let bytecode = contractOutput.evm?.bytecode?.object || contractOutput.evm?.deployedBytecode?.object || '';
bytecode = (0, utils_1.zeroxlify)(bytecode);
const factoryDeps = {};
const entries = Object.entries(contractOutput.factoryDependencies || {});
for (const [hash, dependency] of entries) {
factoryDeps[(0, utils_1.zeroxlify)(hash)] = dependency;
}
return {
_format: constants_1.ZK_ARTIFACT_FORMAT_VERSION,
contractName,
sourceName,
abi: contractOutput.abi,
// technically, zkEVM has no difference between bytecode & deployedBytecode,
// but both fields are included just in case
bytecode,
deployedBytecode: bytecode,
// zksolc does not support unlinked objects,
// all external libraries are either linked during compilation or inlined
linkReferences: {},
deployedLinkReferences: {},
// ZKsync-specific field
factoryDeps,
};
});
(0, config_env_1.subtask)(task_names_1.TASK_COMPILE_SOLIDITY_RUN_SOLC, async (args, hre, runSuper) => {
if (hre.network.zksync !== true) {
return await runSuper(args);
}
return await (0, compile_1.compile)(hre.config.zksolc, args.input, args.solcPath);
});
(0, config_env_1.subtask)(task_names_1.TASK_COMPILE_SOLIDITY_RUN_SOLCJS, async (args, hre, runSuper) => {
if (hre.network.zksync !== true) {
return await runSuper(args);
}
const solcPath = `${args.solcJsPath}.executable`;
if (!fs_1.default.existsSync(solcPath)) {
const solcJsExecutableCode = (0, utils_1.generateSolcJSExecutableCode)(args.solcJsPath, process.cwd());
fs_1.default.writeFileSync(solcPath, Buffer.from(solcJsExecutableCode), { encoding: 'utf-8', flag: 'w' });
fs_1.default.chmodSync(solcPath, '755');
}
return await (0, compile_1.compile)(hre.config.zksolc, args.input, solcPath);
});
/*
* This task is overriden to:
* - use valid zkvm solc version if that is needed and return valid SolcBuild object
*/
(0, config_env_1.subtask)(task_names_1.TASK_COMPILE_SOLIDITY_COMPILE_SOLC, async (args, hre, runSuper) => {
if (hre.network.zksync !== true) {
return await runSuper(args);
}
const solcBuild = await hre.run(task_names_1.TASK_COMPILE_SOLIDITY_GET_SOLC_BUILD, {
quiet: args.quiet,
solcVersion: args.solcVersion,
compilationJob: args.compilationJob,
});
await hre.run(task_names_1.TASK_COMPILE_SOLIDITY_LOG_RUN_COMPILER_START, {
compilationJob: args.compilationJob,
compilationJobs: args.compilationJobs,
compilationJobIndex: args.compilationJobIndex,
quiet: args.quiet,
});
let output;
if (solcBuild.isSolcJs) {
output = await hre.run(task_names_1.TASK_COMPILE_SOLIDITY_RUN_SOLCJS, {
input: args.input,
solcJsPath: solcBuild.compilerPath,
});
}
else {
output = await hre.run(task_names_1.TASK_COMPILE_SOLIDITY_RUN_SOLC, {
input: args.input,
solcPath: solcBuild.compilerPath,
});
}
await hre.run(task_names_1.TASK_COMPILE_SOLIDITY_LOG_RUN_COMPILER_END, {
compilationJob: args.compilationJob,
compilationJobs: args.compilationJobs,
compilationJobIndex: args.compilationJobIndex,
output,
quiet: args.quiet,
});
return { output, solcBuild };
});
// This task is overriden to:
// - prevent unnecessary solc downloads when using docker
// - download zksolc binary if needed
// - validate zksolc binary
(0, config_env_1.subtask)(task_names_1.TASK_COMPILE_SOLIDITY_GET_SOLC_BUILD, async (args, hre, runSuper) => {
if (hre.network.zksync !== true) {
return await runSuper(args);
}
if (hre.config.zksolc.compilerSource === 'docker') {
// Versions are wrong here when using docker, because there is no
// way to know them beforehand except to run the docker image, which
// adds 5-10 seconds to startup time. We cannot read them from artifacts,
// since that would make cache invalid every time, if the version is
// different from the one in the docker image.
//
// If you wish to know the actual versions from build-info files,
// please look at `output.version`, `output.long_version`
// and `output.zk_version` in the generated JSON.
return {
compilerPath: '',
isSolcJs: false,
version: args.solcVersion,
longVersion: '',
};
}
const compiler = args.compilationJob?.getSolcConfig();
if (compiler && compiler.eraVersion) {
const compilersCache = await (0, global_dir_1.getCompilersDir)();
let compilerPath = '';
let version = '';
let normalizedVersion = '';
await zkVmSolcCompilerDownloaderMutex.use(async () => {
const zkVmSolcCompilerDownloader = await zkvm_solc_downloader_1.ZkVmSolcCompilerDownloader.getDownloaderWithVersionValidated(compiler.eraVersion, compiler.version, compilersCache);
const isZksolcDownloaded = await zkVmSolcCompilerDownloader.isCompilerDownloaded();
if (!isZksolcDownloaded) {
await zkVmSolcCompilerDownloader.downloadCompiler();
}
compilerPath = zkVmSolcCompilerDownloader.getCompilerPath();
version = zkVmSolcCompilerDownloader.getVersion();
normalizedVersion = (0, utils_1.getZkVmNormalizedVersion)(zkVmSolcCompilerDownloader.getSolcVersion(), zkVmSolcCompilerDownloader.getZkVmSolcVersion());
});
console.info(chalk_1.default.yellow((0, constants_1.COMPILING_INFO_MESSAGE_ZKVM_SOLC)(hre.config.zksolc.version, version)));
return {
compilerPath,
isSolcJs: false,
version,
longVersion: normalizedVersion,
};
}
else {
const solcBuild = await runSuper(args);
console.info(chalk_1.default.yellow((0, constants_1.COMPILING_INFO_MESSAGE)(hre.config.zksolc.version, args.solcVersion)));
return solcBuild;
}
});
(0, config_env_1.subtask)(task_names_1.TASK_COMPILE_SOLIDITY_LOG_COMPILATION_RESULT, async ({ compilationJobs }, hre, _runSuper) => {
if (hre.config.zksolc.settings.areLibrariesMissing) {
console.info(chalk_1.default.yellow(constants_1.MISSING_LIBRARIES_NOTICE));
console.info(chalk_1.default.red(constants_1.COMPILE_AND_DEPLOY_LIBRARIES_INSTRUCTIONS));
console.info(chalk_1.default.yellow(constants_1.MISSING_LIBRARY_LINK));
}
else {
let count = 0;
for (const job of compilationJobs) {
count += job.getResolvedFiles().filter((file) => job.emitsArtifacts(file)).length;
}
if (count > 0) {
console.info(chalk_1.default.green(`Successfully compiled ${count} Solidity ${(0, utils_1.pluralize)(count, 'file')}`));
}
}
});
(0, config_env_1.subtask)(task_names_1.TASK_COMPILE_SOLIDITY_LOG_DOWNLOAD_COMPILER_START).setAction(async ({ isCompilerDownloaded, solcVersion, }) => {
if (isCompilerDownloaded) {
return;
}
console.info(chalk_1.default.yellow(`Downloading solc ${solcVersion}`));
});
(0, config_env_1.subtask)(task_names_1.TASK_COMPILE_SOLIDITY_LOG_RUN_COMPILER_START).setAction(async ({ compilationJob, }) => {
const count = compilationJob.getResolvedFiles().length;
if (count > 0) {
console.info(chalk_1.default.yellow(`Compiling ${count} Solidity ${(0, utils_1.pluralize)(count, 'file')}`));
}
});
(0, config_env_1.subtask)(task_names_1.TASK_COMPILE_SOLIDITY_EMIT_ARTIFACTS).setAction(async ({ compilationJob, input, output, solcBuild, }, { artifacts, run, network }, runSuper) => {
if (network.zksync !== true) {
return await runSuper({
compilationJob,
input,
output,
solcBuild,
});
}
const version = compilationJob.getSolcConfig().eraVersion
? (0, utils_1.getZkVmNormalizedVersion)(compilationJob.getSolcConfig().version, compilationJob.getSolcConfig().eraVersion)
: compilationJob.getSolcConfig().version;
const pathToBuildInfo = await artifacts.saveBuildInfo(version, solcBuild.longVersion, input, output);
const artifactsEmittedPerFile = await Promise.all(compilationJob
.getResolvedFiles()
.filter((f) => compilationJob.emitsArtifacts(f))
.map(async (file) => {
const artifactsEmitted = await Promise.all(Object.entries(output.contracts?.[file.sourceName] ?? {}).map(async ([contractName, contractOutput]) => {
logDebug(`Emitting artifact for contract '${contractName}'`);
const artifact = await run(task_names_1.TASK_COMPILE_SOLIDITY_GET_ARTIFACT_FROM_COMPILATION_OUTPUT, {
sourceName: file.sourceName,
contractName,
contractOutput,
});
await artifacts.saveArtifactAndDebugFile(artifact, pathToBuildInfo);
return artifact.contractName;
}));
return {
file,
artifactsEmitted,
};
}));
return { artifactsEmittedPerFile };
});
(0, config_env_1.subtask)(task_names_1.TASK_COMPILE_SOLIDITY_GET_COMPILER_INPUT, async (taskArgs, hre, runSuper) => {
const compilerInput = await runSuper(taskArgs);
if (hre.network.zksync !== true) {
return compilerInput;
}
if (hre.config.zksolc.settings.suppressedErrors && hre.config.zksolc.settings.suppressedErrors.length > 0) {
compilerInput.suppressedErrors = hre.config.zksolc.settings.suppressedErrors;
}
if (hre.config.zksolc.settings.suppressedWarnings && hre.config.zksolc.settings.suppressedWarnings.length > 0) {
compilerInput.suppressedWarnings = hre.config.zksolc.settings.suppressedWarnings;
}
return compilerInput;
});
(0, config_env_1.subtask)(task_names_1.TASK_COMPILE_REMOVE_OBSOLETE_ARTIFACTS, async (taskArgs, hre, runSuper) => {
if (hre.network.zksync !== true || !hre.config.zksolc.settings.areLibrariesMissing) {
return await runSuper(taskArgs);
}
// Delete all artifacts and cache files because there are missing libraries and the compilation output is invalid.
const artifactsDir = hre.config.paths.artifacts;
const cacheDir = hre.config.paths.cache;
fs_1.default.rmSync(artifactsDir, { recursive: true });
fs_1.default.rmSync(cacheDir, { recursive: true });
});
//# sourceMappingURL=index.js.map