Skip to content
This repository has been archived by the owner on Jan 24, 2022. It is now read-only.

Typechain support #1285

Merged
merged 10 commits into from
Nov 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@
"solidity-parser-antlr": "^0.4.2",
"spinnies": "^0.3.0",
"truffle-config": "1.1.16",
"ts-generator": "^0.0.8",
"typechain": "^1.0.3",
"typechain-target-truffle": "^1.0.0",
"typechain-target-web3-v1": "^1.0.0",
"underscore": "^1.9.1",
"uuid": "^3.3.3",
"web3": "1.2.2",
Expand Down
30 changes: 28 additions & 2 deletions packages/cli/src/commands/compile.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { compile } from '../models/compiler/Compiler';
import { CompileParams } from '../scripts/interfaces';
import { ProjectCompilerOptions } from '../models/compiler/solidity/SolidityProjectCompiler';
import { ProjectCompilerOptions } from '../models/compiler/ProjectCompilerOptions';
import Telemetry from '../telemetry';
import ProjectFile from '../models/files/ProjectFile';
import isundefined from 'lodash.isundefined';
import { promptIfNeeded } from '../prompts/prompt';
import { TypechainQuestions } from '../prompts/typechain';

const name = 'compile';
const signature = `${name}`;
Expand Down Expand Up @@ -31,7 +35,7 @@ const register: (program: any) => any = program =>
.action(action);

async function action(options: CompileParams & { interactive: boolean }): Promise<void> {
const { evmVersion, solcVersion: version, optimizer, optimizerRuns } = options;
const { evmVersion, solcVersion: version, optimizer, optimizerRuns, interactive } = options;

// Handle optimizer option:
//- on --optimizer or --optimizer=on, enable it
Expand All @@ -57,6 +61,28 @@ async function action(options: CompileParams & { interactive: boolean }): Promis
},
};

// If typechain settings are undefined, we are running from an old project, so we ask the user if they want to enable it if we find a ts-config (this last bit handled by the typechain questions)
const projectFile = new ProjectFile();
if (
!projectFile.compilerOptions ||
!projectFile.compilerOptions.typechain ||
isundefined(projectFile.compilerOptions.typechain.enabled)
) {
const { typechainEnabled, typechainTarget, typechainOutdir } = await promptIfNeeded(
{
args: { typechainEnabled: undefined, typechainTarget: undefined, typechainOutdir: undefined },
defaults: {},
props: TypechainQuestions,
},
interactive,
);
compilerOptions.typechain = { enabled: typechainEnabled };
if (typechainEnabled) {
Object.assign(compilerOptions.typechain, { outDir: typechainOutdir, target: typechainTarget });
compilerOptions.force = true;
}
}

await Telemetry.report(
'compile',
{ evmVersion, solcVersion: version, optimizer, optimizerRuns },
Expand Down
16 changes: 13 additions & 3 deletions packages/cli/src/commands/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { FileSystem } from '@openzeppelin/upgrades';
import ProjectFile from '../models/files/ProjectFile';
import { notEmpty } from '../prompts/validators';
import Telemetry from '../telemetry';
import { TypechainQuestions } from '../prompts/typechain';

const name = 'init';
const signature = `${name} [project-name] [version]`;
Expand All @@ -16,18 +17,26 @@ const register: (program: any) => any = program =>
.command(signature, undefined, { noHelp: true })
.usage('<project-name> [version]')
.description(description)
.option('--publish', 'automatically publishes your project upon pushing it to a network')
.option('--publish', 'automatically publish your project upon pushing it to a network')
.option('--force', 'overwrite existing project if there is one')
.option('--typechain <target>', 'enable typechain support with specified target (web3-v1, ethers, or truffle)')
.option('--typechain-outdir <path>', 'set output directory for typechain compilation (defaults to types/contracts)')
.option('--link <dependency>', 'link to a dependency')
.option('--no-install', 'skip installing packages dependencies locally')
.withPushOptions()
.withNonInteractiveOption()
.action(action);

async function action(projectName: string, version: string, options: any): Promise<void> {
const { publish, force, link, install: installDependencies, interactive } = options;
const { publish, force, link, install: installDependencies, interactive, typechainOutdir, typechain } = options;

const args = { name: projectName, version };
const args = {
name: projectName,
version,
typechainEnabled: typechain ? true : typechain, // keep undefined and false values separate, since undefined means that the user hasn't chosen, and false means they don't want to use it
typechainTarget: typechain,
typechainOutdir,
};
const props = getCommandProps();
const defaults = FileSystem.parseJsonIfExists('package.json') || { version: '1.0.0' };
const prompted = await promptIfNeeded({ args, defaults, props }, interactive);
Expand Down Expand Up @@ -65,6 +74,7 @@ function getCommandProps(): InquirerQuestions {
return `Invalid semantic version: ${input}`;
},
},
...TypechainQuestions,
};
}

