aboutsummaryrefslogtreecommitdiffstats
path: root/packages/deployer/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/deployer/src')
-rw-r--r--packages/deployer/src/cli.ts100
-rw-r--r--packages/deployer/src/compiler.ts130
-rw-r--r--packages/deployer/src/deployer.ts54
-rw-r--r--packages/deployer/src/index.ts1
-rw-r--r--packages/deployer/src/utils/constants.ts1
-rw-r--r--packages/deployer/src/utils/types.ts53
6 files changed, 169 insertions, 170 deletions
diff --git a/packages/deployer/src/cli.ts b/packages/deployer/src/cli.ts
index 7f1ae708a..8c89cf382 100644
--- a/packages/deployer/src/cli.ts
+++ b/packages/deployer/src/cli.ts
@@ -13,13 +13,13 @@ import { constants } from './utils/constants';
import { consoleReporter } from './utils/error_reporter';
import { CliOptions, CompilerOptions, DeployerOptions } from './utils/types';
-const DEFAULT_OPTIMIZER_ENABLED = false;
const DEFAULT_CONTRACTS_DIR = path.resolve('src/contracts');
const DEFAULT_ARTIFACTS_DIR = path.resolve('src/artifacts');
const DEFAULT_NETWORK_ID = 50;
const DEFAULT_JSONRPC_URL = 'http://localhost:8545';
const DEFAULT_GAS_PRICE = (10 ** 9 * 2).toString();
const DEFAULT_CONTRACTS_LIST = '*';
+const SEPARATOR = ',';
/**
* Compiles all contracts with options passed in through CLI.
@@ -28,10 +28,8 @@ const DEFAULT_CONTRACTS_LIST = '*';
async function onCompileCommandAsync(argv: CliOptions): Promise<void> {
const opts: CompilerOptions = {
contractsDir: argv.contractsDir,
- networkId: argv.networkId,
- optimizerEnabled: argv.shouldOptimize,
artifactsDir: argv.artifactsDir,
- specifiedContracts: getContractsSetFromList(argv.contracts),
+ contracts: argv.contracts === DEFAULT_CONTRACTS_LIST ? DEFAULT_CONTRACTS_LIST : argv.contracts.split(SEPARATOR),
};
await commands.compileAsync(opts);
}
@@ -46,10 +44,8 @@ async function onDeployCommandAsync(argv: CliOptions): Promise<void> {
const networkId = await web3Wrapper.getNetworkIdAsync();
const compilerOpts: CompilerOptions = {
contractsDir: argv.contractsDir,
- networkId,
- optimizerEnabled: argv.shouldOptimize,
artifactsDir: argv.artifactsDir,
- specifiedContracts: getContractsSetFromList(argv.contracts),
+ contracts: argv.contracts === DEFAULT_CONTRACTS_LIST ? DEFAULT_CONTRACTS_LIST : argv.contracts.split(SEPARATOR),
};
await commands.compileAsync(compilerOpts);
@@ -58,91 +54,79 @@ async function onDeployCommandAsync(argv: CliOptions): Promise<void> {
from: argv.account,
};
const deployerOpts: DeployerOptions = {
- artifactsDir: argv.artifactsDir,
+ artifactsDir: argv.artifactsDir || DEFAULT_ARTIFACTS_DIR,
jsonrpcUrl: argv.jsonrpcUrl,
networkId,
defaults,
};
- const deployerArgsString = argv.args as string;
- const deployerArgs = deployerArgsString.split(',');
+ const deployerArgsString = argv.constructorArgs as string;
+ const deployerArgs = deployerArgsString.split(SEPARATOR);
await commands.deployAsync(argv.contract as string, deployerArgs, deployerOpts);
}
/**
- * Creates a set of contracts to compile.
- * @param contracts Comma separated list of contracts to compile
- */
-function getContractsSetFromList(contracts: string): Set<string> {
- const specifiedContracts = new Set();
- if (contracts === '*') {
- return new Set(['*']);
- }
- const contractsArray = contracts.split(',');
- _.forEach(contractsArray, contractName => {
- specifiedContracts.add(contractName);
- });
- return specifiedContracts;
-}
-/**
- * Provides extra required options for deploy command.
+ * Adds additional required options for when the user is calling the deploy command.
* @param yargsInstance yargs instance provided in builder function callback.
*/
function deployCommandBuilder(yargsInstance: any) {
return yargsInstance
- .option('contract', {
- type: 'string',
- description: 'name of contract to deploy, exluding .sol extension',
- })
- .option('args', {
- type: 'string',
- description: 'comma separated list of constructor args to deploy contract with',
- })
- .demandOption(['contract', 'args'])
- .help().argv;
-}
-
-(() => {
- const identityCommandBuilder = _.identity;
- return yargs
- .option('contracts-dir', {
- type: 'string',
- default: DEFAULT_CONTRACTS_DIR,
- description: 'path of contracts directory to compile',
- })
.option('network-id', {
type: 'number',
default: DEFAULT_NETWORK_ID,
description: 'mainnet=1, kovan=42, testrpc=50',
})
- .option('should-optimize', {
- type: 'boolean',
- default: DEFAULT_OPTIMIZER_ENABLED,
- description: 'enable optimizer',
+ .option('contract', {
+ type: 'string',
+ description: 'name of contract to deploy, excluding .sol extension',
})
- .option('artifacts-dir', {
+ .option('constructor-args', {
type: 'string',
- default: DEFAULT_ARTIFACTS_DIR,
- description: 'path to write contracts artifacts to',
+ description: 'comma separated list of constructor args to deploy contract with',
})
.option('jsonrpc-url', {
type: 'string',
default: DEFAULT_JSONRPC_URL,
description: 'url of JSON RPC',
})
+ .option('account', {
+ type: 'string',
+ description: 'account to use for deploying contracts',
+ })
.option('gas-price', {
type: 'string',
default: DEFAULT_GAS_PRICE,
description: 'gasPrice to be used for transactions',
})
- .option('account', {
- type: 'string',
- description: 'account to use for deploying contracts',
- })
+ .demandOption(['contract', 'args', 'account'])
+ .help().argv;
+}
+
+/**
+ * Adds additional required options for when the user is calling the compile command.
+ * @param yargsInstance yargs instance provided in builder function callback.
+ */
+function compileCommandBuilder(yargsInstance: any) {
+ return yargsInstance
.option('contracts', {
type: 'string',
default: DEFAULT_CONTRACTS_LIST,
description: 'comma separated list of contracts to compile',
})
- .command('compile', 'compile contracts', identityCommandBuilder, consoleReporter(onCompileCommandAsync))
+ .help().argv;
+}
+
+(() => {
+ const identityCommandBuilder = _.identity;
+ return 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',
+ })
+ .demandCommand(1)
+ .command('compile', 'compile contracts', compileCommandBuilder, consoleReporter(onCompileCommandAsync))
.command(
'deploy',
'deploy a single contract with provided arguments',
diff --git a/packages/deployer/src/compiler.ts b/packages/deployer/src/compiler.ts
index a3c8004ec..e81df3c0a 100644
--- a/packages/deployer/src/compiler.ts
+++ b/packages/deployer/src/compiler.ts
@@ -6,6 +6,7 @@ import {
FSResolver,
NameResolver,
NPMResolver,
+ RelativeFSResolver,
Resolver,
URLResolver,
} from '@0xproject/sol-resolver';
@@ -38,11 +39,30 @@ import {
ContractNetworks,
ContractSourceData,
ContractSpecificSourceData,
+ ContractVersionData,
} from './utils/types';
import { utils } from './utils/utils';
+type TYPE_ALL_FILES_IDENTIFIER = '*';
const ALL_CONTRACTS_IDENTIFIER = '*';
+const ALL_FILES_IDENTIFIER = '*';
const SOLC_BIN_DIR = path.join(__dirname, '..', '..', 'solc_bin');
+const DEFAULT_CONTRACTS_DIR = path.resolve('contracts');
+const DEFAULT_ARTIFACTS_DIR = path.resolve('artifacts');
+// 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';
/**
* The Compiler facilitates compiling Solidity smart contracts and saves the results
@@ -52,26 +72,28 @@ export class Compiler {
private _resolver: Resolver;
private _nameResolver: NameResolver;
private _contractsDir: string;
- private _networkId: number;
- private _optimizerEnabled: boolean;
+ private _compilerSettings: solc.CompilerSettings;
private _artifactsDir: string;
- private _specifiedContracts: Set<string> = new Set();
+ private _specifiedContracts: string[] | TYPE_ALL_FILES_IDENTIFIER;
/**
* Instantiates a new instance of the Compiler class.
- * @param opts Options specifying directories, network, and optimization settings.
* @return An instance of the Compiler class.
*/
constructor(opts: CompilerOptions) {
- this._contractsDir = opts.contractsDir;
- this._networkId = opts.networkId;
- this._optimizerEnabled = opts.optimizerEnabled;
- this._artifactsDir = opts.artifactsDir;
- this._specifiedContracts = opts.specifiedContracts;
+ // 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())
+ : {};
+ this._contractsDir = opts.contractsDir || config.contractsDir || DEFAULT_CONTRACTS_DIR;
+ this._compilerSettings = opts.compilerSettings || config.compilerSettings || DEFAULT_COMPILER_SETTINGS;
+ this._artifactsDir = opts.artifactsDir || config.artifactsDir || DEFAULT_ARTIFACTS_DIR;
+ this._specifiedContracts = opts.contracts || config.contracts || ALL_CONTRACTS_IDENTIFIER;
this._nameResolver = new NameResolver(path.resolve(this._contractsDir));
const resolver = new FallthroughResolver();
resolver.appendResolver(new URLResolver());
const packagePath = path.resolve('');
resolver.appendResolver(new NPMResolver(packagePath));
+ resolver.appendResolver(new RelativeFSResolver(this._contractsDir));
resolver.appendResolver(new FSResolver());
resolver.appendResolver(this._nameResolver);
this._resolver = resolver;
@@ -83,13 +105,13 @@ export class Compiler {
await createDirIfDoesNotExistAsync(this._artifactsDir);
await createDirIfDoesNotExistAsync(SOLC_BIN_DIR);
let contractNamesToCompile: string[] = [];
- if (this._specifiedContracts.has(ALL_CONTRACTS_IDENTIFIER)) {
+ if (this._specifiedContracts === ALL_CONTRACTS_IDENTIFIER) {
const allContracts = this._nameResolver.getAll();
contractNamesToCompile = _.map(allContracts, contractSource =>
path.basename(contractSource.path, constants.SOLIDITY_FILE_EXTENSION),
);
} else {
- contractNamesToCompile = Array.from(this._specifiedContracts.values());
+ contractNamesToCompile = this._specifiedContracts;
}
for (const contractNameToCompile of contractNamesToCompile) {
await this._compileContractAsync(contractNameToCompile);
@@ -101,17 +123,18 @@ export class Compiler {
*/
private async _compileContractAsync(contractName: string): Promise<void> {
const contractSource = this._resolver.resolve(contractName);
+ const absoluteContractPath = path.join(this._contractsDir, contractSource.path);
const currentArtifactIfExists = await getContractArtifactIfExistsAsync(this._artifactsDir, contractName);
- const sourceTreeHashHex = `0x${this._getSourceTreeHash(contractSource.path).toString('hex')}`;
-
+ const sourceTreeHashHex = `0x${this._getSourceTreeHash(absoluteContractPath).toString('hex')}`;
let shouldCompile = false;
if (_.isUndefined(currentArtifactIfExists)) {
shouldCompile = true;
} else {
const currentArtifact = currentArtifactIfExists as ContractArtifact;
- shouldCompile =
- currentArtifact.networks[this._networkId].optimizer_enabled !== this._optimizerEnabled ||
- currentArtifact.networks[this._networkId].source_tree_hash !== sourceTreeHashHex;
+ const isUserOnLatestVersion = currentArtifact.schemaVersion === constants.LATEST_ARTIFACT_VERSION;
+ const didCompilerSettingsChange = !_.isEqual(currentArtifact.compiler.settings, this._compilerSettings);
+ const didSourceChange = currentArtifact.sourceTreeHashHex !== sourceTreeHashHex;
+ shouldCompile = isUserOnLatestVersion || didCompilerSettingsChange || didSourceChange;
}
if (!shouldCompile) {
return;
@@ -139,30 +162,14 @@ export class Compiler {
logUtils.log(`Compiling ${contractName} with Solidity v${solcVersion}...`);
const source = contractSource.source;
- const absoluteFilePath = contractSource.path;
const standardInput: solc.StandardInput = {
language: 'Solidity',
sources: {
- [absoluteFilePath]: {
- urls: [`file://${absoluteFilePath}`],
- },
- },
- settings: {
- optimizer: {
- enabled: this._optimizerEnabled,
- },
- outputSelection: {
- '*': {
- '*': [
- 'abi',
- 'evm.bytecode.object',
- 'evm.bytecode.sourceMap',
- 'evm.deployedBytecode.object',
- 'evm.deployedBytecode.sourceMap',
- ],
- },
+ [contractSource.path]: {
+ content: contractSource.source,
},
},
+ settings: this._compilerSettings,
};
const compiled: solc.StandardOutput = JSON.parse(
solcInstance.compileStandardWrapper(JSON.stringify(standardInput), importPath => {
@@ -188,34 +195,28 @@ export class Compiler {
});
}
}
- const compiledData = compiled.contracts[absoluteFilePath][contractName];
+ const compiledData = compiled.contracts[contractSource.path][contractName];
if (_.isUndefined(compiledData)) {
throw new Error(
- `Contract ${contractName} not found in ${absoluteFilePath}. Please make sure your contract has the same name as it's file name`,
+ `Contract ${contractName} not found in ${
+ contractSource.path
+ }. Please make sure your contract has the same name as it's file name`,
);
}
- const abi: ContractAbi = compiledData.abi;
- const bytecode = `0x${compiledData.evm.bytecode.object}`;
- const runtimeBytecode = `0x${compiledData.evm.deployedBytecode.object}`;
- const sourceMap = compiledData.evm.bytecode.sourceMap;
- const sourceMapRuntime = compiledData.evm.deployedBytecode.sourceMap;
- const unresolvedSourcePaths = _.keys(compiled.sources);
- const sources = _.map(
- unresolvedSourcePaths,
- unresolvedSourcePath => this._resolver.resolve(unresolvedSourcePath).path,
+ const sourceCodes = _.mapValues(
+ compiled.sources,
+ (_1, sourceFilePath) => this._resolver.resolve(sourceFilePath).source,
);
- const updated_at = Date.now();
- const contractNetworkData: ContractNetworkData = {
- solc_version: solcVersion,
- source_tree_hash: sourceTreeHashHex,
- optimizer_enabled: this._optimizerEnabled,
- abi,
- bytecode,
- runtime_bytecode: runtimeBytecode,
- updated_at,
- source_map: sourceMap,
- source_map_runtime: sourceMapRuntime,
- sources,
+ const contractVersion: ContractVersionData = {
+ compilerOutput: compiledData,
+ sources: compiled.sources,
+ sourceCodes,
+ sourceTreeHashHex,
+ compiler: {
+ name: 'solc',
+ version: solcVersion,
+ settings: this._compilerSettings,
+ },
};
let newArtifact: ContractArtifact;
@@ -223,17 +224,14 @@ export class Compiler {
const currentArtifact = currentArtifactIfExists as ContractArtifact;
newArtifact = {
...currentArtifact,
- networks: {
- ...currentArtifact.networks,
- [this._networkId]: contractNetworkData,
- },
+ ...contractVersion,
};
} else {
newArtifact = {
- contract_name: contractName,
- networks: {
- [this._networkId]: contractNetworkData,
- },
+ schemaVersion: constants.LATEST_ARTIFACT_VERSION,
+ contractName,
+ ...contractVersion,
+ networks: {},
};
}
diff --git a/packages/deployer/src/deployer.ts b/packages/deployer/src/deployer.ts
index ad05417b1..c8c3a9a06 100644
--- a/packages/deployer/src/deployer.ts
+++ b/packages/deployer/src/deployer.ts
@@ -2,6 +2,7 @@ import { AbiType, ConstructorAbi, ContractAbi, Provider, TxData } from '@0xproje
import { logUtils } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import * as _ from 'lodash';
+import * as solc from 'solc';
import * as Web3 from 'web3';
import { Contract } from './utils/contract';
@@ -28,7 +29,20 @@ export class Deployer {
private _artifactsDir: string;
private _networkId: number;
private _defaults: Partial<TxData>;
-
+ /**
+ * Gets data for current version stored in artifact.
+ * @param contractArtifact The contract artifact.
+ * @return Version specific contract data.
+ */
+ private static _getContractCompilerOutputFromArtifactIfExists(
+ contractArtifact: ContractArtifact,
+ ): solc.StandardContractOutput {
+ const compilerOutputIfExists = contractArtifact.compilerOutput;
+ if (_.isUndefined(compilerOutputIfExists)) {
+ throw new Error(`Compiler output not found in artifact for contract: ${contractArtifact.contractName}`);
+ }
+ return compilerOutputIfExists;
+ }
/**
* Instantiate a new instance of the Deployer class.
* @param opts Deployer options, including either an RPC url or Provider instance.
@@ -58,10 +72,8 @@ export class Deployer {
*/
public async deployAsync(contractName: string, args: any[] = []): Promise<Web3.ContractInstance> {
const contractArtifactIfExists: ContractArtifact = this._loadContractArtifactIfExists(contractName);
- const contractNetworkDataIfExists: ContractNetworkData = this._getContractNetworkDataFromArtifactIfExists(
- contractArtifactIfExists,
- );
- const data = contractNetworkDataIfExists.bytecode;
+ const compilerOutput = Deployer._getContractCompilerOutputFromArtifactIfExists(contractArtifactIfExists);
+ const data = compilerOutput.evm.bytecode.object;
const from = await this._getFromAddressAsync();
const gas = await this._getAllowableGasEstimateAsync(data);
const txData = {
@@ -70,7 +82,10 @@ export class Deployer {
data,
gas,
};
- const abi = contractNetworkDataIfExists.abi;
+ if (_.isUndefined(compilerOutput.abi)) {
+ throw new Error(`ABI not found in ${contractName} artifacts`);
+ }
+ const abi = compilerOutput.abi;
const constructorAbi = _.find(abi, { type: AbiType.Constructor }) as ConstructorAbi;
const constructorArgs = _.isUndefined(constructorAbi) ? [] : constructorAbi.inputs;
if (constructorArgs.length !== args.length) {
@@ -138,15 +153,16 @@ export class Deployer {
args: any[],
): Promise<void> {
const contractArtifactIfExists: ContractArtifact = this._loadContractArtifactIfExists(contractName);
- const contractNetworkDataIfExists: ContractNetworkData = this._getContractNetworkDataFromArtifactIfExists(
- contractArtifactIfExists,
- );
- const abi = contractNetworkDataIfExists.abi;
+ const compilerOutput = Deployer._getContractCompilerOutputFromArtifactIfExists(contractArtifactIfExists);
+ if (_.isUndefined(compilerOutput.abi)) {
+ throw new Error(`ABI not found in ${contractName} artifacts`);
+ }
+ const abi = compilerOutput.abi;
const encodedConstructorArgs = encoder.encodeConstructorArgsFromAbi(args, abi);
- const newContractData = {
- ...contractNetworkDataIfExists,
+ const newContractData: ContractNetworkData = {
address: contractAddress,
- constructor_args: encodedConstructorArgs,
+ links: {},
+ constructorArgs: encodedConstructorArgs,
};
const newArtifact = {
...contractArtifactIfExists,
@@ -174,18 +190,6 @@ export class Deployer {
}
}
/**
- * Gets data for current networkId stored in artifact.
- * @param contractArtifact The contract artifact.
- * @return Network specific contract data.
- */
- private _getContractNetworkDataFromArtifactIfExists(contractArtifact: ContractArtifact): ContractNetworkData {
- const contractNetworkDataIfExists = contractArtifact.networks[this._networkId];
- if (_.isUndefined(contractNetworkDataIfExists)) {
- throw new Error(`Data not found in artifact for contract: ${contractArtifact.contract_name}`);
- }
- return contractNetworkDataIfExists;
- }
- /**
* Gets the address to use for sending a transaction.
* @return The default from address. If not specified, returns the first address accessible by web3.
*/
diff --git a/packages/deployer/src/index.ts b/packages/deployer/src/index.ts
index 186d644ef..31a75677b 100644
--- a/packages/deployer/src/index.ts
+++ b/packages/deployer/src/index.ts
@@ -1,2 +1,3 @@
export { Deployer } from './deployer';
export { Compiler } from './compiler';
+export { ContractArtifact, ContractNetworks } from './utils/types';
diff --git a/packages/deployer/src/utils/constants.ts b/packages/deployer/src/utils/constants.ts
index 6f9dfb994..df2ddb3b2 100644
--- a/packages/deployer/src/utils/constants.ts
+++ b/packages/deployer/src/utils/constants.ts
@@ -1,4 +1,5 @@
export const constants = {
SOLIDITY_FILE_EXTENSION: '.sol',
BASE_COMPILER_URL: 'https://ethereum.github.io/solc-bin/bin/',
+ LATEST_ARTIFACT_VERSION: '2.0.0',
};
diff --git a/packages/deployer/src/utils/types.ts b/packages/deployer/src/utils/types.ts
index a20d0f627..19c27df67 100644
--- a/packages/deployer/src/utils/types.ts
+++ b/packages/deployer/src/utils/types.ts
@@ -1,4 +1,5 @@
import { ContractAbi, Provider, TxData } from '@0xproject/types';
+import * as solc from 'solc';
import * as Web3 from 'web3';
import * as yargs from 'yargs';
@@ -9,28 +10,40 @@ export enum AbiType {
Fallback = 'fallback',
}
-export interface ContractArtifact {
- contract_name: string;
+export interface ContractArtifact extends ContractVersionData {
+ schemaVersion: string;
+ contractName: string;
networks: ContractNetworks;
}
+export interface ContractVersionData {
+ compiler: {
+ name: 'solc';
+ version: string;
+ settings: solc.CompilerSettings;
+ };
+ sources: {
+ [sourceName: string]: {
+ id: number;
+ };
+ };
+ sourceCodes: {
+ [sourceName: string]: string;
+ };
+ sourceTreeHashHex: string;
+ compilerOutput: solc.StandardContractOutput;
+}
+
export interface ContractNetworks {
- [key: number]: ContractNetworkData;
+ [networkId: number]: ContractNetworkData;
}
export interface ContractNetworkData {
- solc_version: string;
- optimizer_enabled: boolean;
- source_tree_hash: string;
- abi: ContractAbi;
- bytecode: string;
- runtime_bytecode: string;
- address?: string;
- constructor_args?: string;
- updated_at: number;
- source_map: string;
- source_map_runtime: string;
- sources: string[];
+ address: string;
+ links: {
+ [linkName: string]: string;
+ };
+ constructorArgs: string;
}
export interface SolcErrors {
@@ -42,7 +55,6 @@ export interface CliOptions extends yargs.Arguments {
contractsDir: string;
jsonrpcUrl: string;
networkId: number;
- shouldOptimize: boolean;
gasPrice: string;
account?: string;
contract?: string;
@@ -50,11 +62,10 @@ export interface CliOptions extends yargs.Arguments {
}
export interface CompilerOptions {
- contractsDir: string;
- networkId: number;
- optimizerEnabled: boolean;
- artifactsDir: string;
- specifiedContracts: Set<string>;
+ contractsDir?: string;
+ artifactsDir?: string;
+ compilerSettings?: solc.CompilerSettings;
+ contracts?: string[] | '*';
}
export interface BaseDeployerOptions {