aboutsummaryrefslogtreecommitdiffstats
path: root/packages/sol-compiler/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/sol-compiler/src')
-rw-r--r--packages/sol-compiler/src/cli.ts52
-rw-r--r--packages/sol-compiler/src/compiler.ts389
-rw-r--r--packages/sol-compiler/src/globals.d.ts6
-rw-r--r--packages/sol-compiler/src/index.ts29
-rw-r--r--packages/sol-compiler/src/schemas/compiler_options_schema.ts27
-rw-r--r--packages/sol-compiler/src/utils/compiler.ts411
-rw-r--r--packages/sol-compiler/src/utils/constants.ts8
-rw-r--r--packages/sol-compiler/src/utils/encoder.ts20
-rw-r--r--packages/sol-compiler/src/utils/fs_wrapper.ts28
-rw-r--r--packages/sol-compiler/src/utils/types.ts44
-rw-r--r--packages/sol-compiler/src/utils/utils.ts6
11 files changed, 0 insertions, 1020 deletions
diff --git a/packages/sol-compiler/src/cli.ts b/packages/sol-compiler/src/cli.ts
deleted file mode 100644
index db0c09581..000000000
--- a/packages/sol-compiler/src/cli.ts
+++ /dev/null
@@ -1,52 +0,0 @@
-#!/usr/bin/env node
-// We need the above pragma since this script will be run as a command-line tool.
-
-import { logUtils } from '@0x/utils';
-import * as _ from 'lodash';
-import 'source-map-support/register';
-import * as yargs from 'yargs';
-
-import { Compiler } from './compiler';
-
-const DEFAULT_CONTRACTS_LIST = '*';
-const SEPARATOR = ',';
-
-(async () => {
- const argv = yargs
- .option('contracts-dir', {
- type: 'string',
- description: 'path of contracts directory to compile',
- })
- .option('artifacts-dir', {
- type: 'string',
- description: 'path to write contracts artifacts to',
- })
- .option('contracts', {
- type: 'string',
- description: 'comma separated list of contracts to compile',
- })
- .option('watch', {
- alias: 'w',
- default: false,
- })
- .help().argv;
- const contracts = _.isUndefined(argv.contracts)
- ? undefined
- : argv.contracts === DEFAULT_CONTRACTS_LIST
- ? DEFAULT_CONTRACTS_LIST
- : argv.contracts.split(SEPARATOR);
- const opts = {
- contractsDir: argv.contractsDir,
- artifactsDir: argv.artifactsDir,
- contracts,
- };
- const compiler = new Compiler(opts);
- if (argv.watch) {
- await compiler.watchAsync();
- } else {
- await compiler.compileAsync();
- }
-})().catch(err => {
- logUtils.log(err);
- process.exit(1);
-});
diff --git a/packages/sol-compiler/src/compiler.ts b/packages/sol-compiler/src/compiler.ts
deleted file mode 100644
index afa4cc5bb..000000000
--- a/packages/sol-compiler/src/compiler.ts
+++ /dev/null
@@ -1,389 +0,0 @@
-import { assert } from '@0x/assert';
-import {
- FallthroughResolver,
- FSResolver,
- NameResolver,
- NPMResolver,
- RelativeFSResolver,
- Resolver,
- SpyResolver,
- URLResolver,
-} from '@0x/sol-resolver';
-import { logUtils } from '@0x/utils';
-import { execSync } from 'child_process';
-import * as chokidar from 'chokidar';
-import { CompilerOptions, ContractArtifact, ContractVersionData, StandardOutput } from 'ethereum-types';
-import * as fs from 'fs';
-import * as _ from 'lodash';
-import * as path from 'path';
-import * as pluralize from 'pluralize';
-import * as semver from 'semver';
-import solc = require('solc');
-
-import { compilerOptionsSchema } from './schemas/compiler_options_schema';
-import {
- addHexPrefixToContractBytecode,
- compileDockerAsync,
- compileSolcJSAsync,
- createDirIfDoesNotExistAsync,
- getContractArtifactIfExistsAsync,
- getDependencyNameToPackagePath,
- getSolcJSReleasesAsync,
- getSourcesWithDependencies,
- getSourceTreeHash,
- makeContractPathsRelative,
- parseSolidityVersionRange,
- printCompilationErrorsAndWarnings,
-} from './utils/compiler';
-import { constants } from './utils/constants';
-import { fsWrapper } from './utils/fs_wrapper';
-import { utils } from './utils/utils';
-
-type TYPE_ALL_FILES_IDENTIFIER = '*';
-const ALL_CONTRACTS_IDENTIFIER = '*';
-const ALL_FILES_IDENTIFIER = '*';
-const DEFAULT_CONTRACTS_DIR = path.resolve('contracts');
-const DEFAULT_ARTIFACTS_DIR = path.resolve('artifacts');
-const DEFAULT_USE_DOCKERISED_SOLC = false;
-// Solc compiler settings cannot be configured from the commandline.
-// If you need this configured, please create a `compiler.json` config file
-// with your desired configurations.
-const DEFAULT_COMPILER_SETTINGS: solc.CompilerSettings = {
- optimizer: {
- enabled: false,
- },
- outputSelection: {
- [ALL_FILES_IDENTIFIER]: {
- [ALL_CONTRACTS_IDENTIFIER]: ['abi', 'evm.bytecode.object'],
- },
- },
-};
-const CONFIG_FILE = 'compiler.json';
-
-interface VersionToInputs {
- [solcVersion: string]: {
- standardInput: solc.StandardInput;
- contractsToCompile: string[];
- };
-}
-
-interface ContractPathToData {
- [contractPath: string]: ContractData;
-}
-
-interface ContractData {
- currentArtifactIfExists: ContractArtifact | void;
- sourceTreeHashHex: string;
- contractName: string;
-}
-
-/**
- * The Compiler facilitates compiling Solidity smart contracts and saves the results
- * to artifact files.
- */
-export class Compiler {
- private readonly _resolver: Resolver;
- private readonly _nameResolver: NameResolver;
- private readonly _contractsDir: string;
- private readonly _compilerSettings: solc.CompilerSettings;
- private readonly _artifactsDir: string;
- private readonly _solcVersionIfExists: string | undefined;
- private readonly _specifiedContracts: string[] | TYPE_ALL_FILES_IDENTIFIER;
- private readonly _useDockerisedSolc: boolean;
- /**
- * Instantiates a new instance of the Compiler class.
- * @param opts Optional compiler options
- * @return An instance of the Compiler class.
- */
- constructor(opts?: CompilerOptions) {
- const passedOpts = opts || {};
- assert.doesConformToSchema('opts', passedOpts, compilerOptionsSchema);
- // TODO: Look for config file in parent directories if not found in current directory
- const config: CompilerOptions = fs.existsSync(CONFIG_FILE)
- ? JSON.parse(fs.readFileSync(CONFIG_FILE).toString())
- : {};
- assert.doesConformToSchema('compiler.json', config, compilerOptionsSchema);
- this._contractsDir = path.resolve(passedOpts.contractsDir || config.contractsDir || DEFAULT_CONTRACTS_DIR);
- this._solcVersionIfExists = passedOpts.solcVersion || config.solcVersion;
- this._compilerSettings = passedOpts.compilerSettings || config.compilerSettings || DEFAULT_COMPILER_SETTINGS;
- this._artifactsDir = passedOpts.artifactsDir || config.artifactsDir || DEFAULT_ARTIFACTS_DIR;
- this._specifiedContracts = passedOpts.contracts || config.contracts || ALL_CONTRACTS_IDENTIFIER;
- this._useDockerisedSolc =
- passedOpts.useDockerisedSolc || config.useDockerisedSolc || DEFAULT_USE_DOCKERISED_SOLC;
- this._nameResolver = new NameResolver(this._contractsDir);
- const resolver = new FallthroughResolver();
- resolver.appendResolver(new URLResolver());
- resolver.appendResolver(new NPMResolver(this._contractsDir));
- resolver.appendResolver(new RelativeFSResolver(this._contractsDir));
- resolver.appendResolver(new FSResolver());
- resolver.appendResolver(this._nameResolver);
- this._resolver = resolver;
- }
- /**
- * Compiles selected Solidity files found in `contractsDir` and writes JSON artifacts to `artifactsDir`.
- */
- public async compileAsync(): Promise<void> {
- await createDirIfDoesNotExistAsync(this._artifactsDir);
- await createDirIfDoesNotExistAsync(constants.SOLC_BIN_DIR);
- await this._compileContractsAsync(this._getContractNamesToCompile(), true);
- }
- /**
- * Compiles Solidity files specified during instantiation, and returns the
- * compiler output given by solc. Return value is an array of outputs:
- * Solidity modules are batched together by version required, and each
- * element of the returned array corresponds to a compiler version, and
- * each element contains the output for all of the modules compiled with
- * that version.
- */
- public async getCompilerOutputsAsync(): Promise<StandardOutput[]> {
- const promisedOutputs = this._compileContractsAsync(this._getContractNamesToCompile(), false);
- return promisedOutputs;
- }
- public async watchAsync(): Promise<void> {
- console.clear(); // tslint:disable-line:no-console
- logUtils.logWithTime('Starting compilation in watch mode...');
- const MATCH_NOTHING_REGEX = '^$';
- const IGNORE_DOT_FILES_REGEX = /(^|[\/\\])\../;
- // Initially we watch nothing. We'll add the paths later.
- const watcher = chokidar.watch(MATCH_NOTHING_REGEX, { ignored: IGNORE_DOT_FILES_REGEX });
- const onFileChangedAsync = async () => {
- watcher.unwatch('*'); // Stop watching
- try {
- await this.compileAsync();
- logUtils.logWithTime('Found 0 errors. Watching for file changes.');
- } catch (err) {
- if (err.typeName === 'CompilationError') {
- logUtils.logWithTime(
- `Found ${err.errorsCount} ${pluralize('error', err.errorsCount)}. Watching for file changes.`,
- );
- } else {
- logUtils.logWithTime('Found errors. Watching for file changes.');
- }
- }
-
- const pathsToWatch = this._getPathsToWatch();
- watcher.add(pathsToWatch);
- };
- await onFileChangedAsync();
- watcher.on('change', (changedFilePath: string) => {
- console.clear(); // tslint:disable-line:no-console
- logUtils.logWithTime('File change detected. Starting incremental compilation...');
- // NOTE: We can't await it here because that's a callback.
- // Instead we stop watching inside of it and start it again when we're finished.
- onFileChangedAsync(); // tslint:disable-line no-floating-promises
- });
- }
- private _getPathsToWatch(): string[] {
- const contractNames = this._getContractNamesToCompile();
- const spyResolver = new SpyResolver(this._resolver);
- for (const contractName of contractNames) {
- const contractSource = spyResolver.resolve(contractName);
- // NOTE: We ignore the return value here. We don't want to compute the source tree hash.
- // We just want to call a SpyResolver on each contracts and it's dependencies and
- // this is a convenient way to reuse the existing code that does that.
- // We can then get all the relevant paths from the `spyResolver` below.
- getSourceTreeHash(spyResolver, contractSource.path);
- }
- const pathsToWatch = _.uniq(spyResolver.resolvedContractSources.map(cs => cs.absolutePath));
- return pathsToWatch;
- }
- private _getContractNamesToCompile(): string[] {
- let contractNamesToCompile;
- if (this._specifiedContracts === ALL_CONTRACTS_IDENTIFIER) {
- const allContracts = this._nameResolver.getAll();
- contractNamesToCompile = _.map(allContracts, contractSource =>
- path.basename(contractSource.path, constants.SOLIDITY_FILE_EXTENSION),
- );
- } else {
- return this._specifiedContracts;
- }
- return contractNamesToCompile;
- }
- /**
- * Compiles contracts, and, if `shouldPersist` is true, saves artifacts to artifactsDir.
- * @param fileName Name of contract with '.sol' extension.
- * @return an array of compiler outputs, where each element corresponds to a different version of solc-js.
- */
- private async _compileContractsAsync(contractNames: string[], shouldPersist: boolean): Promise<StandardOutput[]> {
- // batch input contracts together based on the version of the compiler that they require.
- const versionToInputs: VersionToInputs = {};
-
- // map contract paths to data about them for later verification and persistence
- const contractPathToData: ContractPathToData = {};
-
- const solcJSReleases = await getSolcJSReleasesAsync();
- const resolvedContractSources = [];
- for (const contractName of contractNames) {
- const spyResolver = new SpyResolver(this._resolver);
- const contractSource = spyResolver.resolve(contractName);
- const sourceTreeHashHex = getSourceTreeHash(spyResolver, contractSource.path).toString('hex');
- const contractData = {
- contractName: path.basename(contractName, constants.SOLIDITY_FILE_EXTENSION),
- currentArtifactIfExists: await getContractArtifactIfExistsAsync(this._artifactsDir, contractName),
- sourceTreeHashHex: `0x${sourceTreeHashHex}`,
- };
- if (!this._shouldCompile(contractData)) {
- continue;
- }
- contractPathToData[contractSource.path] = contractData;
- const solcVersion = _.isUndefined(this._solcVersionIfExists)
- ? semver.maxSatisfying(_.keys(solcJSReleases), parseSolidityVersionRange(contractSource.source))
- : this._solcVersionIfExists;
- const isFirstContractWithThisVersion = _.isUndefined(versionToInputs[solcVersion]);
- if (isFirstContractWithThisVersion) {
- versionToInputs[solcVersion] = {
- standardInput: {
- language: 'Solidity',
- sources: {},
- settings: this._compilerSettings,
- },
- contractsToCompile: [],
- };
- }
- // add input to the right version batch
- for (const resolvedContractSource of spyResolver.resolvedContractSources) {
- versionToInputs[solcVersion].standardInput.sources[resolvedContractSource.absolutePath] = {
- content: resolvedContractSource.source,
- };
- }
- resolvedContractSources.push(...spyResolver.resolvedContractSources);
- versionToInputs[solcVersion].contractsToCompile.push(contractSource.path);
- }
-
- const dependencyNameToPath = getDependencyNameToPackagePath(resolvedContractSources);
-
- const compilerOutputs: StandardOutput[] = [];
- for (const solcVersion of _.keys(versionToInputs)) {
- const input = versionToInputs[solcVersion];
- logUtils.warn(
- `Compiling ${input.contractsToCompile.length} contracts (${
- input.contractsToCompile
- }) with Solidity v${solcVersion}...`,
- );
- let compilerOutput;
- let fullSolcVersion;
- input.standardInput.settings.remappings = _.map(
- dependencyNameToPath,
- (dependencyPackagePath: string, dependencyName: string) => `${dependencyName}=${dependencyPackagePath}`,
- );
- if (this._useDockerisedSolc) {
- const dockerCommand = `docker run ethereum/solc:${solcVersion} --version`;
- const versionCommandOutput = execSync(dockerCommand).toString();
- const versionCommandOutputParts = versionCommandOutput.split(' ');
- fullSolcVersion = versionCommandOutputParts[versionCommandOutputParts.length - 1].trim();
- compilerOutput = await compileDockerAsync(solcVersion, input.standardInput);
- } else {
- fullSolcVersion = solcJSReleases[solcVersion];
- compilerOutput = await compileSolcJSAsync(solcVersion, input.standardInput);
- }
- if (!_.isUndefined(compilerOutput.errors)) {
- printCompilationErrorsAndWarnings(compilerOutput.errors);
- }
- compilerOutput.sources = makeContractPathsRelative(
- compilerOutput.sources,
- this._contractsDir,
- dependencyNameToPath,
- );
- compilerOutput.contracts = makeContractPathsRelative(
- compilerOutput.contracts,
- this._contractsDir,
- dependencyNameToPath,
- );
-
- for (const contractPath of input.contractsToCompile) {
- const contractName = contractPathToData[contractPath].contractName;
-
- const compiledContract = compilerOutput.contracts[contractPath][contractName];
- if (_.isUndefined(compiledContract)) {
- throw new Error(
- `Contract ${contractName} not found in ${contractPath}. Please make sure your contract has the same name as it's file name`,
- );
- }
-
- addHexPrefixToContractBytecode(compiledContract);
-
- if (shouldPersist) {
- await this._persistCompiledContractAsync(
- contractPath,
- contractPathToData[contractPath].currentArtifactIfExists,
- contractPathToData[contractPath].sourceTreeHashHex,
- contractName,
- fullSolcVersion,
- compilerOutput,
- );
- }
- }
-
- compilerOutputs.push(compilerOutput);
- }
-
- return compilerOutputs;
- }
- private _shouldCompile(contractData: ContractData): boolean {
- if (_.isUndefined(contractData.currentArtifactIfExists)) {
- return true;
- } else {
- const currentArtifact = contractData.currentArtifactIfExists as ContractArtifact;
- const isUserOnLatestVersion = currentArtifact.schemaVersion === constants.LATEST_ARTIFACT_VERSION;
- const didCompilerSettingsChange = !_.isEqual(
- _.omit(currentArtifact.compiler.settings, 'remappings'),
- _.omit(this._compilerSettings, 'remappings'),
- );
- const didSourceChange = currentArtifact.sourceTreeHashHex !== contractData.sourceTreeHashHex;
- return !isUserOnLatestVersion || didCompilerSettingsChange || didSourceChange;
- }
- }
- private async _persistCompiledContractAsync(
- contractPath: string,
- currentArtifactIfExists: ContractArtifact | void,
- sourceTreeHashHex: string,
- contractName: string,
- fullSolcVersion: string,
- compilerOutput: solc.StandardOutput,
- ): Promise<void> {
- const compiledContract = compilerOutput.contracts[contractPath][contractName];
-
- // need to gather sourceCodes for this artifact, but compilerOutput.sources (the list of contract modules)
- // contains listings for every contract compiled during the compiler invocation that compiled the contract
- // to be persisted, which could include many that are irrelevant to the contract at hand. So, gather up only
- // the relevant sources:
- const { sourceCodes, sources } = getSourcesWithDependencies(
- this._resolver,
- contractPath,
- compilerOutput.sources,
- );
-
- const contractVersion: ContractVersionData = {
- compilerOutput: compiledContract,
- sources,
- sourceCodes,
- sourceTreeHashHex,
- compiler: {
- name: 'solc',
- version: fullSolcVersion,
- settings: this._compilerSettings,
- },
- };
-
- let newArtifact: ContractArtifact;
- if (!_.isUndefined(currentArtifactIfExists)) {
- const currentArtifact = currentArtifactIfExists as ContractArtifact;
- newArtifact = {
- ...currentArtifact,
- ...contractVersion,
- };
- } else {
- newArtifact = {
- schemaVersion: constants.LATEST_ARTIFACT_VERSION,
- contractName,
- ...contractVersion,
- networks: {},
- };
- }
-
- const artifactString = utils.stringifyWithFormatting(newArtifact);
- const currentArtifactPath = `${this._artifactsDir}/${contractName}.json`;
- await fsWrapper.writeFileAsync(currentArtifactPath, artifactString);
- logUtils.warn(`${contractName} artifact saved!`);
- }
-}
diff --git a/packages/sol-compiler/src/globals.d.ts b/packages/sol-compiler/src/globals.d.ts
deleted file mode 100644
index 94e63a32d..000000000
--- a/packages/sol-compiler/src/globals.d.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-declare module '*.json' {
- const json: any;
- /* tslint:disable */
- export default json;
- /* tslint:enable */
-}
diff --git a/packages/sol-compiler/src/index.ts b/packages/sol-compiler/src/index.ts
deleted file mode 100644
index d8a60666f..000000000
--- a/packages/sol-compiler/src/index.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-export { Compiler } from './compiler';
-export {
- AbiDefinition,
- CompilerOptions,
- CompilerSettings,
- DataItem,
- DevdocOutput,
- ErrorSeverity,
- ErrorType,
- EventAbi,
- EventParameter,
- EvmBytecodeOutput,
- EvmOutput,
- FallbackAbi,
- FunctionAbi,
- MethodAbi,
- ConstructorAbi,
- ConstructorStateMutability,
- ContractAbi,
- OutputField,
- CompilerSettingsMetadata,
- OptimizerSettings,
- ParamDescription,
- SolcError,
- StandardContractOutput,
- StandardOutput,
- StateMutability,
- SourceLocation,
-} from 'ethereum-types';
diff --git a/packages/sol-compiler/src/schemas/compiler_options_schema.ts b/packages/sol-compiler/src/schemas/compiler_options_schema.ts
deleted file mode 100644
index c0766b625..000000000
--- a/packages/sol-compiler/src/schemas/compiler_options_schema.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-export const compilerOptionsSchema = {
- id: '/CompilerOptions',
- properties: {
- contractsDir: { type: 'string' },
- artifactsDir: { type: 'string' },
- solcVersion: { type: 'string', pattern: '^\\d+.\\d+.\\d+$' },
- compilerSettings: { type: 'object' },
- contracts: {
- oneOf: [
- {
- type: 'string',
- pattern: '^\\*$',
- },
- {
- type: 'array',
- items: {
- type: 'string',
- },
- },
- ],
- },
- useDockerisedSolc: { type: 'boolean' },
- },
- type: 'object',
- required: [],
- additionalProperties: false,
-};
diff --git a/packages/sol-compiler/src/utils/compiler.ts b/packages/sol-compiler/src/utils/compiler.ts
deleted file mode 100644
index 28049e453..000000000
--- a/packages/sol-compiler/src/utils/compiler.ts
+++ /dev/null
@@ -1,411 +0,0 @@
-import { ContractSource, Resolver } from '@0x/sol-resolver';
-import { fetchAsync, logUtils } from '@0x/utils';
-import chalk from 'chalk';
-import { execSync } from 'child_process';
-import { ContractArtifact } from 'ethereum-types';
-import * as ethUtil from 'ethereumjs-util';
-import * as _ from 'lodash';
-import * as path from 'path';
-import * as requireFromString from 'require-from-string';
-import * as solc from 'solc';
-
-import { constants } from './constants';
-import { fsWrapper } from './fs_wrapper';
-import { BinaryPaths, CompilationError } from './types';
-
-/**
- * Gets contract data on network or returns if an artifact does not exist.
- * @param artifactsDir Path to the artifacts directory.
- * @param contractName Name of contract.
- * @return Contract data on network or undefined.
- */
-export async function getContractArtifactIfExistsAsync(
- artifactsDir: string,
- contractName: string,
-): Promise<ContractArtifact | void> {
- let contractArtifact;
- const currentArtifactPath = `${artifactsDir}/${path.basename(
- contractName,
- constants.SOLIDITY_FILE_EXTENSION,
- )}.json`;
- try {
- const opts = {
- encoding: 'utf8',
- };
- const contractArtifactString = await fsWrapper.readFileAsync(currentArtifactPath, opts);
- contractArtifact = JSON.parse(contractArtifactString);
- return contractArtifact;
- } catch (err) {
- logUtils.warn(`Artifact for ${contractName} does not exist`);
- return undefined;
- }
-}
-
-/**
- * Creates a directory if it does not already exist.
- * @param artifactsDir Path to the directory.
- */
-export async function createDirIfDoesNotExistAsync(dirPath: string): Promise<void> {
- if (!fsWrapper.doesPathExistSync(dirPath)) {
- logUtils.warn(`Creating directory at ${dirPath}...`);
- await fsWrapper.mkdirpAsync(dirPath);
- }
-}
-
-/**
- * Searches Solidity source code for compiler version range.
- * @param source Source code of contract.
- * @return Solc compiler version range.
- */
-export function parseSolidityVersionRange(source: string): string {
- const SOLIDITY_VERSION_RANGE_REGEX = /pragma\s+solidity\s+(.*);/;
- const solcVersionRangeMatch = source.match(SOLIDITY_VERSION_RANGE_REGEX);
- if (_.isNull(solcVersionRangeMatch)) {
- throw new Error('Could not find Solidity version range in source');
- }
- const solcVersionRange = solcVersionRangeMatch[1];
- return solcVersionRange;
-}
-
-/**
- * Normalizes the path found in the error message. If it cannot be normalized
- * the original error message is returned.
- * Example: converts 'base/Token.sol:6:46: Warning: Unused local variable'
- * to 'Token.sol:6:46: Warning: Unused local variable'
- * This is used to prevent logging the same error multiple times.
- * @param errMsg An error message from the compiled output.
- * @return The error message with directories truncated from the contract path.
- */
-export function getNormalizedErrMsg(errMsg: string): string {
- const SOLIDITY_FILE_EXTENSION_REGEX = /(.*\.sol)/;
- const errPathMatch = errMsg.match(SOLIDITY_FILE_EXTENSION_REGEX);
- if (_.isNull(errPathMatch)) {
- // This can occur if solidity outputs a general warning, e.g
- // Warning: This is a pre-release compiler version, please do not use it in production.
- return errMsg;
- }
- const errPath = errPathMatch[0];
- const baseContract = path.basename(errPath);
- const normalizedErrMsg = errMsg.replace(errPath, baseContract);
- return normalizedErrMsg;
-}
-
-/**
- * Parses the contract source code and extracts the dendencies
- * @param source Contract source code
- * @return List of dependendencies
- */
-export function parseDependencies(contractSource: ContractSource): string[] {
- // TODO: Use a proper parser
- const source = contractSource.source;
- const IMPORT_REGEX = /(import\s)/;
- const DEPENDENCY_PATH_REGEX = /"([^"]+)"/; // Source: https://github.com/BlockChainCompany/soljitsu/blob/master/lib/shared.js
- const dependencies: string[] = [];
- const lines = source.split('\n');
- _.forEach(lines, line => {
- if (!_.isNull(line.match(IMPORT_REGEX))) {
- const dependencyMatch = line.match(DEPENDENCY_PATH_REGEX);
- if (!_.isNull(dependencyMatch)) {
- let dependencyPath = dependencyMatch[1];
- if (dependencyPath.startsWith('.')) {
- dependencyPath = path.join(path.dirname(contractSource.path), dependencyPath);
- }
- dependencies.push(dependencyPath);
- }
- }
- });
- return dependencies;
-}
-
-let solcJSReleasesCache: BinaryPaths | undefined;
-
-/**
- * Fetches the list of available solidity compilers
- */
-export async function getSolcJSReleasesAsync(): Promise<BinaryPaths> {
- if (_.isUndefined(solcJSReleasesCache)) {
- const versionList = await fetch('https://ethereum.github.io/solc-bin/bin/list.json');
- const versionListJSON = await versionList.json();
- solcJSReleasesCache = versionListJSON.releases;
- }
- return solcJSReleasesCache as BinaryPaths;
-}
-
-/**
- * Compiles the contracts and prints errors/warnings
- * @param solcVersion Version of a solc compiler
- * @param standardInput Solidity standard JSON input
- */
-export async function compileSolcJSAsync(
- solcVersion: string,
- standardInput: solc.StandardInput,
-): Promise<solc.StandardOutput> {
- const solcInstance = await getSolcJSAsync(solcVersion);
- const standardInputStr = JSON.stringify(standardInput);
- const standardOutputStr = solcInstance.compileStandardWrapper(standardInputStr);
- const compiled: solc.StandardOutput = JSON.parse(standardOutputStr);
- return compiled;
-}
-
-/**
- * Compiles the contracts and prints errors/warnings
- * @param solcVersion Version of a solc compiler
- * @param standardInput Solidity standard JSON input
- */
-export async function compileDockerAsync(
- solcVersion: string,
- standardInput: solc.StandardInput,
-): Promise<solc.StandardOutput> {
- const standardInputStr = JSON.stringify(standardInput, null, 2);
- const dockerCommand = `docker run -i -a stdin -a stdout -a stderr ethereum/solc:${solcVersion} solc --standard-json`;
- const standardOutputStr = execSync(dockerCommand, { input: standardInputStr }).toString();
- const compiled: solc.StandardOutput = JSON.parse(standardOutputStr);
- return compiled;
-}
-
-/**
- * Example "relative" paths:
- * /user/leo/0x-monorepo/contracts/extensions/contracts/extension.sol -> extension.sol
- * /user/leo/0x-monorepo/node_modules/@0x/contracts-protocol/contracts/exchange.sol -> @0x/contracts-protocol/contracts/exchange.sol
- */
-function makeContractPathRelative(
- absolutePath: string,
- contractsDir: string,
- dependencyNameToPath: { [dependencyName: string]: string },
-): string {
- let contractPath = absolutePath.replace(`${contractsDir}/`, '');
- _.map(dependencyNameToPath, (packagePath: string, dependencyName: string) => {
- contractPath = contractPath.replace(packagePath, dependencyName);
- });
- return contractPath;
-}
-
-/**
- * Makes the path relative removing all system-dependent data. Converts absolute paths to a format suitable for artifacts.
- * @param absolutePathToSmth Absolute path to contract or source
- * @param contractsDir Current package contracts directory location
- * @param dependencyNameToPath Mapping of dependency name to package path
- */
-export function makeContractPathsRelative(
- absolutePathToSmth: { [absoluteContractPath: string]: any },
- contractsDir: string,
- dependencyNameToPath: { [dependencyName: string]: string },
-): { [contractPath: string]: any } {
- return _.mapKeys(absolutePathToSmth, (_val: any, absoluteContractPath: string) =>
- makeContractPathRelative(absoluteContractPath, contractsDir, dependencyNameToPath),
- );
-}
-
-/**
- * Separates errors from warnings, formats the messages and prints them. Throws if there is any compilation error (not warning).
- * @param solcErrors The errors field of standard JSON output that contains errors and warnings.
- */
-export function printCompilationErrorsAndWarnings(solcErrors: solc.SolcError[]): void {
- const SOLIDITY_WARNING = 'warning';
- const errors = _.filter(solcErrors, entry => entry.severity !== SOLIDITY_WARNING);
- const warnings = _.filter(solcErrors, entry => entry.severity === SOLIDITY_WARNING);
- if (!_.isEmpty(errors)) {
- errors.forEach(error => {
- const normalizedErrMsg = getNormalizedErrMsg(error.formattedMessage || error.message);
- logUtils.log(chalk.red('error'), normalizedErrMsg);
- });
- throw new CompilationError(errors.length);
- } else {
- warnings.forEach(warning => {
- const normalizedWarningMsg = getNormalizedErrMsg(warning.formattedMessage || warning.message);
- logUtils.log(chalk.yellow('warning'), normalizedWarningMsg);
- });
- }
-}
-
-/**
- * Gets the source tree hash for a file and its dependencies.
- * @param fileName Name of contract file.
- */
-export function getSourceTreeHash(resolver: Resolver, importPath: string): Buffer {
- const contractSource = resolver.resolve(importPath);
- const dependencies = parseDependencies(contractSource);
- const sourceHash = ethUtil.sha3(contractSource.source);
- if (dependencies.length === 0) {
- return sourceHash;
- } else {
- const dependencySourceTreeHashes = _.map(dependencies, (dependency: string) =>
- getSourceTreeHash(resolver, dependency),
- );
- const sourceTreeHashesBuffer = Buffer.concat([sourceHash, ...dependencySourceTreeHashes]);
- const sourceTreeHash = ethUtil.sha3(sourceTreeHashesBuffer);
- return sourceTreeHash;
- }
-}
-
-/**
- * For the given @param contractPath, populates JSON objects to be used in the ContractVersionData interface's
- * properties `sources` (source code file names mapped to ID numbers) and `sourceCodes` (source code content of
- * contracts) for that contract. The source code pointed to by contractPath is read and parsed directly (via
- * `resolver.resolve().source`), as are its imports, recursively. The ID numbers for @return `sources` are
- * taken from the corresponding ID's in @param fullSources, and the content for @return sourceCodes is read from
- * disk (via the aforementioned `resolver.source`).
- */
-export function getSourcesWithDependencies(
- resolver: Resolver,
- contractPath: string,
- fullSources: { [sourceName: string]: { id: number } },
-): { sourceCodes: { [sourceName: string]: string }; sources: { [sourceName: string]: { id: number } } } {
- const sources = { [contractPath]: fullSources[contractPath] };
- const sourceCodes = { [contractPath]: resolver.resolve(contractPath).source };
- recursivelyGatherDependencySources(
- resolver,
- contractPath,
- sourceCodes[contractPath],
- fullSources,
- sources,
- sourceCodes,
- );
- return { sourceCodes, sources };
-}
-
-function recursivelyGatherDependencySources(
- resolver: Resolver,
- contractPath: string,
- contractSource: string,
- fullSources: { [sourceName: string]: { id: number } },
- sourcesToAppendTo: { [sourceName: string]: { id: number } },
- sourceCodesToAppendTo: { [sourceName: string]: string },
-): void {
- const importStatementMatches = contractSource.match(/\nimport[^;]*;/g);
- if (importStatementMatches === null) {
- return;
- }
- for (const importStatementMatch of importStatementMatches) {
- const importPathMatches = importStatementMatch.match(/\"([^\"]*)\"/);
- if (importPathMatches === null || importPathMatches.length === 0) {
- continue;
- }
-
- let importPath = importPathMatches[1];
- // HACK(albrow): We have, e.g.:
- //
- // importPath = "../../utils/LibBytes/LibBytes.sol"
- // contractPath = "2.0.0/protocol/AssetProxyOwner/AssetProxyOwner.sol"
- //
- // Resolver doesn't understand "../" so we want to pass
- // "2.0.0/utils/LibBytes/LibBytes.sol" to resolver.
- //
- // This hack involves using path.resolve. But path.resolve returns
- // absolute directories by default. We trick it into thinking that
- // contractPath is a root directory by prepending a '/' and then
- // removing the '/' the end.
- //
- // path.resolve("/a/b/c", ""../../d/e") === "/a/d/e"
- //
- const lastPathSeparatorPos = contractPath.lastIndexOf('/');
- const contractFolder = lastPathSeparatorPos === -1 ? '' : contractPath.slice(0, lastPathSeparatorPos + 1);
- if (importPath.startsWith('.')) {
- /**
- * Some imports path are relative ("../Token.sol", "./Wallet.sol")
- * while others are absolute ("Token.sol", "@0x/contracts/Wallet.sol")
- * And we need to append the base path for relative imports.
- */
- importPath = path.resolve(`/${contractFolder}`, importPath).replace('/', '');
- }
-
- if (_.isUndefined(sourcesToAppendTo[importPath])) {
- sourcesToAppendTo[importPath] = { id: fullSources[importPath].id };
- sourceCodesToAppendTo[importPath] = resolver.resolve(importPath).source;
-
- recursivelyGatherDependencySources(
- resolver,
- importPath,
- resolver.resolve(importPath).source,
- fullSources,
- sourcesToAppendTo,
- sourceCodesToAppendTo,
- );
- }
- }
-}
-
-/**
- * Gets the solidity compiler instance. If the compiler is already cached - gets it from FS,
- * otherwise - fetches it and caches it.
- * @param solcVersion The compiler version. e.g. 0.5.0
- */
-export async function getSolcJSAsync(solcVersion: string): Promise<solc.SolcInstance> {
- const solcJSReleases = await getSolcJSReleasesAsync();
- const fullSolcVersion = solcJSReleases[solcVersion];
- if (_.isUndefined(fullSolcVersion)) {
- throw new Error(`${solcVersion} is not a known compiler version`);
- }
- const compilerBinFilename = path.join(constants.SOLC_BIN_DIR, fullSolcVersion);
- let solcjs: string;
- if (await fsWrapper.doesFileExistAsync(compilerBinFilename)) {
- solcjs = (await fsWrapper.readFileAsync(compilerBinFilename)).toString();
- } else {
- logUtils.warn(`Downloading ${fullSolcVersion}...`);
- const url = `${constants.BASE_COMPILER_URL}${fullSolcVersion}`;
- const response = await fetchAsync(url);
- const SUCCESS_STATUS = 200;
- if (response.status !== SUCCESS_STATUS) {
- throw new Error(`Failed to load ${fullSolcVersion}`);
- }
- solcjs = await response.text();
- await fsWrapper.writeFileAsync(compilerBinFilename, solcjs);
- }
- if (solcjs.length === 0) {
- throw new Error('No compiler available');
- }
- const solcInstance = solc.setupMethods(requireFromString(solcjs, compilerBinFilename));
- return solcInstance;
-}
-
-/**
- * Solidity compiler emits the bytecode without a 0x prefix for a hex. This function fixes it if bytecode is present.
- * @param compiledContract The standard JSON output section for a contract. Geth modified in place.
- */
-export function addHexPrefixToContractBytecode(compiledContract: solc.StandardContractOutput): void {
- if (!_.isUndefined(compiledContract.evm)) {
- if (!_.isUndefined(compiledContract.evm.bytecode) && !_.isUndefined(compiledContract.evm.bytecode.object)) {
- compiledContract.evm.bytecode.object = ethUtil.addHexPrefix(compiledContract.evm.bytecode.object);
- }
- if (
- !_.isUndefined(compiledContract.evm.deployedBytecode) &&
- !_.isUndefined(compiledContract.evm.deployedBytecode.object)
- ) {
- compiledContract.evm.deployedBytecode.object = ethUtil.addHexPrefix(
- compiledContract.evm.deployedBytecode.object,
- );
- }
- }
-}
-
-/**
- * Takes the list of resolved contract sources from `SpyResolver` and produces a mapping from dependency name
- * to package path used in `remappings` later, as well as in generating the "relative" source paths saved to the artifact files.
- * @param contractSources The list of resolved contract sources
- */
-export function getDependencyNameToPackagePath(
- contractSources: ContractSource[],
-): { [dependencyName: string]: string } {
- const allTouchedFiles = contractSources.map(contractSource => `${contractSource.absolutePath}`);
- const NODE_MODULES = 'node_modules';
- const allTouchedDependencies = _.filter(allTouchedFiles, filePath => filePath.includes(NODE_MODULES));
- const dependencyNameToPath: { [dependencyName: string]: string } = {};
- _.map(allTouchedDependencies, dependencyFilePath => {
- const lastNodeModulesStart = dependencyFilePath.lastIndexOf(NODE_MODULES);
- const lastNodeModulesEnd = lastNodeModulesStart + NODE_MODULES.length;
- const importPath = dependencyFilePath.substr(lastNodeModulesEnd + 1);
- let packageName;
- let packageScopeIfExists;
- let dependencyName;
- if (_.startsWith(importPath, '@')) {
- [packageScopeIfExists, packageName] = importPath.split('/');
- dependencyName = `${packageScopeIfExists}/${packageName}`;
- } else {
- [packageName] = importPath.split('/');
- dependencyName = `${packageName}`;
- }
- const dependencyPackagePath = path.join(dependencyFilePath.substr(0, lastNodeModulesEnd), dependencyName);
- dependencyNameToPath[dependencyName] = dependencyPackagePath;
- });
- return dependencyNameToPath;
-}
diff --git a/packages/sol-compiler/src/utils/constants.ts b/packages/sol-compiler/src/utils/constants.ts
deleted file mode 100644
index 433897f8a..000000000
--- a/packages/sol-compiler/src/utils/constants.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import * as path from 'path';
-
-export const constants = {
- SOLIDITY_FILE_EXTENSION: '.sol',
- BASE_COMPILER_URL: 'https://ethereum.github.io/solc-bin/bin/',
- LATEST_ARTIFACT_VERSION: '2.0.0',
- SOLC_BIN_DIR: path.join(__dirname, '..', '..', 'solc_bin'),
-};
diff --git a/packages/sol-compiler/src/utils/encoder.ts b/packages/sol-compiler/src/utils/encoder.ts
deleted file mode 100644
index 40b103fd5..000000000
--- a/packages/sol-compiler/src/utils/encoder.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { AbiDefinition, AbiType, ConstructorAbi, ContractAbi, DataItem } from 'ethereum-types';
-import * as _ from 'lodash';
-import * as web3Abi from 'web3-eth-abi';
-
-export const encoder = {
- encodeConstructorArgsFromAbi(args: any[], abi: ContractAbi): string {
- const constructorTypes: string[] = [];
- _.each(abi, (element: AbiDefinition) => {
- if (element.type === AbiType.Constructor) {
- // tslint:disable-next-line:no-unnecessary-type-assertion
- const constuctorAbi = element as ConstructorAbi;
- _.each(constuctorAbi.inputs, (input: DataItem) => {
- constructorTypes.push(input.type);
- });
- }
- });
- const encodedParameters = web3Abi.encodeParameters(constructorTypes, args);
- return encodedParameters;
- },
-};
diff --git a/packages/sol-compiler/src/utils/fs_wrapper.ts b/packages/sol-compiler/src/utils/fs_wrapper.ts
deleted file mode 100644
index a52b50963..000000000
--- a/packages/sol-compiler/src/utils/fs_wrapper.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import { promisify } from '@0x/utils';
-import * as fs from 'fs';
-import * as mkdirp from 'mkdirp';
-
-export const fsWrapper = {
- readdirAsync: promisify<string[]>(fs.readdir),
- readFileAsync: promisify<string>(fs.readFile),
- writeFileAsync: promisify<undefined>(fs.writeFile),
- mkdirpAsync: promisify<undefined>(mkdirp),
- doesPathExistSync: fs.existsSync,
- rmdirSync: fs.rmdirSync,
- removeFileAsync: promisify<undefined>(fs.unlink),
- statAsync: promisify<fs.Stats>(fs.stat),
- appendFileAsync: promisify<undefined>(fs.appendFile),
- accessAsync: promisify<boolean>(fs.access),
- doesFileExistAsync: async (filePath: string): Promise<boolean> => {
- try {
- await fsWrapper.accessAsync(
- filePath,
- // node says we need to use bitwise, but tslint says no:
- fs.constants.F_OK | fs.constants.R_OK, // tslint:disable-line:no-bitwise
- );
- } catch (err) {
- return false;
- }
- return true;
- },
-};
diff --git a/packages/sol-compiler/src/utils/types.ts b/packages/sol-compiler/src/utils/types.ts
deleted file mode 100644
index f756c51bb..000000000
--- a/packages/sol-compiler/src/utils/types.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-export enum AbiType {
- Function = 'function',
- Constructor = 'constructor',
- Event = 'event',
- Fallback = 'fallback',
-}
-
-export interface SolcErrors {
- [key: string]: boolean;
-}
-
-export interface ContractSourceData {
- [contractName: string]: ContractSpecificSourceData;
-}
-
-export interface BinaryPaths {
- [key: string]: string;
-}
-
-export interface ContractSpecificSourceData {
- solcVersionRange: string;
- sourceHash: Buffer;
- sourceTreeHash: Buffer;
-}
-
-export interface Token {
- address?: string;
- name: string;
- symbol: string;
- decimals: number;
- ipfsHash: string;
- swarmHash: string;
-}
-
-export type DoneCallback = (err?: Error) => void;
-
-export class CompilationError extends Error {
- public errorsCount: number;
- public typeName = 'CompilationError';
- constructor(errorsCount: number) {
- super('Compilation errors encountered');
- this.errorsCount = errorsCount;
- }
-}
diff --git a/packages/sol-compiler/src/utils/utils.ts b/packages/sol-compiler/src/utils/utils.ts
deleted file mode 100644
index 4f2de2caa..000000000
--- a/packages/sol-compiler/src/utils/utils.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-export const utils = {
- stringifyWithFormatting(obj: any): string {
- const stringifiedObj = JSON.stringify(obj, null, '\t');
- return stringifiedObj;
- },
-};