diff options
author | Hsuan Lee <boczeratul@gmail.com> | 2019-03-06 17:46:50 +0800 |
---|---|---|
committer | Hsuan Lee <boczeratul@gmail.com> | 2019-03-06 17:46:50 +0800 |
commit | 35703539d0f2b4ddb3b11d0de8c9634af59ab71f (patch) | |
tree | ae3731221dbbb3a6fa40060a8d916cfd3f738289 /packages/sol-compiler/src | |
parent | 92a1fde5b1ecd81b07cdb5bf0c9c1cd3544799db (diff) | |
download | dexon-0x-contracts-35703539d0f2b4ddb3b11d0de8c9634af59ab71f.tar dexon-0x-contracts-35703539d0f2b4ddb3b11d0de8c9634af59ab71f.tar.gz dexon-0x-contracts-35703539d0f2b4ddb3b11d0de8c9634af59ab71f.tar.bz2 dexon-0x-contracts-35703539d0f2b4ddb3b11d0de8c9634af59ab71f.tar.lz dexon-0x-contracts-35703539d0f2b4ddb3b11d0de8c9634af59ab71f.tar.xz dexon-0x-contracts-35703539d0f2b4ddb3b11d0de8c9634af59ab71f.tar.zst dexon-0x-contracts-35703539d0f2b4ddb3b11d0de8c9634af59ab71f.zip |
Deploy @dexon-foundation/0x.jsstable
Diffstat (limited to 'packages/sol-compiler/src')
-rw-r--r-- | packages/sol-compiler/src/cli.ts | 52 | ||||
-rw-r--r-- | packages/sol-compiler/src/compiler.ts | 389 | ||||
-rw-r--r-- | packages/sol-compiler/src/globals.d.ts | 6 | ||||
-rw-r--r-- | packages/sol-compiler/src/index.ts | 29 | ||||
-rw-r--r-- | packages/sol-compiler/src/schemas/compiler_options_schema.ts | 27 | ||||
-rw-r--r-- | packages/sol-compiler/src/utils/compiler.ts | 411 | ||||
-rw-r--r-- | packages/sol-compiler/src/utils/constants.ts | 8 | ||||
-rw-r--r-- | packages/sol-compiler/src/utils/encoder.ts | 20 | ||||
-rw-r--r-- | packages/sol-compiler/src/utils/fs_wrapper.ts | 28 | ||||
-rw-r--r-- | packages/sol-compiler/src/utils/types.ts | 44 | ||||
-rw-r--r-- | packages/sol-compiler/src/utils/utils.ts | 6 |
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; - }, -}; |