Expand Down
17 changes: 15 additions & 2 deletions packages/cli/src/models/compiler/Compiler.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { execFile as callbackExecFile, ExecException } from 'child_process';
import { Loggy } from '@openzeppelin/upgrades';
import { Loggy, Contracts } from '@openzeppelin/upgrades';
import Truffle from '../config/TruffleConfig';
import { compileProject, ProjectCompilerOptions, ProjectCompileResult } from './solidity/SolidityProjectCompiler';
import { compileProject, ProjectCompileResult } from './solidity/SolidityProjectCompiler';
import { ProjectCompilerOptions } from './ProjectCompilerOptions';
import findUp from 'find-up';
import ProjectFile from '../files/ProjectFile';
import { promisify } from 'util';
import merge from 'lodash.merge';
import typechain from './Typechain';

const state = { alreadyCompiled: false };
const execFile = promisify(callbackExecFile);
Expand All @@ -23,6 +25,8 @@ export async function compile(
enabled: false,
runs: 200,
},
inputDir: Contracts.getLocalContractsDir(),
outputDir: Contracts.getLocalBuildDir(),
};
merge(resolvedOptions, projectFile.compilerOptions, compilerOptions);

Expand All @@ -42,6 +46,14 @@ export async function compile(
const compileVersion = compileResult && compileResult.compilerVersion && compileResult.compilerVersion.version;
const compileVersionOptions = compileVersion ? { version: compileVersion } : null;

// Run typechain if requested
if (resolvedOptions.typechain && resolvedOptions.typechain.enabled) {
const result = compileResult as ProjectCompileResult;
const filesList = result && result.artifacts ? result.artifacts.map(c => c.contractName).join(',') : '*';
const filesGlob = `${resolvedOptions.outputDir}/${filesList}.json`;
await typechain(filesGlob, resolvedOptions.typechain.outDir, resolvedOptions.typechain.target);
}

// If compiled successfully, write back compiler settings to project.json to persist them
projectFile.setCompilerOptions({
...resolvedOptions,
Expand All @@ -50,6 +62,7 @@ export async function compile(
});
if (projectFile.exists()) projectFile.write();

// Avoid multiple compilations per CLI run
state.alreadyCompiled = true;
}

Expand Down
15 changes: 15 additions & 0 deletions packages/cli/src/models/compiler/ProjectCompilerOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { CompilerOptions } from './solidity/SolidityContractsCompiler';

export interface TypechainOptions {
enabled: boolean;
outDir?: string;
target?: string;
}

export interface ProjectCompilerOptions extends CompilerOptions {
manager?: string;
inputDir?: string;
outputDir?: string;
force?: boolean;
typechain?: TypechainOptions;
}
24 changes: 24 additions & 0 deletions packages/cli/src/models/compiler/Typechain.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { mkdirpSync } from 'fs-extra';
import path from 'path';
import { tsGenerator } from 'ts-generator';
import { TypeChain as typeChain } from 'typechain/dist/TypeChain';

export default async function typechain(files: string, outDir: string, target: string): Promise<void> {
const cwd = process.cwd();
mkdirpSync(outDir);

// Hack required until https://github.com/ethereum-ts/TypeChain/pull/186 is merged
const relativeOutDir = path.relative(cwd, outDir);

return tsGenerator(
{ cwd },
new typeChain({
cwd,
rawConfig: {
files,
outDir: relativeOutDir,
target,
},
}),
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { Loggy, Contracts } from '@openzeppelin/upgrades';
import {
RawContract,
CompiledContract,
CompilerOptions,
resolveCompilerVersion,
compileWith,
DEFAULT_OPTIMIZER,
Expand All @@ -21,6 +20,7 @@ import { gatherSources } from './ResolverEngineGatherer';
import { SolcBuild } from './CompilerProvider';
import { compilerVersionsMatch, compilerSettingsMatch } from '../../../utils/solidity';
import { tryFunc } from '../../../utils/try';
import { ProjectCompilerOptions } from '../ProjectCompilerOptions';

export async function compileProject(options: ProjectCompilerOptions = {}): Promise<ProjectCompileResult> {
const projectCompiler = new SolidityProjectCompiler({
Expand All @@ -33,19 +33,19 @@ export async function compileProject(options: ProjectCompilerOptions = {}): Prom
return {
contracts: projectCompiler.contracts,
compilerVersion: projectCompiler.compilerVersion,
artifacts: projectCompiler.compilerOutput,
};
}

export interface ProjectCompilerOptions extends CompilerOptions {
manager?: string;
inputDir?: string;
outputDir?: string;
force?: boolean;
export interface CompiledContract {
filePath: string;
contractName: string;
}

export interface ProjectCompileResult {
compilerVersion: SolcBuild;
contracts: RawContract[];
artifacts: CompiledContract[];
}

class SolidityProjectCompiler {
Expand Down Expand Up @@ -191,6 +191,7 @@ class SolidityProjectCompiler {
const buildFileName = `${this.outputDir}/${name}.json`;
if (networksInfo[name]) Object.assign(data, { networks: networksInfo[name] });
await writeJson(buildFileName, data, { spaces: 2 });
return { filePath: buildFileName, contractName: name };
}),
);
}
Expand Down
12 changes: 10 additions & 2 deletions packages/cli/src/models/files/ProjectFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import Dependency from '../dependency/Dependency';
import { MANIFEST_VERSION, checkVersion } from './ManifestVersion';
import { OPEN_ZEPPELIN_FOLDER } from './constants';
import NetworkFile from './NetworkFile';
import { ProjectCompilerOptions } from '../compiler/solidity/SolidityProjectCompiler';
import { ProjectCompilerOptions } from '../compiler/ProjectCompilerOptions';

interface ConfigFileCompilerOptions {
manager: string;
Expand All @@ -22,6 +22,11 @@ interface ConfigFileCompilerOptions {
runs?: string;
};
};
typechain: {
enabled: boolean;
outDir?: string;
target?: string;
};
}

interface ProjectFileData {
Expand Down Expand Up @@ -164,6 +169,7 @@ export default class ProjectFile {
const inputDir = config && config.contractsDir;
const outputDir = config && config.artifactsDir;
const compilerSettings = config && config.compilerSettings;
const typechain = config && config.typechain;
const evmVersion = compilerSettings && compilerSettings.evmVersion;
const optimizer = compilerSettings && compilerSettings.optimizer;

Expand All @@ -177,6 +183,7 @@ export default class ProjectFile {
enabled: optimizer && optimizer.enabled,
runs: optimizer && optimizer.runs && parseInt(optimizer.runs),
},
typechain,
};
}

Expand All @@ -187,7 +194,7 @@ export default class ProjectFile {
}

public setCompilerOptions(options: ProjectCompilerOptions): void {
const { manager, version, outputDir, inputDir, evmVersion, optimizer } = options;
const { manager, version, outputDir, inputDir, evmVersion, optimizer, typechain } = options;
const configOptions: ConfigFileCompilerOptions = {
manager,
solcVersion: version,
Expand All @@ -200,6 +207,7 @@ export default class ProjectFile {
runs: optimizer && optimizer.runs && optimizer.runs.toString(),
},
},
typechain,
};

this.data.compiler =
Expand Down
29 changes: 29 additions & 0 deletions packages/cli/src/prompts/typechain.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { FileSystem } from '@openzeppelin/upgrades';
import { notEmpty } from './validators';
import { InquirerQuestions } from './prompt';

export const TypechainQuestions: InquirerQuestions = {
typechainEnabled: {
message: 'Enable typechain support?',
type: 'confirm',
default: true,
when: () => FileSystem.exists('tsconfig.json'),
},
typechainTarget: {
message: 'Typechain compilation target',
type: 'list',
choices: [
{ name: 'web3-v1 compatible', value: 'web3-v1' },
{ name: 'truffle-contract compatible', value: 'truffle' },
{ name: 'ethers.js compatible', value: 'ethers' },
],
when: ({ typechainEnabled }) => typechainEnabled,
},
typechainOutdir: {
message: 'Typechain output directory',
type: 'input',
validate: notEmpty,
default: './types/contracts/',
when: ({ typechainEnabled }) => typechainEnabled,
},
};
8 changes: 8 additions & 0 deletions packages/cli/src/scripts/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,17 @@ export default async function init({
installDependencies = false,
force = false,
projectFile = new ProjectFile(),
typechainEnabled = false,
typechainOutdir = null,
typechainTarget = null,
}: InitParams): Promise<void | never> {
const controller = new LocalController(projectFile, true);
controller.init(name, version, force, publish);

const typechain = { enabled: typechainEnabled };
if (typechainEnabled) Object.assign(typechain, { outDir: typechainOutdir, target: typechainTarget });
controller.projectFile.setCompilerOptions({ typechain });

if (dependencies.length !== 0) await controller.linkDependencies(dependencies, installDependencies);
controller.writePackage();
}
3 changes: 3 additions & 0 deletions packages/cli/src/scripts/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ export interface InitParams extends Dependencies {
force?: boolean;
publish?: boolean;
projectFile?: ProjectFile;
typechainEnabled?: boolean;
typechainTarget?: string;
typechainOutdir?: string;
}

export interface UnpackParams {
Expand Down
6 changes: 3 additions & 3 deletions packages/cli/test/commands/compile.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ import { stubCommands, itShouldParse } from './share';
describe('compile command', function() {
stubCommands();

itShouldParse('should call compile', 'compiler', 'zos compile', function(compiler) {
itShouldParse('should call compile', 'compiler', 'zos compile --no-interactive', function(compiler) {
compiler.should.have.been.calledOnce;
});

itShouldParse(
'should call compile with options',
'compiler',
'zos compile --solc-version 0.5.0 --optimizer --optimizer-runs 300 --evm-version petersburg',
'zos compile --solc-version 0.5.0 --optimizer --optimizer-runs 300 --evm-version petersburg --no-interactive',
function(compiler) {
compiler.should.have.been.calledWithMatch({
manager: 'openzeppelin',
Expand All @@ -27,7 +27,7 @@ describe('compile command', function() {
itShouldParse(
'should call compile with optimizer disabled',
'compiler',
'zos compile --solc-version 0.5.0 --optimizer=off',
'zos compile --solc-version 0.5.0 --optimizer=off --no-interactive',
function(compiler) {
compiler.should.have.been.calledWithMatch({
manager: 'openzeppelin',
Expand Down
Loading