@openzeppelin/cli
Version:
Command-line interface for the OpenZeppelin smart contract platform
202 lines • 9.55 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs_1 = __importDefault(require("fs"));
const fs_extra_1 = require("fs-extra");
const axios_1 = __importDefault(require("axios"));
const solc_wrapper_1 = __importDefault(require("solc-wrapper"));
const semver_1 = __importDefault(require("semver"));
const lodash_1 = require("lodash");
const upgrades_1 = require("@openzeppelin/upgrades");
const os_1 = require("os");
const path_1 = __importDefault(require("path"));
const child_1 = __importDefault(require("../../../utils/child"));
const try_1 = require("../../../utils/try");
const solidity_1 = require("../../../utils/solidity");
const ethereumjs_util_1 = require("ethereumjs-util");
// Downloaded compilers will be stored here.
// TODO: Check writeability and fall back to tmp if needed
let SOLC_CACHE_PATH = path_1.default.join(os_1.homedir(), '.solc');
// Modified ENV to use when running native solc
let SOLC_BIN_ENV = null;
// How frequently to renew the solc list
const SOLC_LIST_EXPIRES_IN_SECONDS = 1 * 60 * 60; // 1 hour
// and where to download it from
const SOLC_LIST_URL = 'https://solc-bin.ethereum.org/bin/list.json';
class SolcjsCompiler {
constructor(compilerBinary) {
this.compiler = solc_wrapper_1.default(compilerBinary);
}
version() {
return this.compiler.version();
}
compile(input) {
return __awaiter(this, void 0, void 0, function* () {
return JSON.parse(this.compiler.compile(JSON.stringify(input), undefined));
});
}
}
class SolcBinCompiler {
constructor(version) {
this._version = version;
}
version() {
return this._version;
}
compile(input) {
return __awaiter(this, void 0, void 0, function* () {
const output = child_1.default.execSync('solc --standard-json', {
input: JSON.stringify(input),
env: SOLC_BIN_ENV,
});
return JSON.parse(output.toString());
});
}
}
function resolveCompilerVersion(requiredSemver) {
return __awaiter(this, void 0, void 0, function* () {
// Create an array with all unique semver restrictions (dropping initial 'v' if set manually by the user)
const requiredSemvers = lodash_1.uniq(lodash_1.compact(lodash_1.castArray(requiredSemver))).map(str => str.startsWith('v') ? str.slice(1) : str);
// TODO: Pin the compiler release, so a new release does not cause all contracts to be recompiled and thus redeployed
const solcList = yield getAvailableCompilerVersions();
const build = yield getCompilerVersion(requiredSemvers, solcList);
return build;
});
}
exports.resolveCompilerVersion = resolveCompilerVersion;
function fetchCompiler(build) {
return __awaiter(this, void 0, void 0, function* () {
// Try local compiler and see if version matches
const localVersion = yield localCompilerVersion();
if (localVersion && solidity_1.compilerVersionsMatch(localVersion, build.longVersion)) {
upgrades_1.Loggy.onVerbose(__filename, 'fetchCompiler', 'download-compiler', `Using local solc compiler found`);
return new SolcBinCompiler(localVersion);
}
// Go with emscriptem version if not
const localFile = path_1.default.join(SOLC_CACHE_PATH, build.path);
if (!fs_1.default.existsSync(localFile))
yield downloadCompiler(build, localFile);
const compilerBinary = getCompilerBinary(localFile);
// Wrap emscriptem with solc-wrapper
return new SolcjsCompiler(compilerBinary);
});
}
exports.fetchCompiler = fetchCompiler;
function getCompiler(requiredSemver) {
return __awaiter(this, void 0, void 0, function* () {
const version = yield resolveCompilerVersion(requiredSemver);
return fetchCompiler(version);
});
}
exports.getCompiler = getCompiler;
function localCompilerVersion() {
return __awaiter(this, void 0, void 0, function* () {
const output = yield try_1.tryAwait(() => child_1.default.exec('solc --version', { env: SOLC_BIN_ENV }));
if (!output)
return null;
const match = output.stdout.match(/^Version: ([^\s]+)/m);
if (!match)
return null;
return match[1];
});
}
function getAvailableCompilerVersions() {
return __awaiter(this, void 0, void 0, function* () {
const localPath = path_1.default.join(SOLC_CACHE_PATH, 'list.json');
yield fs_extra_1.mkdirp(SOLC_CACHE_PATH);
if (fs_1.default.existsSync(localPath) && Date.now() - +fs_1.default.statSync(localPath).mtime < SOLC_LIST_EXPIRES_IN_SECONDS * 1000) {
return fs_extra_1.readJson(localPath);
}
try {
const response = yield axios_1.default.get(SOLC_LIST_URL);
const list = response.data;
yield fs_extra_1.writeJson(localPath, list);
return list;
}
catch (err) {
if (fs_1.default.existsSync(localPath)) {
upgrades_1.Loggy.noSpin.warn(__filename, 'getAvailableCompilerVersions', 'get-compiler-versions', `Error downloading solc releases list, using cached version`);
return fs_extra_1.readJson(localPath);
}
else {
err.message = `Could not retrieve solc releases list: ${err.message}`;
throw err;
}
}
});
}
function getCompilerVersion(requiredSemvers, solcList) {
return __awaiter(this, void 0, void 0, function* () {
// Returns build from list given a version
const getBuild = (version) => solcList.builds.find(build => build.path === solcList.releases[version]);
// Return latest release if there are no restrictions
if (requiredSemvers.length === 0)
return getBuild(solcList.latestRelease);
// Look for the most recent release that matches semver restriction
const comparatorSet = requiredSemvers.join(' ');
const releases = Object.keys(solcList.releases);
const maxRelease = semver_1.default.maxSatisfying(releases, comparatorSet);
if (maxRelease)
return getBuild(maxRelease);
// If user asked for a specific version, look for it
if (requiredSemvers.length === 1) {
const build = lodash_1.reverse(solcList.builds).find(b => b.path.startsWith(`soljson-v${requiredSemvers[0]}`));
if (build)
return build;
}
throw new Error(`Could not find a compiler that matches required versions (${comparatorSet})`);
});
}
function downloadCompiler(build, localFile) {
return __awaiter(this, void 0, void 0, function* () {
const { version, keccak256: expectedHash, path: versionPath } = build;
upgrades_1.Loggy.onVerbose(__filename, 'downloadCompiler', 'download-compiler', `Downloading compiler version ${version}`);
const url = `https://solc-bin.ethereum.org/bin/${versionPath}`;
const { data: compilerSource } = yield axios_1.default.get(url);
// Verify checksum
const hash = '0x' + ethereumjs_util_1.keccak256(compilerSource).toString('hex');
if (hash !== expectedHash) {
throw new Error(`Checksum of compiler downloaded from ${url} does not match (expected ${expectedHash} but got ${hash})`);
}
// Cache downloaded source
yield fs_extra_1.mkdirp(SOLC_CACHE_PATH);
fs_1.default.writeFileSync(localFile, compilerSource);
upgrades_1.Loggy.succeed('download-compiler');
});
}
function getCompilerBinary(compilerPath) {
// This magic is necessary as ts-node gets stuck when requiring the compiler large asm file
// Copied from https://github.com/nomiclabs/buidler/blob/0a551dca18522ac2ac0cf2143d71580bf29f5dce/packages/buidler-core/src/internal/solidity/compiler/index.ts#L72-L89
const Module = module.constructor;
const previousHook = Module._extensions['.js'];
Module._extensions['.js'] = function (module, filename) {
const content = fs_1.default.readFileSync(filename, 'utf8');
Object.getPrototypeOf(module)._compile.call(module, content, filename);
};
// eslint-disable-next-line @typescript-eslint/no-var-requires
const loadedSolc = require(compilerPath);
Module._extensions['.js'] = previousHook;
return loadedSolc;
}
exports.getCompilerBinary = getCompilerBinary;
// Used for tests
function setSolcCachePath(value) {
SOLC_CACHE_PATH = value;
}
exports.setSolcCachePath = setSolcCachePath;
function setSolcBinEnv(value) {
SOLC_BIN_ENV = value;
}
exports.setSolcBinEnv = setSolcBinEnv;
//# sourceMappingURL=CompilerProvider.js.map
;