diff options
Diffstat (limited to 'packages/deployer/src/utils')
-rw-r--r-- | packages/deployer/src/utils/compiler.ts | 123 | ||||
-rw-r--r-- | packages/deployer/src/utils/contract.ts | 17 | ||||
-rw-r--r-- | packages/deployer/src/utils/encoder.ts | 8 | ||||
-rw-r--r-- | packages/deployer/src/utils/error_reporter.ts | 18 | ||||
-rw-r--r-- | packages/deployer/src/utils/fs_wrapper.ts | 1 | ||||
-rw-r--r-- | packages/deployer/src/utils/types.ts | 30 |
6 files changed, 161 insertions, 36 deletions
diff --git a/packages/deployer/src/utils/compiler.ts b/packages/deployer/src/utils/compiler.ts new file mode 100644 index 000000000..9c8fef26d --- /dev/null +++ b/packages/deployer/src/utils/compiler.ts @@ -0,0 +1,123 @@ +import { logUtils } from '@0xproject/utils'; +import * as _ from 'lodash'; +import * as path from 'path'; +import * as solc from 'solc'; + +import { constants } from './constants'; +import { fsWrapper } from './fs_wrapper'; +import { ContractArtifact, ContractSources } from './types'; + +/** + * 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. + * @return Contract data on network or undefined. + */ +export async function getContractArtifactIfExistsAsync( + artifactsDir: string, + fileName: string, +): Promise<ContractArtifact | void> { + let contractArtifact; + const contractName = path.basename(fileName, constants.SOLIDITY_FILE_EXTENSION); + const currentArtifactPath = `${artifactsDir}/${contractName}.json`; + try { + const opts = { + encoding: 'utf8', + }; + const contractArtifactString = await fsWrapper.readFileAsync(currentArtifactPath, opts); + contractArtifact = JSON.parse(contractArtifactString); + return contractArtifact; + } catch (err) { + logUtils.log(`Artifact for ${fileName} does not exist`); + return undefined; + } +} + +/** + * Creates the artifacts directory if it does not already exist. + * @param artifactsDir Path to the artifacts directory. + */ +export async function createArtifactsDirIfDoesNotExistAsync(artifactsDir: string): Promise<void> { + if (!fsWrapper.doesPathExistSync(artifactsDir)) { + logUtils.log('Creating artifacts directory...'); + await fsWrapper.mkdirAsync(artifactsDir); + } +} + +/** + * 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. + * 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)) { + throw new Error('Could not find a path in error message'); + } + 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(source: 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 + 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)) { + const dependencyPath = dependencyMatch[1]; + const basenName = path.basename(dependencyPath); + dependencies.push(basenName); + } + } + }); + return dependencies; +} + +/** + * 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. + * @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]; + if (_.isUndefined(source)) { + throw new Error(`Contract source not found for ${fileName}`); + } + const importContents: solc.ImportContents = { + contents: source, + }; + return importContents; +} diff --git a/packages/deployer/src/utils/contract.ts b/packages/deployer/src/utils/contract.ts index 9c57751ff..9b7baac11 100644 --- a/packages/deployer/src/utils/contract.ts +++ b/packages/deployer/src/utils/contract.ts @@ -1,4 +1,5 @@ import { schemas, SchemaValidator } from '@0xproject/json-schemas'; +import { ContractAbi, EventAbi, FunctionAbi, MethodAbi, TxData } from '@0xproject/types'; import { promisify } from '@0xproject/utils'; import * as _ from 'lodash'; import * as Web3 from 'web3'; @@ -7,14 +8,14 @@ import { AbiType } from './types'; export class Contract implements Web3.ContractInstance { public address: string; - public abi: Web3.ContractAbi; + public abi: ContractAbi; private _contract: Web3.ContractInstance; - private _defaults: Partial<Web3.TxData>; + private _defaults: Partial<TxData>; private _validator: SchemaValidator; // This class instance is going to be populated with functions and events depending on the ABI // and we don't know their types in advance [name: string]: any; - constructor(web3ContractInstance: Web3.ContractInstance, defaults: Partial<Web3.TxData>) { + constructor(web3ContractInstance: Web3.ContractInstance, defaults: Partial<TxData>) { this._contract = web3ContractInstance; this.address = web3ContractInstance.address; this.abi = web3ContractInstance.abi; @@ -24,8 +25,8 @@ export class Contract implements Web3.ContractInstance { this._validator = new SchemaValidator(); } private _populateFunctions(): void { - const functionsAbi = _.filter(this.abi, abiPart => abiPart.type === AbiType.Function) as Web3.FunctionAbi[]; - _.forEach(functionsAbi, (functionAbi: Web3.MethodAbi) => { + const functionsAbi = _.filter(this.abi, abiPart => abiPart.type === AbiType.Function) as FunctionAbi[]; + _.forEach(functionsAbi, (functionAbi: MethodAbi) => { if (functionAbi.constant) { const cbStyleCallFunction = this._contract[functionAbi.name].call; this[functionAbi.name] = promisify(cbStyleCallFunction, this._contract); @@ -42,8 +43,8 @@ export class Contract implements Web3.ContractInstance { }); } private _populateEvents(): void { - const eventsAbi = _.filter(this.abi, abiPart => abiPart.type === AbiType.Event) as Web3.EventAbi[]; - _.forEach(eventsAbi, (eventAbi: Web3.EventAbi) => { + const eventsAbi = _.filter(this.abi, abiPart => abiPart.type === AbiType.Event) as EventAbi[]; + _.forEach(eventsAbi, (eventAbi: EventAbi) => { this[eventAbi.name] = this._contract[eventAbi.name]; }); } @@ -51,7 +52,7 @@ export class Contract implements Web3.ContractInstance { const promisifiedWithDefaultParams = async (...args: any[]) => { const promise = new Promise((resolve, reject) => { const lastArg = args[args.length - 1]; - let txData: Partial<Web3.TxData> = {}; + let txData: Partial<TxData> = {}; if (this._isTxData(lastArg)) { txData = args.pop(); } diff --git a/packages/deployer/src/utils/encoder.ts b/packages/deployer/src/utils/encoder.ts index e3acde252..4f62662e1 100644 --- a/packages/deployer/src/utils/encoder.ts +++ b/packages/deployer/src/utils/encoder.ts @@ -1,15 +1,15 @@ +import { AbiDefinition, ContractAbi, DataItem } from '@0xproject/types'; import * as _ from 'lodash'; -import * as Web3 from 'web3'; import * as web3Abi from 'web3-eth-abi'; import { AbiType } from './types'; export const encoder = { - encodeConstructorArgsFromAbi(args: any[], abi: Web3.ContractAbi): string { + encodeConstructorArgsFromAbi(args: any[], abi: ContractAbi): string { const constructorTypes: string[] = []; - _.each(abi, (element: Web3.AbiDefinition) => { + _.each(abi, (element: AbiDefinition) => { if (element.type === AbiType.Constructor) { - _.each(element.inputs, (input: Web3.DataItem) => { + _.each(element.inputs, (input: DataItem) => { constructorTypes.push(input.type); }); } diff --git a/packages/deployer/src/utils/error_reporter.ts b/packages/deployer/src/utils/error_reporter.ts new file mode 100644 index 000000000..4e73307f0 --- /dev/null +++ b/packages/deployer/src/utils/error_reporter.ts @@ -0,0 +1,18 @@ +import { logUtils } from '@0xproject/utils'; + +/** + * Makes an async function no-throw printing errors to the console + * @param asyncFn async function to wrap + * @return Wrapped version of the passed function + */ +export function consoleReporter<T>(asyncFn: (arg: T) => Promise<void>): (arg: T) => Promise<void> { + const noThrowFnAsync = async (arg: T) => { + try { + const result = await asyncFn(arg); + return result; + } catch (err) { + logUtils.log(`${err}`); + } + }; + return noThrowFnAsync; +} diff --git a/packages/deployer/src/utils/fs_wrapper.ts b/packages/deployer/src/utils/fs_wrapper.ts index 34c7caa0e..e02c83f27 100644 --- a/packages/deployer/src/utils/fs_wrapper.ts +++ b/packages/deployer/src/utils/fs_wrapper.ts @@ -7,5 +7,6 @@ export const fsWrapper = { writeFileAsync: promisify<undefined>(fs.writeFile), mkdirAsync: promisify<undefined>(fs.mkdir), doesPathExistSync: fs.existsSync, + rmdirSync: fs.rmdirSync, removeFileAsync: promisify<undefined>(fs.unlink), }; diff --git a/packages/deployer/src/utils/types.ts b/packages/deployer/src/utils/types.ts index 0068faf6a..7cb3958cb 100644 --- a/packages/deployer/src/utils/types.ts +++ b/packages/deployer/src/utils/types.ts @@ -1,4 +1,4 @@ -import { TxData } from '@0xproject/types'; +import { ContractAbi, TxData } from '@0xproject/types'; import * as Web3 from 'web3'; import * as yargs from 'yargs'; @@ -20,10 +20,10 @@ export interface ContractNetworks { export interface ContractNetworkData { solc_version: string; - optimizer_enabled: number; + optimizer_enabled: boolean; keccak256: string; source_tree_hash: string; - abi: Web3.ContractAbi; + abi: ContractAbi; bytecode: string; runtime_bytecode: string; address?: string; @@ -53,7 +53,7 @@ export interface CliOptions extends yargs.Arguments { export interface CompilerOptions { contractsDir: string; networkId: number; - optimizerEnabled: number; + optimizerEnabled: boolean; artifactsDir: string; specifiedContracts: Set<string>; } @@ -84,27 +84,9 @@ export interface ContractSourceData { export interface ContractSpecificSourceData { dependencies: string[]; - solcVersion: string; + solcVersionRange: string; sourceHash: Buffer; - sourceTreeHashIfExists?: Buffer; -} - -// TODO: Consolidate with 0x.js definitions once types are moved into a separate package. -export enum ZeroExError { - ContractDoesNotExist = 'CONTRACT_DOES_NOT_EXIST', - ExchangeContractDoesNotExist = 'EXCHANGE_CONTRACT_DOES_NOT_EXIST', - UnhandledError = 'UNHANDLED_ERROR', - UserHasNoAssociatedAddress = 'USER_HAS_NO_ASSOCIATED_ADDRESSES', - InvalidSignature = 'INVALID_SIGNATURE', - ContractNotDeployedOnNetwork = 'CONTRACT_NOT_DEPLOYED_ON_NETWORK', - InsufficientAllowanceForTransfer = 'INSUFFICIENT_ALLOWANCE_FOR_TRANSFER', - InsufficientBalanceForTransfer = 'INSUFFICIENT_BALANCE_FOR_TRANSFER', - InsufficientEthBalanceForDeposit = 'INSUFFICIENT_ETH_BALANCE_FOR_DEPOSIT', - InsufficientWEthBalanceForWithdrawal = 'INSUFFICIENT_WETH_BALANCE_FOR_WITHDRAWAL', - InvalidJump = 'INVALID_JUMP', - OutOfGas = 'OUT_OF_GAS', - NoNetworkId = 'NO_NETWORK_ID', - SubscriptionNotFound = 'SUBSCRIPTION_NOT_FOUND', + sourceTreeHash: Buffer; } export interface Token { |