diff --git a/packages/core/console/src/index.ts b/packages/core/console/src/index.ts index 693d97b5f..abd4aab34 100644 --- a/packages/core/console/src/index.ts +++ b/packages/core/console/src/index.ts @@ -50,7 +50,7 @@ export default class Console { const error = { name: "Console error", message: err, stack: err.stack }; return cb(error); } - cb(null, util.inspect(result)); + cb(null, typeof result !== "string" ? util.inspect(result) : result); }); }); this.ipc.on("console:executePartial", (cmd: string, cb: any) => { @@ -93,18 +93,18 @@ export default class Console { private registerApi() { const plugin = this.plugins.createPlugin("consoleApi", {}); plugin.registerAPICall("post", "/embark-api/command", (req: any, res: any) => { - this.executeCmd(req.body.command, (err: any, result: any) => { + this.executeCmd(req.body.command, (err: any, result: any, shouldEscapeHtml = true) => { if (err) { return res.send({ result: err.message || err }); } let response = result; if (typeof result !== "string") { response = stringify(result, jsonFunctionReplacer, 2); - } else { + } else if (shouldEscapeHtml) { // Avoid HTML injection in the Cockpit response = escapeHtml(response); } - const jsonResponse = {result: response}; + const jsonResponse = { result: response }; if (res.headersSent) { return res.end(jsonResponse); } diff --git a/packages/plugins/profiler/src/gasEstimator.js b/packages/plugins/profiler/src/gasEstimator.js index 32af6ab3a..bc7e34861 100644 --- a/packages/plugins/profiler/src/gasEstimator.js +++ b/packages/plugins/profiler/src/gasEstimator.js @@ -2,7 +2,10 @@ const async = require('async'); const ContractFuzzer = require('./fuzzer.js'); const Web3 = require('web3'); -class GasEstimator { +export const GAS_ERROR = ' -ERROR- '; +export const EVENT_NO_GAS = ' -EVENT- '; + +export class GasEstimator { constructor(embark) { this.embark = embark; this.logger = embark.logger; @@ -10,6 +13,14 @@ class GasEstimator { this.fuzzer = new ContractFuzzer(embark); } + printError(message, name, values = []) { + this.logger.error(`Error getting gas estimate for "${name}(${Object.values(values).join(",")})"`, message); + if (message.includes('always failing transaction')) { + this.logger.error(`This may mean function assertions (revert, assert, require) are preventing the estimate from completing. Gas will be listed as "${GAS_ERROR}" in the profile.`); + } + this.logger.error(''); // new line to separate likely many lines + } + estimateGas(contractName, cb) { const self = this; let gasMap = {}; @@ -19,13 +30,16 @@ class GasEstimator { if (err) return cb(err); let fuzzMap = self.fuzzer.generateFuzz(3, contract); let contractObj = new web3.eth.Contract(contract.abiDefinition, contract.deployedAddress); - async.each(contract.abiDefinition.filter((x) => x.type !== "event"), + async.each(contract.abiDefinition, (abiMethod, gasCb) => { let name = abiMethod.name; if (abiMethod.type === "constructor") { // already provided for us gasMap['constructor'] = parseFloat(contract.gasEstimates.creation.totalCost.toString()); return gasCb(null, name, abiMethod.type); + } else if (abiMethod.type === "event") { + gasMap[name] = EVENT_NO_GAS; + return gasCb(null, name, abiMethod.type); } else if (abiMethod.type === "fallback") { gasMap['fallback'] = parseFloat(contract.gasEstimates.external[""].toString()); return gasCb(null, name, abiMethod.type); @@ -35,9 +49,9 @@ class GasEstimator { // just run it and register it contractObj.methods[name] .apply(contractObj.methods[name], []) - .estimateGas((err, gasAmount) => { + .estimateGas({ from: web3.eth.defaultAccount }, (err, gasAmount) => { if (err) { - self.logger.error(`Error getting gas estimate for "${name}"`, err.message || err); + self.printError(err.message || err, name); return gasCb(null, name, abiMethod.type); } gasMap[name] = gasAmount; @@ -49,13 +63,13 @@ class GasEstimator { contractObj.methods[name].apply(contractObj.methods[name], values) .estimateGas((err, gasAmount) => { if (err) { - self.logger.error(`Error getting gas estimate for "${name}"`, err.message || err); + self.printError(err.message || err, name, values); } getVarianceCb(null, gasAmount); }); }, (err, variance) => { if (variance.every(v => v === variance[0])) { - gasMap[name] = variance[0]; + gasMap[name] = variance[0] ?? GAS_ERROR; } else { // get average let sum = variance.reduce(function(memo, num) { return memo + num; }); @@ -77,5 +91,3 @@ class GasEstimator { }); } } - -module.exports = GasEstimator; diff --git a/packages/plugins/profiler/src/index.js b/packages/plugins/profiler/src/index.js index 691c4c47e..c04eabcee 100644 --- a/packages/plugins/profiler/src/index.js +++ b/packages/plugins/profiler/src/index.js @@ -1,7 +1,7 @@ import { warnIfPackageNotDefinedLocally } from 'embark-utils'; const asciiTable = require('ascii-table'); -const GasEstimator = require('./gasEstimator.js'); +import { GasEstimator, GAS_ERROR, EVENT_NO_GAS } from './gasEstimator'; class Profiler { constructor(embark, _options) { @@ -60,10 +60,19 @@ class Profiler { let table = new asciiTable(contractName); table.setHeading('Function', 'Payable', 'Mutability', 'Inputs', 'Outputs', 'Gas Estimates'); + table.setAlign(5, asciiTable.RIGHT); profileObj.methods.forEach((method) => { table.addRow(method.name, method.payable, method.mutability, self.formatParams(method.inputs), self.formatParams(method.outputs), method.gasEstimates); }); - return returnCb(null, table.toString()); + const strTable = table.toString(); + let result = [strTable]; + if (strTable.includes(GAS_ERROR)) { + result.push(`${GAS_ERROR} indicates there was an error during gas estimation (see console for details).`); + } + if (strTable.includes(EVENT_NO_GAS)) { + result.push(`${EVENT_NO_GAS} indicates the method is an event, and therefore no gas was estimated.`); + } + return returnCb(null, result.join('\n'), false); }); }