2018-05-08 15:30:46 +00:00
|
|
|
const asciiTable = require('ascii-table');
|
2018-07-07 13:30:47 +00:00
|
|
|
const GasEstimator = require('./gasEstimator.js');
|
2018-05-08 15:30:46 +00:00
|
|
|
|
|
|
|
class Profiler {
|
2018-08-01 17:27:50 +00:00
|
|
|
constructor(embark, options) {
|
2018-05-08 15:30:46 +00:00
|
|
|
this.embark = embark;
|
|
|
|
this.logger = embark.logger;
|
|
|
|
this.events = embark.events;
|
2018-08-01 17:27:50 +00:00
|
|
|
this.plugins = options.plugins;
|
2018-06-06 19:37:08 +00:00
|
|
|
this.gasEstimator = new GasEstimator(embark);
|
2018-05-08 15:30:46 +00:00
|
|
|
|
|
|
|
this.registerConsoleCommand();
|
2018-08-01 17:27:50 +00:00
|
|
|
this.registerApi();
|
2018-05-08 15:30:46 +00:00
|
|
|
}
|
|
|
|
|
2018-08-27 20:22:53 +00:00
|
|
|
profileJSON(contractName, returnCb) {
|
|
|
|
const self = this;
|
|
|
|
|
|
|
|
let profileObj = {};
|
|
|
|
profileObj.name = contractName;
|
|
|
|
profileObj.methods = [];
|
|
|
|
|
|
|
|
self.events.request('contracts:contract', contractName, (contract) => {
|
|
|
|
if (!contract || !contract.deployedAddress) {
|
|
|
|
return returnCb("-- couldn't profile " + contractName + " - it's not deployed or could be an interface");
|
|
|
|
}
|
|
|
|
self.gasEstimator.estimateGas(contractName, function(_err, gastimates, _name) {
|
|
|
|
contract.abiDefinition.forEach((abiMethod) => {
|
|
|
|
let methodName = abiMethod.name;
|
|
|
|
if (['constructor', 'fallback'].indexOf(abiMethod.type) >= 0) {
|
|
|
|
methodName = abiMethod.type;
|
|
|
|
}
|
|
|
|
|
|
|
|
profileObj.methods.push({
|
|
|
|
name: methodName,
|
|
|
|
payable: abiMethod.payable,
|
|
|
|
mutability: abiMethod.stateMutability,
|
|
|
|
inputs: abiMethod.inputs || [],
|
|
|
|
outputs: abiMethod.outputs || [],
|
|
|
|
gasEstimates: gastimates && gastimates[methodName]
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
returnCb(null, profileObj);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
profile(contractName, returnCb) {
|
2018-05-08 15:30:46 +00:00
|
|
|
const self = this;
|
2018-08-01 18:10:02 +00:00
|
|
|
|
2018-08-01 19:09:16 +00:00
|
|
|
this.profileJSON(contractName, (err, profileObj) => {
|
2018-06-12 17:10:35 +00:00
|
|
|
if (err) {
|
2018-08-27 20:22:53 +00:00
|
|
|
self.logger.error(JSON.stringify(err));
|
|
|
|
return returnCb(err);
|
2018-06-12 17:10:35 +00:00
|
|
|
}
|
2018-08-01 18:10:02 +00:00
|
|
|
|
|
|
|
let table = new asciiTable(contractName);
|
|
|
|
table.setHeading('Function', 'Payable', 'Mutability', 'Inputs', 'Outputs', 'Gas Estimates');
|
|
|
|
profileObj.methods.forEach((method) => {
|
2018-08-01 19:16:41 +00:00
|
|
|
table.addRow(method.name, method.payable, method.mutability, self.formatParams(method.inputs), self.formatParams(method.outputs), method.gasEstimates);
|
2018-06-08 19:26:32 +00:00
|
|
|
});
|
2018-08-27 20:22:53 +00:00
|
|
|
return returnCb(null, table.toString());
|
2018-05-08 15:30:46 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
formatParams(params) {
|
2018-08-01 19:16:41 +00:00
|
|
|
return "(" + (params || []).map(param => param.type).join(',') + ")";
|
2018-05-08 15:30:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
registerConsoleCommand() {
|
|
|
|
const self = this;
|
|
|
|
self.embark.registerConsoleCommand((cmd, _options) => {
|
|
|
|
let cmdName = cmd.split(' ')[0];
|
|
|
|
let contractName = cmd.split(' ')[1];
|
2018-08-08 12:42:45 +00:00
|
|
|
|
|
|
|
return {
|
|
|
|
match: () => cmdName === 'profile',
|
|
|
|
process: (callback) => {
|
|
|
|
self.events.request('contracts:contract', contractName, (contract) => {
|
|
|
|
if (!contract || !contract.deployedAddress) {
|
2018-08-10 13:22:45 +00:00
|
|
|
return callback(null, "-- couldn't profile " + contractName + " - it's not deployed or could be an interface");
|
2018-08-08 12:42:45 +00:00
|
|
|
}
|
|
|
|
this.profile(contractName, contract, callback);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
2018-05-08 15:30:46 +00:00
|
|
|
});
|
|
|
|
}
|
2018-08-01 17:27:50 +00:00
|
|
|
|
|
|
|
registerApi() {
|
|
|
|
const self = this;
|
|
|
|
|
|
|
|
let plugin = this.plugins.createPlugin('profiler', {});
|
|
|
|
plugin.registerAPICall(
|
|
|
|
'get',
|
|
|
|
'/embark-api/profiler/:contractName',
|
|
|
|
(req, res) => {
|
|
|
|
let contractName = req.params.contractName;
|
2018-08-01 19:09:16 +00:00
|
|
|
|
|
|
|
self.profileJSON(contractName, (err, table) => {
|
|
|
|
if (err) {
|
2018-08-09 09:38:01 +00:00
|
|
|
return res.send({error: err.message});
|
2018-08-01 17:27:50 +00:00
|
|
|
}
|
2018-08-01 19:09:16 +00:00
|
|
|
res.send(table);
|
2018-08-01 17:27:50 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2018-05-08 15:30:46 +00:00
|
|
|
}
|
|
|
|
|
2018-05-08 15:41:38 +00:00
|
|
|
module.exports = Profiler;
|