Better solc support in CLI; it will search the local pacakge for an existing solc version.
This commit is contained in:
parent
edb49da155
commit
7428776f75
@ -13,7 +13,7 @@ import { ethers } from "ethers";
|
|||||||
|
|
||||||
import { ArgParser, CLI, dump, Help, Plugin } from "../cli";
|
import { ArgParser, CLI, dump, Help, Plugin } from "../cli";
|
||||||
import { getPassword, getProgressBar } from "../prompt";
|
import { getPassword, getProgressBar } from "../prompt";
|
||||||
import { compile } from "../solc";
|
import { compile, ContractCode, customRequire } from "../solc";
|
||||||
|
|
||||||
function setupContext(path: string, context: any, plugin: Plugin) {
|
function setupContext(path: string, context: any, plugin: Plugin) {
|
||||||
|
|
||||||
@ -24,7 +24,8 @@ function setupContext(path: string, context: any, plugin: Plugin) {
|
|||||||
if (!context.__dirname) { context.__dirname = dirname(path); }
|
if (!context.__dirname) { context.__dirname = dirname(path); }
|
||||||
if (!context.console) { context.console = console; }
|
if (!context.console) { context.console = console; }
|
||||||
if (!context.require) {
|
if (!context.require) {
|
||||||
context.require = _module.createRequireFromPath(path);
|
//context.require = _module.createRequireFromPath(path);
|
||||||
|
context.require = customRequire(path);
|
||||||
}
|
}
|
||||||
if (!context.process) { context.process = process; }
|
if (!context.process) { context.process = process; }
|
||||||
|
|
||||||
@ -234,11 +235,13 @@ class FundPlugin extends Plugin {
|
|||||||
this.throwError("Funding requires --network ropsten");
|
this.throwError("Funding requires --network ropsten");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.length !== 1) {
|
if (args.length === 1) {
|
||||||
|
this.toAddress = await this.getAddress(args[0], "Cannot fund ZERO address", false);
|
||||||
|
} else if (args.length === 0 && this.accounts.length === 1) {
|
||||||
|
this.toAddress = await this.accounts[0].getAddress();
|
||||||
|
} else {
|
||||||
this.throwUsageError("fund requires ADDRESS");
|
this.throwUsageError("fund requires ADDRESS");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.toAddress = await this.getAddress(args[0], "Cannot fund ZERO address", false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async run(): Promise<void> {
|
async run(): Promise<void> {
|
||||||
@ -802,22 +805,36 @@ class CompilePlugin extends Plugin {
|
|||||||
this.throwError("compile requires exactly FILENAME");
|
this.throwError("compile requires exactly FILENAME");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.filename = args[0];
|
this.filename = resolve(args[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
async run(): Promise<void> {
|
async run(): Promise<void> {
|
||||||
let source = fs.readFileSync(this.filename).toString();
|
const source = fs.readFileSync(this.filename).toString();
|
||||||
let result = compile(source, {
|
|
||||||
filename: this.filename,
|
let result: Array<ContractCode> = null;
|
||||||
optimize: (!this.noOptimize)
|
try {
|
||||||
});
|
result = compile(source, {
|
||||||
|
filename: this.filename,
|
||||||
|
optimize: (!this.noOptimize)
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
if (error.errors) {
|
||||||
|
error.errors.forEach((error: string) => {
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
throw new Error("Failed to compile contract.");
|
||||||
|
}
|
||||||
|
|
||||||
let output: any = { };
|
let output: any = { };
|
||||||
result.forEach((contract, index) => {
|
result.forEach((contract, index) => {
|
||||||
output[contract.name] = {
|
output[contract.name] = {
|
||||||
bytecode: contract.bytecode,
|
bytecode: contract.bytecode,
|
||||||
runtime: contract.runtime,
|
runtime: contract.runtime,
|
||||||
interface: contract.interface.fragments.map((f) => f.format(ethers.utils.FormatTypes.full))
|
interface: contract.interface.fragments.map((f) => f.format(ethers.utils.FormatTypes.full)),
|
||||||
|
compiler: contract.compiler
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -826,7 +843,6 @@ class CompilePlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
cli.addPlugin("compile", CompilePlugin);
|
cli.addPlugin("compile", CompilePlugin);
|
||||||
|
|
||||||
|
|
||||||
class DeployPlugin extends Plugin {
|
class DeployPlugin extends Plugin {
|
||||||
filename: string;
|
filename: string;
|
||||||
contractName: string;
|
contractName: string;
|
||||||
@ -870,39 +886,56 @@ class DeployPlugin extends Plugin {
|
|||||||
this.throwError("deploy requires exactly FILENAME");
|
this.throwError("deploy requires exactly FILENAME");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.filename = args[0];
|
this.filename = resolve(args[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
async run(): Promise<void> {
|
async run(): Promise<void> {
|
||||||
let source = fs.readFileSync(this.filename).toString();
|
let source = fs.readFileSync(this.filename).toString();
|
||||||
let result = compile(source, {
|
let result: Array<ContractCode> = null;
|
||||||
filename: this.filename,
|
try {
|
||||||
optimize: (!this.noOptimize)
|
result = compile(source, {
|
||||||
});
|
filename: this.filename,
|
||||||
|
optimize: (!this.noOptimize)
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
if (error.errors) {
|
||||||
|
error.errors.forEach((error: string) => {
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
throw new Error("Failed to compile contract.");
|
||||||
|
}
|
||||||
|
|
||||||
let codes = result.filter((c) => (c.bytecode !== "0x" && (this.contractName == null || this.contractName == c.name)));
|
const codes = result.filter((c) => (this.contractName == null || this.contractName == c.name));
|
||||||
|
|
||||||
if (codes.length > 1) {
|
if (codes.length > 1) {
|
||||||
this.throwError("Please specify a contract with --contract NAME");
|
this.throwError("Multiple contracts found; please specify a contract with --contract NAME");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (codes.length === 0) {
|
if (codes.length === 0) {
|
||||||
this.throwError("No contract found");
|
this.throwError("No contract found");
|
||||||
}
|
}
|
||||||
|
|
||||||
let factory = new ethers.ContractFactory(codes[0].interface, codes[0].bytecode, this.accounts[0]);
|
const factory = new ethers.ContractFactory(codes[0].interface, codes[0].bytecode, this.accounts[0]);
|
||||||
|
|
||||||
let contract = await factory.deploy();
|
dump("Deploying:", {
|
||||||
|
Contract: codes[0].name,
|
||||||
|
Bytecode: codes[0].bytecode,
|
||||||
|
Interface: codes[0].interface.fragments.map((f) => f.format(ethers.utils.FormatTypes.full)),
|
||||||
|
Compiler: codes[0].compiler,
|
||||||
|
Optimizer: (this.noOptimize ? "No": "Yes")
|
||||||
|
});
|
||||||
|
|
||||||
|
const contract = await factory.deploy();
|
||||||
|
|
||||||
dump("Deployed:", {
|
dump("Deployed:", {
|
||||||
Contract: codes[0].name,
|
Contract: codes[0].name,
|
||||||
Address: contract.address,
|
Address: contract.address,
|
||||||
Bytecode: codes[0].bytecode,
|
|
||||||
Interface: codes[0].interface.fragments.map((f) => f.format(ethers.utils.FormatTypes.full))
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cli.addPlugin("deploy", DeployPlugin);
|
cli.addPlugin("deploy", DeployPlugin);
|
||||||
|
|
||||||
|
|
||||||
cli.run(process.argv.slice(2));
|
cli.run(process.argv.slice(2));
|
||||||
|
@ -1,23 +1,17 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
|
import _module from "module";
|
||||||
import { dirname, resolve } from "path";
|
import { dirname, resolve } from "path";
|
||||||
|
|
||||||
import { ethers } from "ethers";
|
import { ethers } from "ethers";
|
||||||
|
|
||||||
let _solc: any = null;
|
|
||||||
function getSolc(): any {
|
|
||||||
if (!_solc) {
|
|
||||||
_solc = require("solc");
|
|
||||||
}
|
|
||||||
return _solc;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ContractCode {
|
export interface ContractCode {
|
||||||
interface: ethers.utils.Interface;
|
interface: ethers.utils.Interface;
|
||||||
name: string;
|
name: string;
|
||||||
bytecode?: string;
|
compiler: string;
|
||||||
runtime?: string
|
bytecode: string;
|
||||||
|
runtime: string
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CompilerOptions = {
|
export type CompilerOptions = {
|
||||||
@ -27,7 +21,7 @@ export type CompilerOptions = {
|
|||||||
throwWarnings?: boolean;
|
throwWarnings?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function compile(source: string, options?: CompilerOptions): Array<ContractCode> {
|
function populateOptions(options?: CompilerOptions): CompilerOptions {
|
||||||
options = ethers.utils.shallowCopy(options || { });
|
options = ethers.utils.shallowCopy(options || { });
|
||||||
|
|
||||||
if (options.filename && !options.basedir) {
|
if (options.filename && !options.basedir) {
|
||||||
@ -36,10 +30,14 @@ export function compile(source: string, options?: CompilerOptions): Array<Contra
|
|||||||
if (!options.filename) { options.filename = "_contract.sol"; }
|
if (!options.filename) { options.filename = "_contract.sol"; }
|
||||||
if (!options.basedir) { options.basedir = "."; }
|
if (!options.basedir) { options.basedir = "."; }
|
||||||
|
|
||||||
let sources: { [ filename: string]: { content: string } } = { };
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getInput(source: string, options: CompilerOptions): any {
|
||||||
|
const sources: { [ filename: string ]: { content: string } } = { };
|
||||||
sources[options.filename] = { content: source };
|
sources[options.filename] = { content: source };
|
||||||
|
|
||||||
let input: any = {
|
const input: any = {
|
||||||
language: "Solidity",
|
language: "Solidity",
|
||||||
sources: sources,
|
sources: sources,
|
||||||
settings: {
|
settings: {
|
||||||
@ -58,7 +56,26 @@ export function compile(source: string, options?: CompilerOptions): Array<Contra
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let findImport = (filename: string): { contents?: string, error?: string } => {
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _compile(_solc: any, source: string, options?: CompilerOptions): Array<ContractCode> {
|
||||||
|
const compilerVersion = _solc.version();
|
||||||
|
|
||||||
|
const ver = compilerVersion.match(/(\d+)\.(\d+)\.(\d+)/);
|
||||||
|
if (!ver || ver[1] !== "0") { throw new Error("unknown version"); }
|
||||||
|
|
||||||
|
const version = parseFloat(ver[2] + "." + ver[3]);
|
||||||
|
//if (version < 4.11 || version >= 7) {
|
||||||
|
if (version < 5.0 || version >= 7.0) {
|
||||||
|
throw new Error(`unsupported version: ${ ver[1] }.${ ver[2] }.${ ver[3] }`);
|
||||||
|
}
|
||||||
|
|
||||||
|
options = populateOptions(options);
|
||||||
|
|
||||||
|
const input = getInput(source, options);
|
||||||
|
|
||||||
|
let findImport: any = (filename: string): { contents?: string, error?: string } => {
|
||||||
try {
|
try {
|
||||||
return {
|
return {
|
||||||
contents: fs.readFileSync(resolve(options.basedir, filename)).toString()
|
contents: fs.readFileSync(resolve(options.basedir, filename)).toString()
|
||||||
@ -68,25 +85,34 @@ export function compile(source: string, options?: CompilerOptions): Array<Contra
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let output = JSON.parse(getSolc().compile(JSON.stringify(input), findImport));
|
if (version >= 6) {
|
||||||
|
findImport = { import: findImport };
|
||||||
|
}
|
||||||
|
|
||||||
let errors = (output.errors || []).filter((x: any) => (x.severity === "error" || options.throwWarnings)).map((x: any) => x.formattedMessage);
|
const outputJson = _solc.compile(JSON.stringify(input), findImport);
|
||||||
|
const output = JSON.parse(outputJson);
|
||||||
|
|
||||||
|
const errors = (output.errors || []).filter((x: any) => (x.severity === "error" || options.throwWarnings)).map((x: any) => x.formattedMessage);
|
||||||
if (errors.length) {
|
if (errors.length) {
|
||||||
let error = new Error("compilation error");
|
const error = new Error("compilation error");
|
||||||
(<any>error).errors = errors;
|
(<any>error).errors = errors;
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
let result: Array<ContractCode> = [];
|
const result: Array<ContractCode> = [];
|
||||||
for (let filename in output.contracts) {
|
for (let filename in output.contracts) {
|
||||||
for (let name in output.contracts[filename]) {
|
for (let name in output.contracts[filename]) {
|
||||||
let contract = output.contracts[filename][name];
|
let contract = output.contracts[filename][name];
|
||||||
|
|
||||||
|
// Skip empty contracts
|
||||||
|
if (!contract.evm.bytecode.object) { continue; }
|
||||||
|
|
||||||
result.push({
|
result.push({
|
||||||
name: name,
|
name: name,
|
||||||
interface: new ethers.utils.Interface(contract.abi),
|
interface: new ethers.utils.Interface(contract.abi),
|
||||||
bytecode: "0x" + contract.evm.bytecode.object,
|
bytecode: "0x" + contract.evm.bytecode.object,
|
||||||
runtime: "0x" + contract.evm.deployedBytecode.object
|
runtime: "0x" + contract.evm.deployedBytecode.object,
|
||||||
|
compiler: compilerVersion
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -94,3 +120,33 @@ export function compile(source: string, options?: CompilerOptions): Array<Contra
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Creates a require which will first search from the current location,
|
||||||
|
// and for solc will fallback onto the version included in @ethersproject/cli
|
||||||
|
export function customRequire(path: string): (name: string) => any {
|
||||||
|
const pathRequire = _module.createRequireFromPath(resolve(path, "./sandbox.js"));
|
||||||
|
const libRequire = _module.createRequireFromPath(resolve(__filename));
|
||||||
|
|
||||||
|
return function(name: string): any {
|
||||||
|
try {
|
||||||
|
return pathRequire(name);
|
||||||
|
} catch (error) {
|
||||||
|
if (name === "solc") {
|
||||||
|
try {
|
||||||
|
return libRequire(name);
|
||||||
|
} catch (error) { }
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function wrapSolc(_solc: any): (source: string, options?: CompilerOptions) => Array<ContractCode> {
|
||||||
|
return function(source: string, options?: CompilerOptions): Array<ContractCode> {
|
||||||
|
return _compile(_solc, source, options || { });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const compile = wrapSolc(customRequire(".")("solc"));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user