diff options
author | Fabio Berger <me@fabioberger.com> | 2018-04-11 18:00:30 +0800 |
---|---|---|
committer | Fabio Berger <me@fabioberger.com> | 2018-04-11 18:00:30 +0800 |
commit | 29dc22e208080fa8ff0871b98b530a2deb251a73 (patch) | |
tree | db372a34e85ac8caf4a3f3ed9d7591452df9cb1a /packages/deployer | |
parent | 6f72fed8b5b37fac5096413b363b533e0a29f7b5 (diff) | |
parent | c44f9e56ada898ffd0d0e57aa228006977fb238c (diff) | |
download | dexon-sol-tools-29dc22e208080fa8ff0871b98b530a2deb251a73.tar dexon-sol-tools-29dc22e208080fa8ff0871b98b530a2deb251a73.tar.gz dexon-sol-tools-29dc22e208080fa8ff0871b98b530a2deb251a73.tar.bz2 dexon-sol-tools-29dc22e208080fa8ff0871b98b530a2deb251a73.tar.lz dexon-sol-tools-29dc22e208080fa8ff0871b98b530a2deb251a73.tar.xz dexon-sol-tools-29dc22e208080fa8ff0871b98b530a2deb251a73.tar.zst dexon-sol-tools-29dc22e208080fa8ff0871b98b530a2deb251a73.zip |
Merge branch 'development' into removeMigrateStep
* development:
Fix lint error
Fix documentation links in some READMEs
Fix relative link
Add step to publishing that upload staging doc jsons, deploys staging website, opens every docs page and asks the dev to confirm that each one renders properly before publishing
Fix web3Wrapper build command
Add top-level `yarn lerna:stage_docs` to upload docJsons to the staging S3 bucket for all packages with a docs page
Added a detailed description of `renameOverloadedMethods` (special thanks to @fabioberger). Updated Javascript styles in the Abi-Gen and Utils packages, around support for function overloading.
Updated deployer to accept a list of contract directories as input. Contract directories are namespaced to a void clashes. Also in this commit is a fix for overloading contract functions.
Refactor publish script to have it's main execution body be lean and discrete steps
# Conflicts:
# packages/contracts/package.json
# packages/deployer/package.json
Diffstat (limited to 'packages/deployer')
-rw-r--r-- | packages/deployer/README.md | 2 | ||||
-rw-r--r-- | packages/deployer/package.json | 1 | ||||
-rw-r--r-- | packages/deployer/src/cli.ts | 37 | ||||
-rw-r--r-- | packages/deployer/src/compiler.ts | 107 | ||||
-rw-r--r-- | packages/deployer/src/utils/compiler.ts | 65 | ||||
-rw-r--r-- | packages/deployer/src/utils/contract.ts | 4 | ||||
-rw-r--r-- | packages/deployer/src/utils/encoder.ts | 4 | ||||
-rw-r--r-- | packages/deployer/src/utils/types.ts | 19 | ||||
-rw-r--r-- | packages/deployer/test/compiler_test.ts | 16 | ||||
-rw-r--r-- | packages/deployer/test/compiler_utils_test.ts | 22 | ||||
-rw-r--r-- | packages/deployer/test/deployer_test.ts | 16 | ||||
-rw-r--r-- | packages/deployer/test/fixtures/contracts/main/Exchange.sol (renamed from packages/deployer/test/fixtures/contracts/Exchange.sol) | 6 | ||||
-rw-r--r-- | packages/deployer/test/fixtures/contracts/main/TokenTransferProxy.sol (renamed from packages/deployer/test/fixtures/contracts/TokenTransferProxy.sol) | 6 |
13 files changed, 225 insertions, 80 deletions
diff --git a/packages/deployer/README.md b/packages/deployer/README.md index d8b049bdf..ef0ddd59d 100644 --- a/packages/deployer/README.md +++ b/packages/deployer/README.md @@ -2,7 +2,7 @@ This repository contains a CLI tool that facilitates compiling and deployment of smart contracts. -### Read the [Documentation](0xproject.com/docs/deployer). +### Read the [Documentation](https://0xproject.com/docs/deployer). ## Installation diff --git a/packages/deployer/package.json b/packages/deployer/package.json index ad4ff30df..2c8ad4943 100644 --- a/packages/deployer/package.json +++ b/packages/deployer/package.json @@ -54,6 +54,7 @@ "@types/semver": "^5.5.0", "@types/yargs": "^11.0.0", "chai": "^4.0.1", + "chai-as-promised": "^7.1.0", "copyfiles": "^1.2.0", "dirty-chai": "^2.0.1", "mocha": "^4.0.1", diff --git a/packages/deployer/src/cli.ts b/packages/deployer/src/cli.ts index f1cf56416..7b32187c4 100644 --- a/packages/deployer/src/cli.ts +++ b/packages/deployer/src/cli.ts @@ -11,7 +11,7 @@ import * as yargs from 'yargs'; import { commands } from './commands'; import { constants } from './utils/constants'; import { consoleReporter } from './utils/error_reporter'; -import { CliOptions, CompilerOptions, DeployerOptions } from './utils/types'; +import { CliOptions, CompilerOptions, ContractDirectory, DeployerOptions } from './utils/types'; const DEFAULT_OPTIMIZER_ENABLED = false; const DEFAULT_CONTRACTS_DIR = path.resolve('src/contracts'); @@ -27,7 +27,7 @@ const DEFAULT_CONTRACTS_LIST = '*'; */ async function onCompileCommandAsync(argv: CliOptions): Promise<void> { const opts: CompilerOptions = { - contractsDir: argv.contractsDir, + contractDirs: getContractDirectoriesFromList(argv.contractDirs), networkId: argv.networkId, optimizerEnabled: argv.shouldOptimize, artifactsDir: argv.artifactsDir, @@ -45,7 +45,7 @@ async function onDeployCommandAsync(argv: CliOptions): Promise<void> { const web3Wrapper = new Web3Wrapper(provider); const networkId = await web3Wrapper.getNetworkIdAsync(); const compilerOpts: CompilerOptions = { - contractsDir: argv.contractsDir, + contractDirs: getContractDirectoriesFromList(argv.contractDirs), networkId, optimizerEnabled: argv.shouldOptimize, artifactsDir: argv.artifactsDir, @@ -69,6 +69,29 @@ async function onDeployCommandAsync(argv: CliOptions): Promise<void> { } /** * Creates a set of contracts to compile. + * @param contractDirectoriesList Comma separated list of contract directories + * @return Set of contract directories + */ +function getContractDirectoriesFromList(contractDirectoriesList: string): Set<ContractDirectory> { + const directories = new Set(); + const possiblyNamespacedDirectories = contractDirectoriesList.split(','); + _.forEach(possiblyNamespacedDirectories, namespacedDirectory => { + const directoryComponents = namespacedDirectory.split(':'); + if (directoryComponents.length === 1) { + const directory = { namespace: '', path: directoryComponents[0] }; + directories.add(directory); + } else if (directoryComponents.length === 2) { + const directory = { namespace: directoryComponents[0], path: directoryComponents[1] }; + directories.add(directory); + } else { + throw new Error(`Unable to parse contracts directory: '${namespacedDirectory}'`); + } + }); + + return directories; +} +/** + * Creates a set of contracts to compile. * @param contracts Comma separated list of contracts to compile */ function getContractsSetFromList(contracts: string): Set<string> { @@ -78,8 +101,7 @@ function getContractsSetFromList(contracts: string): Set<string> { } const contractsArray = contracts.split(','); _.forEach(contractsArray, contractName => { - const fileName = `${contractName}${constants.SOLIDITY_FILE_EXTENSION}`; - specifiedContracts.add(fileName); + specifiedContracts.add(contractName); }); return specifiedContracts; } @@ -104,10 +126,11 @@ function deployCommandBuilder(yargsInstance: any) { (() => { const identityCommandBuilder = _.identity; return yargs - .option('contracts-dir', { + .option('contract-dirs', { type: 'string', default: DEFAULT_CONTRACTS_DIR, - description: 'path of contracts directory to compile', + description: + "comma separated list of contract directories.\nTo avoid filename clashes, directories should be prefixed with a namespace as follows: 'namespace:/path/to/dir'.", }) .option('network-id', { type: 'number', diff --git a/packages/deployer/src/compiler.ts b/packages/deployer/src/compiler.ts index ba360cb57..e3ecc6c72 100644 --- a/packages/deployer/src/compiler.ts +++ b/packages/deployer/src/compiler.ts @@ -1,4 +1,4 @@ -import { ContractAbi } from '@0xproject/types'; +import { AbiType, ContractAbi, MethodAbi } from '@0xproject/types'; import { logUtils, promisify } from '@0xproject/utils'; import * as ethUtil from 'ethereumjs-util'; import * as fs from 'fs'; @@ -11,6 +11,8 @@ import solc = require('solc'); import { binPaths } from './solc/bin_paths'; import { + constructContractId, + constructUniqueSourceFileId, createDirIfDoesNotExistAsync, findImportIfExist, getContractArtifactIfExistsAsync, @@ -23,11 +25,14 @@ import { fsWrapper } from './utils/fs_wrapper'; import { CompilerOptions, ContractArtifact, + ContractDirectory, + ContractIdToSourceFileId, ContractNetworkData, ContractNetworks, - ContractSourceData, + ContractSourceDataByFileId, ContractSources, ContractSpecificSourceData, + FunctionNameToSeenCount, } from './utils/types'; import { utils } from './utils/utils'; @@ -39,20 +44,22 @@ const SOLC_BIN_DIR = path.join(__dirname, '..', '..', 'solc_bin'); * to artifact files. */ export class Compiler { - private _contractsDir: string; + private _contractDirs: Set<ContractDirectory>; private _networkId: number; private _optimizerEnabled: boolean; private _artifactsDir: string; // This get's set in the beggining of `compileAsync` function. It's not called from a constructor, but it's the only public method of that class and could as well be. private _contractSources!: ContractSources; private _specifiedContracts: Set<string> = new Set(); - private _contractSourceData: ContractSourceData = {}; + private _contractSourceDataByFileId: ContractSourceDataByFileId = {}; + /** * Recursively retrieves Solidity source code from directory. * @param dirPath Directory to search. - * @return Mapping of contract fileName to contract source. + * @param contractBaseDir Base contracts directory of search tree. + * @return Mapping of sourceFilePath to the contract source. */ - private static async _getContractSourcesAsync(dirPath: string): Promise<ContractSources> { + private static async _getContractSourcesAsync(dirPath: string, contractBaseDir: string): Promise<ContractSources> { let dirContents: string[] = []; try { dirContents = await fsWrapper.readdirAsync(dirPath); @@ -68,14 +75,18 @@ export class Compiler { encoding: 'utf8', }; const source = await fsWrapper.readFileAsync(contentPath, opts); - sources[fileName] = source; - logUtils.log(`Reading ${fileName} source...`); + if (!_.startsWith(contentPath, contractBaseDir)) { + throw new Error(`Expected content path '${contentPath}' to begin with '${contractBaseDir}'`); + } + const sourceFilePath = contentPath.slice(contractBaseDir.length); + sources[sourceFilePath] = source; + logUtils.log(`Reading ${sourceFilePath} source...`); } catch (err) { logUtils.log(`Could not find file at ${contentPath}`); } } else { try { - const nestedSources = await Compiler._getContractSourcesAsync(contentPath); + const nestedSources = await Compiler._getContractSourcesAsync(contentPath, contractBaseDir); sources = { ...sources, ...nestedSources, @@ -93,7 +104,7 @@ export class Compiler { * @return An instance of the Compiler class. */ constructor(opts: CompilerOptions) { - this._contractsDir = opts.contractsDir; + this._contractDirs = opts.contractDirs; this._networkId = opts.networkId; this._optimizerEnabled = opts.optimizerEnabled; this._artifactsDir = opts.artifactsDir; @@ -105,25 +116,49 @@ export class Compiler { public async compileAsync(): Promise<void> { await createDirIfDoesNotExistAsync(this._artifactsDir); await createDirIfDoesNotExistAsync(SOLC_BIN_DIR); - this._contractSources = await Compiler._getContractSourcesAsync(this._contractsDir); + this._contractSources = {}; + const contractIdToSourceFileId: ContractIdToSourceFileId = {}; + const contractDirs = Array.from(this._contractDirs.values()); + for (const contractDir of contractDirs) { + const sources = await Compiler._getContractSourcesAsync(contractDir.path, contractDir.path); + _.forIn(sources, (source, sourceFilePath) => { + const sourceFileId = constructUniqueSourceFileId(contractDir.namespace, sourceFilePath); + // Record the file's source and data + if (!_.isUndefined(this._contractSources[sourceFileId])) { + throw new Error(`Found duplicate source files with ID '${sourceFileId}'`); + } + this._contractSources[sourceFileId] = source; + // Create a mapping between the contract id and its source file id + const contractId = constructContractId(contractDir.namespace, sourceFilePath); + if (!_.isUndefined(contractIdToSourceFileId[contractId])) { + throw new Error(`Found duplicate contract with ID '${contractId}'`); + } + contractIdToSourceFileId[contractId] = sourceFileId; + }); + } _.forIn(this._contractSources, this._setContractSpecificSourceData.bind(this)); - const fileNames = this._specifiedContracts.has(ALL_CONTRACTS_IDENTIFIER) - ? _.keys(this._contractSources) + const specifiedContractIds = this._specifiedContracts.has(ALL_CONTRACTS_IDENTIFIER) + ? _.keys(contractIdToSourceFileId) : Array.from(this._specifiedContracts.values()); - for (const fileName of fileNames) { - await this._compileContractAsync(fileName); - } + await Promise.all( + _.map(specifiedContractIds, async contractId => + this._compileContractAsync(contractIdToSourceFileId[contractId]), + ), + ); } /** * Compiles contract and saves artifact to artifactsDir. - * @param fileName Name of contract with '.sol' extension. + * @param sourceFileId Unique ID of the source file. */ - private async _compileContractAsync(fileName: string): Promise<void> { + private async _compileContractAsync(sourceFileId: string): Promise<void> { if (_.isUndefined(this._contractSources)) { throw new Error('Contract sources not yet initialized'); } - const contractSpecificSourceData = this._contractSourceData[fileName]; - const currentArtifactIfExists = await getContractArtifactIfExistsAsync(this._artifactsDir, fileName); + if (_.isUndefined(this._contractSourceDataByFileId[sourceFileId])) { + throw new Error(`Contract source for ${sourceFileId} not yet initialized`); + } + const contractSpecificSourceData = this._contractSourceDataByFileId[sourceFileId]; + const currentArtifactIfExists = await getContractArtifactIfExistsAsync(this._artifactsDir, sourceFileId); const sourceHash = `0x${contractSpecificSourceData.sourceHash.toString('hex')}`; const sourceTreeHash = `0x${contractSpecificSourceData.sourceTreeHash.toString('hex')}`; @@ -162,16 +197,17 @@ export class Compiler { } const solcInstance = solc.setupMethods(requireFromString(solcjs, compilerBinFilename)); - logUtils.log(`Compiling ${fileName} with Solidity v${solcVersion}...`); - const source = this._contractSources[fileName]; + logUtils.log(`Compiling ${sourceFileId} with Solidity v${solcVersion}...`); + const source = this._contractSources[sourceFileId]; const input = { - [fileName]: source, + [sourceFileId]: source, }; const sourcesToCompile = { sources: input, }; + const compiled = solcInstance.compile(sourcesToCompile, Number(this._optimizerEnabled), importPath => - findImportIfExist(this._contractSources, importPath), + findImportIfExist(this._contractSources, sourceFileId, importPath), ); if (!_.isUndefined(compiled.errors)) { @@ -193,11 +229,11 @@ export class Compiler { }); } } - const contractName = path.basename(fileName, constants.SOLIDITY_FILE_EXTENSION); - const contractIdentifier = `${fileName}:${contractName}`; + const contractName = path.basename(sourceFileId, constants.SOLIDITY_FILE_EXTENSION); + const contractIdentifier = `${sourceFileId}:${contractName}`; if (_.isUndefined(compiled.contracts[contractIdentifier])) { throw new Error( - `Contract ${contractName} not found in ${fileName}. Please make sure your contract has the same name as it's file name`, + `Contract ${contractName} not found in ${sourceFileId}. Please make sure your contract has the same name as it's file name`, ); } const abi: ContractAbi = JSON.parse(compiled.contracts[contractIdentifier].interface); @@ -207,6 +243,7 @@ export class Compiler { const sourceMapRuntime = compiled.contracts[contractIdentifier].srcmapRuntime; const sources = _.keys(compiled.sources); const updated_at = Date.now(); + const contractNetworkData: ContractNetworkData = { solc_version: solcVersion, keccak256: sourceHash, @@ -243,28 +280,30 @@ export class Compiler { const artifactString = utils.stringifyWithFormatting(newArtifact); const currentArtifactPath = `${this._artifactsDir}/${contractName}.json`; await fsWrapper.writeFileAsync(currentArtifactPath, artifactString); - logUtils.log(`${fileName} artifact saved!`); + logUtils.log(`${sourceFileId} artifact saved!`); } /** * Gets contract dependendencies and keccak256 hash from source. * @param source Source code of contract. + * @param fileId FileId of the contract source file. * @return Object with contract dependencies and keccak256 hash of source. */ - private _setContractSpecificSourceData(source: string, fileName: string): void { - if (!_.isUndefined(this._contractSourceData[fileName])) { + private _setContractSpecificSourceData(source: string, fileId: string): void { + if (!_.isUndefined(this._contractSourceDataByFileId[fileId])) { return; } const sourceHash = ethUtil.sha3(source); const solcVersionRange = parseSolidityVersionRange(source); - const dependencies = parseDependencies(source); - const sourceTreeHash = this._getSourceTreeHash(fileName, sourceHash, dependencies); - this._contractSourceData[fileName] = { + const dependencies = parseDependencies(source, fileId); + const sourceTreeHash = this._getSourceTreeHash(fileId, sourceHash, dependencies); + this._contractSourceDataByFileId[fileId] = { dependencies, solcVersionRange, sourceHash, sourceTreeHash, }; } + /** * Gets the source tree hash for a file and its dependencies. * @param fileName Name of contract file. @@ -276,7 +315,7 @@ export class Compiler { const dependencySourceTreeHashes = _.map(dependencies, dependency => { const source = this._contractSources[dependency]; this._setContractSpecificSourceData(source, dependency); - const sourceData = this._contractSourceData[dependency]; + const sourceData = this._contractSourceDataByFileId[dependency]; return this._getSourceTreeHash(dependency, sourceData.sourceHash, sourceData.dependencies); }); const sourceTreeHashesBuffer = Buffer.concat([sourceHash, ...dependencySourceTreeHashes]); diff --git a/packages/deployer/src/utils/compiler.ts b/packages/deployer/src/utils/compiler.ts index d5137d394..600495693 100644 --- a/packages/deployer/src/utils/compiler.ts +++ b/packages/deployer/src/utils/compiler.ts @@ -1,3 +1,4 @@ +import { AbiType, ContractAbi, MethodAbi } from '@0xproject/types'; import { logUtils } from '@0xproject/utils'; import * as _ from 'lodash'; import * as path from 'path'; @@ -5,9 +6,49 @@ import * as solc from 'solc'; import { constants } from './constants'; import { fsWrapper } from './fs_wrapper'; -import { ContractArtifact, ContractSources } from './types'; +import { ContractArtifact, ContractSources, FunctionNameToSeenCount } from './types'; /** + * Constructs a system-wide unique identifier for a source file. + * @param directoryNamespace Namespace of the source file's root contract directory. + * @param sourceFilePath Path to a source file, relative to contractBaseDir. + * @return sourceFileId A system-wide unique identifier for the source file. + */ +export function constructUniqueSourceFileId(directoryNamespace: string, sourceFilePath: string): string { + const namespacePrefix = !_.isEmpty(directoryNamespace) ? `/${directoryNamespace}` : ''; + const sourceFilePathNoLeadingSlash = sourceFilePath.replace(/^\/+/g, ''); + const sourceFileId = `${namespacePrefix}/${sourceFilePathNoLeadingSlash}`; + return sourceFileId; +} +/** + * Constructs a system-wide unique identifier for a dependency file. + * @param dependencyFilePath Path from a sourceFile to a dependency. + * @param contractBaseDir Base contracts directory of search tree. + * @return sourceFileId A system-wide unique identifier for the source file. + */ +export function constructDependencyFileId(dependencyFilePath: string, sourceFilePath: string): string { + if (_.startsWith(dependencyFilePath, '/')) { + // Path of the form /namespace/path/to/dependency.sol + return dependencyFilePath; + } else { + // Dependency is relative to the source file: ./dependency.sol, ../../some/path/dependency.sol, etc. + // Join the two paths to construct a valid source file id: /namespace/path/to/dependency.sol + return path.join(path.dirname(sourceFilePath), dependencyFilePath); + } +} +/** + * Constructs a system-wide unique identifier for a contract. + * @param directoryNamespace Namespace of the source file's root contract directory. + * @param sourceFilePath Path to a source file, relative to contractBaseDir. + * @return sourceFileId A system-wide unique identifier for contract. + */ +export function constructContractId(directoryNamespace: string, sourceFilePath: string): string { + const namespacePrefix = !_.isEmpty(directoryNamespace) ? `${directoryNamespace}:` : ''; + const sourceFileName = path.basename(sourceFilePath, constants.SOLIDITY_FILE_EXTENSION); + const contractId = `${namespacePrefix}${sourceFileName}`; + return contractId; +} +/** * Gets contract data on network or returns if an artifact does not exist. * @param artifactsDir Path to the artifacts directory. * @param fileName Name of contract file. @@ -82,9 +123,10 @@ export function getNormalizedErrMsg(errMsg: string): string { /** * Parses the contract source code and extracts the dendencies * @param source Contract source code + * @param sourceFilePath File path of the source code. * @return List of dependendencies */ -export function parseDependencies(source: string): string[] { +export function parseDependencies(source: string, sourceFileId: string): string[] { // TODO: Use a proper parser const IMPORT_REGEX = /(import\s)/; const DEPENDENCY_PATH_REGEX = /"([^"]+)"/; // Source: https://github.com/BlockChainCompany/soljitsu/blob/master/lib/shared.js @@ -95,8 +137,8 @@ export function parseDependencies(source: string): string[] { const dependencyMatch = line.match(DEPENDENCY_PATH_REGEX); if (!_.isNull(dependencyMatch)) { const dependencyPath = dependencyMatch[1]; - const basenName = path.basename(dependencyPath); - dependencies.push(basenName); + const dependencyId = constructDependencyFileId(dependencyPath, sourceFileId); + dependencies.push(dependencyId); } } }); @@ -107,14 +149,19 @@ export function parseDependencies(source: string): string[] { * Callback to resolve dependencies with `solc.compile`. * Throws error if contractSources not yet initialized. * @param contractSources Source codes of contracts. - * @param importPath Path to an imported dependency. + * @param sourceFileId ID of the source file. + * @param importPath Path of dependency source file. * @return Import contents object containing source code of dependency. */ -export function findImportIfExist(contractSources: ContractSources, importPath: string): solc.ImportContents { - const fileName = path.basename(importPath); - const source = contractSources[fileName]; +export function findImportIfExist( + contractSources: ContractSources, + sourceFileId: string, + importPath: string, +): solc.ImportContents { + const dependencyFileId = constructDependencyFileId(importPath, sourceFileId); + const source = contractSources[dependencyFileId]; if (_.isUndefined(source)) { - throw new Error(`Contract source not found for ${fileName}`); + throw new Error(`Contract source not found for ${dependencyFileId}`); } const importContents: solc.ImportContents = { contents: source, diff --git a/packages/deployer/src/utils/contract.ts b/packages/deployer/src/utils/contract.ts index 9b7baac11..e8dd5218a 100644 --- a/packages/deployer/src/utils/contract.ts +++ b/packages/deployer/src/utils/contract.ts @@ -1,11 +1,9 @@ import { schemas, SchemaValidator } from '@0xproject/json-schemas'; -import { ContractAbi, EventAbi, FunctionAbi, MethodAbi, TxData } from '@0xproject/types'; +import { AbiType, ContractAbi, EventAbi, FunctionAbi, MethodAbi, TxData } from '@0xproject/types'; import { promisify } from '@0xproject/utils'; import * as _ from 'lodash'; import * as Web3 from 'web3'; -import { AbiType } from './types'; - export class Contract implements Web3.ContractInstance { public address: string; public abi: ContractAbi; diff --git a/packages/deployer/src/utils/encoder.ts b/packages/deployer/src/utils/encoder.ts index 4f62662e1..806efbbca 100644 --- a/packages/deployer/src/utils/encoder.ts +++ b/packages/deployer/src/utils/encoder.ts @@ -1,9 +1,7 @@ -import { AbiDefinition, ContractAbi, DataItem } from '@0xproject/types'; +import { AbiDefinition, AbiType, ContractAbi, DataItem } from '@0xproject/types'; import * as _ from 'lodash'; import * as web3Abi from 'web3-eth-abi'; -import { AbiType } from './types'; - export const encoder = { encodeConstructorArgsFromAbi(args: any[], abi: ContractAbi): string { const constructorTypes: string[] = []; diff --git a/packages/deployer/src/utils/types.ts b/packages/deployer/src/utils/types.ts index 7d131f5ce..1a866b873 100644 --- a/packages/deployer/src/utils/types.ts +++ b/packages/deployer/src/utils/types.ts @@ -18,6 +18,11 @@ export interface ContractNetworks { [key: number]: ContractNetworkData; } +export interface ContractDirectory { + path: string; + namespace: string; +} + export interface ContractNetworkData { solc_version: string; optimizer_enabled: boolean; @@ -40,7 +45,7 @@ export interface SolcErrors { export interface CliOptions extends yargs.Arguments { artifactsDir: string; - contractsDir: string; + contractDirs: string; jsonrpcUrl: string; networkId: number; shouldOptimize: boolean; @@ -51,7 +56,7 @@ export interface CliOptions extends yargs.Arguments { } export interface CompilerOptions { - contractsDir: string; + contractDirs: Set<ContractDirectory>; networkId: number; optimizerEnabled: boolean; artifactsDir: string; @@ -78,7 +83,11 @@ export interface ContractSources { [key: string]: string; } -export interface ContractSourceData { +export interface ContractIdToSourceFileId { + [key: string]: string; +} + +export interface ContractSourceDataByFileId { [key: string]: ContractSpecificSourceData; } @@ -98,4 +107,8 @@ export interface Token { swarmHash: string; } +export interface FunctionNameToSeenCount { + [key: string]: number; +} + export type DoneCallback = (err?: Error) => void; diff --git a/packages/deployer/test/compiler_test.ts b/packages/deployer/test/compiler_test.ts index b03ae7935..817a3b3f9 100644 --- a/packages/deployer/test/compiler_test.ts +++ b/packages/deployer/test/compiler_test.ts @@ -3,7 +3,13 @@ import 'mocha'; import { Compiler } from '../src/compiler'; import { fsWrapper } from '../src/utils/fs_wrapper'; -import { CompilerOptions, ContractArtifact, ContractNetworkData, DoneCallback } from '../src/utils/types'; +import { + CompilerOptions, + ContractArtifact, + ContractDirectory, + ContractNetworkData, + DoneCallback, +} from '../src/utils/types'; import { exchange_binary } from './fixtures/exchange_bin'; import { constants } from './util/constants'; @@ -13,11 +19,15 @@ const expect = chai.expect; describe('#Compiler', function() { this.timeout(constants.timeoutMs); const artifactsDir = `${__dirname}/fixtures/artifacts`; - const contractsDir = `${__dirname}/fixtures/contracts`; + const mainContractDir: ContractDirectory = { path: `${__dirname}/fixtures/contracts/main`, namespace: 'main' }; + const baseContractDir: ContractDirectory = { path: `${__dirname}/fixtures/contracts/base`, namespace: 'base' }; + const contractDirs: Set<ContractDirectory> = new Set(); + contractDirs.add(mainContractDir); + contractDirs.add(baseContractDir); const exchangeArtifactPath = `${artifactsDir}/Exchange.json`; const compilerOpts: CompilerOptions = { artifactsDir, - contractsDir, + contractDirs, networkId: constants.networkId, optimizerEnabled: constants.optimizerEnabled, specifiedContracts: new Set(constants.specifiedContracts), diff --git a/packages/deployer/test/compiler_utils_test.ts b/packages/deployer/test/compiler_utils_test.ts index 246304858..5377d3308 100644 --- a/packages/deployer/test/compiler_utils_test.ts +++ b/packages/deployer/test/compiler_utils_test.ts @@ -47,28 +47,34 @@ describe('Compiler utils', () => { }); describe('#parseDependencies', () => { it('correctly parses Exchange dependencies', async () => { - const exchangeSource = await fsWrapper.readFileAsync(`${__dirname}/fixtures/contracts/Exchange.sol`, { + const exchangeSource = await fsWrapper.readFileAsync(`${__dirname}/fixtures/contracts/main/Exchange.sol`, { encoding: 'utf8', }); - expect(parseDependencies(exchangeSource)).to.be.deep.equal([ - 'TokenTransferProxy.sol', - 'Token.sol', - 'SafeMath.sol', + const sourceFileId = '/main/Exchange.sol'; + expect(parseDependencies(exchangeSource, sourceFileId)).to.be.deep.equal([ + '/main/TokenTransferProxy.sol', + '/base/Token.sol', + '/base/SafeMath.sol', ]); }); it('correctly parses TokenTransferProxy dependencies', async () => { const exchangeSource = await fsWrapper.readFileAsync( - `${__dirname}/fixtures/contracts/TokenTransferProxy.sol`, + `${__dirname}/fixtures/contracts/main/TokenTransferProxy.sol`, { encoding: 'utf8', }, ); - expect(parseDependencies(exchangeSource)).to.be.deep.equal(['Token.sol', 'Ownable.sol']); + const sourceFileId = '/main/TokenTransferProxy.sol'; + expect(parseDependencies(exchangeSource, sourceFileId)).to.be.deep.equal([ + '/base/Token.sol', + '/base/Ownable.sol', + ]); }); // TODO: For now that doesn't work. This will work after we switch to a grammar-based parser it.skip('correctly parses commented out dependencies', async () => { const contractWithCommentedOutDependencies = `// import "./TokenTransferProxy.sol";`; - expect(parseDependencies(contractWithCommentedOutDependencies)).to.be.deep.equal([]); + const sourceFileId = '/main/TokenTransferProxy.sol'; + expect(parseDependencies(contractWithCommentedOutDependencies, sourceFileId)).to.be.deep.equal([]); }); }); }); diff --git a/packages/deployer/test/deployer_test.ts b/packages/deployer/test/deployer_test.ts index 238624220..a213932f9 100644 --- a/packages/deployer/test/deployer_test.ts +++ b/packages/deployer/test/deployer_test.ts @@ -4,7 +4,13 @@ import 'mocha'; import { Compiler } from '../src/compiler'; import { Deployer } from '../src/deployer'; import { fsWrapper } from '../src/utils/fs_wrapper'; -import { CompilerOptions, ContractArtifact, ContractNetworkData, DoneCallback } from '../src/utils/types'; +import { + CompilerOptions, + ContractArtifact, + ContractDirectory, + ContractNetworkData, + DoneCallback, +} from '../src/utils/types'; import { constructor_args, exchange_binary } from './fixtures/exchange_bin'; import { constants } from './util/constants'; @@ -14,11 +20,15 @@ const expect = chai.expect; describe('#Deployer', () => { const artifactsDir = `${__dirname}/fixtures/artifacts`; - const contractsDir = `${__dirname}/fixtures/contracts`; const exchangeArtifactPath = `${artifactsDir}/Exchange.json`; + const mainContractDir: ContractDirectory = { path: `${__dirname}/fixtures/contracts/main`, namespace: '' }; + const baseContractDir: ContractDirectory = { path: `${__dirname}/fixtures/contracts/base`, namespace: 'base' }; + const contractDirs: Set<ContractDirectory> = new Set(); + contractDirs.add(mainContractDir); + contractDirs.add(baseContractDir); const compilerOpts: CompilerOptions = { artifactsDir, - contractsDir, + contractDirs, networkId: constants.networkId, optimizerEnabled: constants.optimizerEnabled, specifiedContracts: new Set(constants.specifiedContracts), diff --git a/packages/deployer/test/fixtures/contracts/Exchange.sol b/packages/deployer/test/fixtures/contracts/main/Exchange.sol index 1b6819700..ea9ca3afa 100644 --- a/packages/deployer/test/fixtures/contracts/Exchange.sol +++ b/packages/deployer/test/fixtures/contracts/main/Exchange.sol @@ -1,6 +1,6 @@ /* - Copyright 2017 ZeroEx Intl. + Copyright 2018 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -19,8 +19,8 @@ pragma solidity 0.4.14; import "./TokenTransferProxy.sol"; -import "./base/Token.sol"; -import "./base/SafeMath.sol"; +import "/base/Token.sol"; +import "/base/SafeMath.sol"; /// @title Exchange - Facilitates exchange of ERC20 tokens. /// @author Amir Bandeali - <amir@0xProject.com>, Will Warren - <will@0xProject.com> diff --git a/packages/deployer/test/fixtures/contracts/TokenTransferProxy.sol b/packages/deployer/test/fixtures/contracts/main/TokenTransferProxy.sol index 90c8e7d66..99d16cb57 100644 --- a/packages/deployer/test/fixtures/contracts/TokenTransferProxy.sol +++ b/packages/deployer/test/fixtures/contracts/main/TokenTransferProxy.sol @@ -1,6 +1,6 @@ /* - Copyright 2017 ZeroEx Intl. + Copyright 2018 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -18,8 +18,8 @@ pragma solidity 0.4.14; -import "./base/Token.sol"; -import "./base/Ownable.sol"; +import "/base/Token.sol"; +import "/base/Ownable.sol"; /// @title TokenTransferProxy - Transfers tokens on behalf of contracts that have been approved via decentralized governance. /// @author Amir Bandeali - <amir@0xProject.com>, Will Warren - <will@0xProject.com> |