blob: c918ed1f3626f5232711ec6ba1f8391e94466119 (
plain) (
tree)
|
|
import { ContractSource } from '@0xproject/sol-resolver';
import { logUtils } from '@0xproject/utils';
import { ContractArtifact } from 'ethereum-types';
import * as _ from 'lodash';
import * as path from 'path';
import { fsWrapper } from './fs_wrapper';
/**
* 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}/${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 ${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.log(`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;
}
|