2020-07-13 13:48:33 +03:00
|
|
|
/* istanbul ignore file */
|
|
|
|
|
2019-05-15 01:25:46 +03:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
import fs from "fs";
|
2020-01-30 05:36:50 +03:00
|
|
|
import _module from "module";
|
2019-05-15 01:25:46 +03:00
|
|
|
import { dirname, resolve } from "path";
|
|
|
|
|
|
|
|
import { ethers } from "ethers";
|
|
|
|
|
|
|
|
export interface ContractCode {
|
|
|
|
interface: ethers.utils.Interface;
|
|
|
|
name: string;
|
2020-01-30 05:36:50 +03:00
|
|
|
compiler: string;
|
|
|
|
bytecode: string;
|
|
|
|
runtime: string
|
2019-05-15 01:25:46 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
export type CompilerOptions = {
|
|
|
|
filename?: string;
|
|
|
|
basedir?: string;
|
|
|
|
optimize?: boolean;
|
|
|
|
throwWarnings?: boolean;
|
|
|
|
};
|
|
|
|
|
2020-01-30 05:36:50 +03:00
|
|
|
function populateOptions(options?: CompilerOptions): CompilerOptions {
|
2019-05-15 01:25:46 +03:00
|
|
|
options = ethers.utils.shallowCopy(options || { });
|
|
|
|
|
|
|
|
if (options.filename && !options.basedir) {
|
|
|
|
options.basedir = dirname(options.filename);
|
|
|
|
}
|
|
|
|
if (!options.filename) { options.filename = "_contract.sol"; }
|
|
|
|
if (!options.basedir) { options.basedir = "."; }
|
|
|
|
|
2020-01-30 05:36:50 +03:00
|
|
|
return options;
|
|
|
|
}
|
|
|
|
|
|
|
|
function getInput(source: string, options: CompilerOptions): any {
|
|
|
|
const sources: { [ filename: string ]: { content: string } } = { };
|
2019-05-15 01:25:46 +03:00
|
|
|
sources[options.filename] = { content: source };
|
|
|
|
|
2020-01-30 05:36:50 +03:00
|
|
|
const input: any = {
|
2019-05-15 01:25:46 +03:00
|
|
|
language: "Solidity",
|
|
|
|
sources: sources,
|
|
|
|
settings: {
|
|
|
|
outputSelection: {
|
|
|
|
"*": {
|
|
|
|
"*": [ "*" ]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if (options.optimize) {
|
|
|
|
input.settings.optimizer = {
|
|
|
|
enabled: true,
|
|
|
|
runs: 200
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-01-30 05:36:50 +03:00
|
|
|
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]);
|
2020-09-23 07:00:21 +03:00
|
|
|
//if (version < 4.11 || version >= 8) {
|
|
|
|
if (version < 5.0 || version >= 8.0) {
|
2020-01-30 05:36:50 +03:00
|
|
|
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 } => {
|
2019-05-15 01:25:46 +03:00
|
|
|
try {
|
|
|
|
return {
|
2019-11-19 12:17:31 +03:00
|
|
|
contents: fs.readFileSync(resolve(options.basedir, filename)).toString()
|
2019-05-15 01:25:46 +03:00
|
|
|
};
|
|
|
|
} catch (error) {
|
|
|
|
return { error: error.message }
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-01-30 05:36:50 +03:00
|
|
|
if (version >= 6) {
|
|
|
|
findImport = { import: findImport };
|
|
|
|
}
|
|
|
|
|
|
|
|
const outputJson = _solc.compile(JSON.stringify(input), findImport);
|
|
|
|
const output = JSON.parse(outputJson);
|
2019-05-15 01:25:46 +03:00
|
|
|
|
2020-01-30 05:36:50 +03:00
|
|
|
const errors = (output.errors || []).filter((x: any) => (x.severity === "error" || options.throwWarnings)).map((x: any) => x.formattedMessage);
|
2019-05-15 01:25:46 +03:00
|
|
|
if (errors.length) {
|
2020-01-30 05:36:50 +03:00
|
|
|
const error = new Error("compilation error");
|
2019-05-15 01:25:46 +03:00
|
|
|
(<any>error).errors = errors;
|
|
|
|
throw error;
|
|
|
|
}
|
|
|
|
|
2020-01-30 05:36:50 +03:00
|
|
|
const result: Array<ContractCode> = [];
|
2019-05-15 01:25:46 +03:00
|
|
|
for (let filename in output.contracts) {
|
|
|
|
for (let name in output.contracts[filename]) {
|
|
|
|
let contract = output.contracts[filename][name];
|
|
|
|
|
2020-01-30 05:36:50 +03:00
|
|
|
// Skip empty contracts
|
|
|
|
if (!contract.evm.bytecode.object) { continue; }
|
|
|
|
|
2019-05-15 01:25:46 +03:00
|
|
|
result.push({
|
|
|
|
name: name,
|
|
|
|
interface: new ethers.utils.Interface(contract.abi),
|
|
|
|
bytecode: "0x" + contract.evm.bytecode.object,
|
2020-01-30 05:36:50 +03:00
|
|
|
runtime: "0x" + contract.evm.deployedBytecode.object,
|
|
|
|
compiler: compilerVersion
|
2019-05-15 01:25:46 +03:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2020-01-30 05:36:50 +03:00
|
|
|
|
|
|
|
|
|
|
|
// 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 {
|
2020-02-12 23:00:18 +03:00
|
|
|
// Node 8.x does not support createRequireFromPath
|
|
|
|
const createRequire = (_module.createRequireFromPath || (function(path: string) {
|
|
|
|
return require
|
|
|
|
}));
|
|
|
|
|
|
|
|
const pathRequire = createRequire(resolve(path, "./sandbox.js"));
|
|
|
|
const libRequire = createRequire(resolve(__filename));
|
2020-01-30 05:36:50 +03:00
|
|
|
|
|
|
|
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"));
|