@nomiclabs/buidler
Version:
Buidler is an extensible developer tool that helps smart contract developers increase productivity by reliably bringing together the tools they want.
235 lines • 10.7 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const ansi_escapes_1 = __importDefault(require("ansi-escapes"));
const chalk_1 = __importDefault(require("chalk"));
const debug_1 = __importDefault(require("debug"));
const events_1 = require("events");
const fs_extra_1 = __importDefault(require("fs-extra"));
const path_1 = __importDefault(require("path"));
const semver_1 = __importDefault(require("semver"));
const util_1 = __importDefault(require("util"));
const constants_1 = require("../../constants");
const solidity_errors_1 = require("../stack-traces/solidity-errors");
const solidityTracer_1 = require("../stack-traces/solidityTracer");
const await_semaphore_1 = require("../vendor/await-semaphore");
const errors_1 = require("./errors");
const buidler_1 = require("./modules/buidler");
const eth_1 = require("./modules/eth");
const evm_1 = require("./modules/evm");
const logger_1 = require("./modules/logger");
const net_1 = require("./modules/net");
const web3_1 = require("./modules/web3");
const node_1 = require("./node");
const log = debug_1.default("buidler:core:buidler-evm:provider");
// Set of methods that are never logged
const PRIVATE_RPC_METHODS = new Set(["buidler_getStackTraceFailuresCount"]);
// tslint:disable only-buidler-error
class BuidlerEVMProvider extends events_1.EventEmitter {
constructor(_hardfork, _networkName, _chainId, _networkId, _blockGasLimit, _throwOnTransactionFailures, _throwOnCallFailures, _genesisAccounts = [], _solcVersion, _paths, _loggingEnabled = false, _allowUnlimitedContractSize = false, _initialDate, _experimentalBuidlerEVMMessageTraceHooks = []) {
super();
this._hardfork = _hardfork;
this._networkName = _networkName;
this._chainId = _chainId;
this._networkId = _networkId;
this._blockGasLimit = _blockGasLimit;
this._throwOnTransactionFailures = _throwOnTransactionFailures;
this._throwOnCallFailures = _throwOnCallFailures;
this._genesisAccounts = _genesisAccounts;
this._solcVersion = _solcVersion;
this._paths = _paths;
this._loggingEnabled = _loggingEnabled;
this._allowUnlimitedContractSize = _allowUnlimitedContractSize;
this._initialDate = _initialDate;
this._experimentalBuidlerEVMMessageTraceHooks = _experimentalBuidlerEVMMessageTraceHooks;
this._mutex = new await_semaphore_1.Mutex();
this._logger = new logger_1.ModulesLogger();
this._methodCollapsedCount = 0;
}
async send(method, params = []) {
const release = await this._mutex.acquire();
try {
if (this._loggingEnabled && !PRIVATE_RPC_METHODS.has(method)) {
return await this._sendWithLogging(method, params);
}
return await this._send(method, params);
}
finally {
release();
}
}
async _sendWithLogging(method, params = []) {
this._logger.clearLogs();
try {
const result = await this._send(method, params);
// We log after running the method, because we want to use different
// colors depending on whether it failed or not
// TODO: If an eth_call, eth_sendTransaction, or eth_sendRawTransaction
// fails without throwing, this will be displayed in green. It's unclear
// if this is correct. See Eth module's TODOs for more info.
if (this._shouldCollapseMethod(method)) {
this._logCollapsedMethod(method);
}
else {
this._startCollapsingMethod(method);
this._log(method, false, chalk_1.default.green);
}
const loggedSomething = this._logModuleMessages();
if (loggedSomething) {
this._stopCollapsingMethod();
this._log("");
}
return result;
}
catch (err) {
this._stopCollapsingMethod();
if (err instanceof errors_1.MethodNotFoundError ||
err instanceof errors_1.MethodNotSupportedError) {
this._log(`${method} - Method not supported`, false, chalk_1.default.red);
throw err;
}
this._log(method, false, chalk_1.default.red);
const loggedSomething = this._logModuleMessages();
if (loggedSomething) {
this._log("");
}
if (err instanceof solidity_errors_1.SolidityError) {
this._logError(err);
}
else if (err instanceof errors_1.BuidlerEVMProviderError) {
this._log(err.message, true);
}
else {
this._logError(err, true);
this._log("");
this._log("If you think this is a bug in Buidler, please report it here: https://buidler.dev/reportbug", true);
}
this._log("");
throw err;
}
}
_logCollapsedMethod(method) {
this._methodCollapsedCount += 1;
process.stdout.write(
// tslint:disable-next-line:prefer-template
ansi_escapes_1.default.cursorHide +
ansi_escapes_1.default.cursorPrevLine +
chalk_1.default.green(`${method} (${this._methodCollapsedCount})`) +
"\n" +
ansi_escapes_1.default.eraseEndLine +
ansi_escapes_1.default.cursorShow);
}
_startCollapsingMethod(method) {
this._methodBeingCollapsed = method;
this._methodCollapsedCount = 1;
}
_stopCollapsingMethod() {
this._methodBeingCollapsed = undefined;
this._methodCollapsedCount = 0;
}
_shouldCollapseMethod(method) {
return (method === this._methodBeingCollapsed &&
!this._logger.hasLogs() &&
this._methodCollapsedCount > 0);
}
async _send(method, params = []) {
await this._init();
if (method.startsWith("eth_")) {
return this._ethModule.processRequest(method, params);
}
if (method.startsWith("net_")) {
return this._netModule.processRequest(method, params);
}
if (method.startsWith("web3_")) {
return this._web3Module.processRequest(method, params);
}
if (method.startsWith("evm_")) {
return this._evmModule.processRequest(method, params);
}
if (method.startsWith("buidler_")) {
return this._buidlerModule.processRequest(method, params);
}
throw new errors_1.MethodNotFoundError(`Method ${method} not found`);
}
async _init() {
if (this._node !== undefined) {
return;
}
let compilerInput;
let compilerOutput;
if (this._solcVersion !== undefined && this._paths !== undefined) {
if (semver_1.default.lt(this._solcVersion, solidityTracer_1.FIRST_SOLC_VERSION_SUPPORTED)) {
console.warn(chalk_1.default.yellow(`Solidity stack traces only work with Solidity version ${solidityTracer_1.FIRST_SOLC_VERSION_SUPPORTED} or higher.`));
}
else {
let hasCompiledContracts = false;
if (await fs_extra_1.default.pathExists(this._paths.artifacts)) {
const artifactsDir = await fs_extra_1.default.readdir(this._paths.artifacts);
hasCompiledContracts = artifactsDir.some((f) => f.endsWith(".json"));
}
if (hasCompiledContracts) {
try {
const solcInputPath = path_1.default.join(this._paths.cache, constants_1.SOLC_INPUT_FILENAME);
const solcOutputPath = path_1.default.join(this._paths.cache, constants_1.SOLC_OUTPUT_FILENAME);
compilerInput = await fs_extra_1.default.readJSON(solcInputPath, {
encoding: "utf8",
});
compilerOutput = await fs_extra_1.default.readJSON(solcOutputPath, {
encoding: "utf8",
});
}
catch (error) {
console.warn(chalk_1.default.yellow("Stack traces engine could not be initialized. Run Buidler with --verbose to learn more."));
log("Solidity stack traces disabled: Failed to read solc's input and output files. Please report this to help us improve Buidler.\n", error);
}
}
}
}
const [common, node] = await node_1.BuidlerNode.create(this._hardfork, this._networkName, this._chainId, this._networkId, this._blockGasLimit, this._genesisAccounts, this._solcVersion, this._allowUnlimitedContractSize, this._initialDate, compilerInput, compilerOutput);
this._common = common;
this._node = node;
this._ethModule = new eth_1.EthModule(common, node, this._throwOnTransactionFailures, this._throwOnCallFailures, this._loggingEnabled ? this._logger : undefined, this._experimentalBuidlerEVMMessageTraceHooks);
this._netModule = new net_1.NetModule(common);
this._web3Module = new web3_1.Web3Module();
this._evmModule = new evm_1.EvmModule(node);
this._buidlerModule = new buidler_1.BuidlerModule(node);
const listener = (payload) => {
this.emit("notifications", {
subscription: `0x${payload.filterId.toString(16)}`,
result: payload.result,
});
};
// Handle eth_subscribe events and proxy them to handler
this._node.addListener("ethEvent", listener);
}
_logModuleMessages() {
const logs = this._logger.getLogs();
if (logs.length === 0) {
return false;
}
for (const msg of logs) {
this._log(msg, true);
}
return true;
}
_logError(err, logInRed = false) {
this._log(util_1.default.inspect(err), true, logInRed ? chalk_1.default.red : undefined);
}
_log(msg, indent = false, color) {
if (indent) {
msg = msg
.split("\n")
.map((line) => ` ${line}`)
.join("\n");
}
if (color !== undefined) {
console.log(color(msg));
return;
}
console.log(msg);
}
}
exports.BuidlerEVMProvider = BuidlerEVMProvider;
//# sourceMappingURL=provider.js.map