Added contract profile api for fiddle
Compiled fiddles are automatically profiled on response of compilation via saga. The profile is passed to the ContractFunctions component for constructor/function view.
This commit is contained in:
parent
ea63e406e7
commit
69aea01b8b
|
@ -168,13 +168,13 @@ export const ensRecords = {
|
|||
failure: (error) => action(ENS_RECORDS[FAILURE], {error})
|
||||
};
|
||||
|
||||
export const FIDDLE = createRequestTypes('FIDDLE');
|
||||
export const fiddle = {
|
||||
post: (codeToCompile, timestamp) => action(FIDDLE[REQUEST], {codeToCompile, timestamp}),
|
||||
export const FIDDLE_COMPILE = createRequestTypes('FIDDLE_COMPILE');
|
||||
export const fiddleCompile = {
|
||||
post: (codeToCompile, timestamp) => action(FIDDLE_COMPILE[REQUEST], {codeToCompile, timestamp}),
|
||||
success: (fiddle, payload) => {
|
||||
return action(FIDDLE[SUCCESS], {fiddles: [{...fiddle, ...payload}]});
|
||||
return action(FIDDLE_COMPILE[SUCCESS], {fiddleCompiles: [{...fiddle, ...payload}]});
|
||||
},
|
||||
failure: (error) => action(FIDDLE[FAILURE], {error})
|
||||
failure: (error) => action(FIDDLE_COMPILE[FAILURE], {error})
|
||||
};
|
||||
|
||||
export const FIDDLE_DEPLOY = createRequestTypes('FIDDLE_DEPLOY');
|
||||
|
@ -193,6 +193,15 @@ export const fiddleFile = {
|
|||
failure: (error) => action(FIDDLE_FILE[FAILURE], {error})
|
||||
};
|
||||
|
||||
export const FIDDLE_PROFILE = createRequestTypes('FIDDLE_PROFILE');
|
||||
export const fiddleProfile = {
|
||||
post: (compiledCode, timestamp) => action(FIDDLE_PROFILE[REQUEST], {compiledCode, timestamp}),
|
||||
success: (fiddleProfile, payload) => {
|
||||
return action(FIDDLE_PROFILE[SUCCESS], {fiddleProfiles: [{...fiddleProfile, ...payload}]});
|
||||
},
|
||||
failure: (error) => action(FIDDLE_PROFILE[FAILURE], {error})
|
||||
};
|
||||
|
||||
export const FILES = createRequestTypes('FILES');
|
||||
export const files = {
|
||||
request: () => action(FILES[REQUEST]),
|
||||
|
|
|
@ -136,7 +136,7 @@ export function websocketGasOracle() {
|
|||
return new WebSocket(`${constants.wsEndpoint}/blockchain/gas/oracle`);
|
||||
}
|
||||
|
||||
export function postFiddle(payload) {
|
||||
export function postFiddleCompile(payload) {
|
||||
return post('/contract/compile', payload);
|
||||
}
|
||||
|
||||
|
@ -147,3 +147,8 @@ export function postFiddleDeploy(payload) {
|
|||
export function fetchFiles() {
|
||||
return get('/files');
|
||||
}
|
||||
|
||||
export function postFiddleProfile(payload) {
|
||||
return post('/contract/profiler/profile', {compiledContract: payload.compiledCode});
|
||||
}
|
||||
|
||||
|
|
|
@ -4,15 +4,15 @@ import React, {Component} from 'react';
|
|||
import {connect} from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
fiddle as fiddleAction,
|
||||
fiddleDeploy as fiddleDeployAction,
|
||||
fiddleFile as fiddleFileAction
|
||||
fiddleCompile,
|
||||
fiddleDeploy,
|
||||
fiddleFile
|
||||
} from '../actions';
|
||||
import Fiddle from '../components/Fiddle';
|
||||
import FiddleResults from '../components/FiddleResults';
|
||||
import FiddleResultsSummary from '../components/FiddleResultsSummary';
|
||||
import scrollToComponent from 'react-scroll-to-component';
|
||||
import {getFiddle, getFiddleDeploy} from "../reducers/selectors";
|
||||
import {getFiddleCompile, getFiddleDeploy, getFiddleProfile} from "../reducers/selectors";
|
||||
import CompilerError from "../components/CompilerError";
|
||||
import {List, Badge, Button} from 'tabler-react';
|
||||
import {NavLink} from 'react-router-dom';
|
||||
|
@ -65,7 +65,7 @@ class FiddleContainer extends Component {
|
|||
if (this.compileTimeout) clearTimeout(this.compileTimeout);
|
||||
this.compileTimeout = setTimeout(() => {
|
||||
this.setState({loadingMessage: 'Compiling...'});
|
||||
this.props.postFiddle(newValue, Date.now());
|
||||
this.props.postFiddleCompile(newValue, Date.now());
|
||||
}, immediate ? 0 : 1000);
|
||||
}
|
||||
|
||||
|
@ -81,7 +81,7 @@ class FiddleContainer extends Component {
|
|||
|
||||
_onDeployClick(_e) {
|
||||
this.setState({loadingMessage: 'Deploying...'});
|
||||
this.props.postFiddleDeploy(this.props.fiddle.compilationResult);
|
||||
this.props.postFiddleDeploy(this.props.compiledFiddle.compilationResult);
|
||||
scrollToComponent(this.deployedCardRef || this.fiddleResultsRef.current); // deployedCardRef null on first Deploy click
|
||||
}
|
||||
|
||||
|
@ -165,15 +165,23 @@ class FiddleContainer extends Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const {fiddle, loading, fiddleError, fiddleDeployError, deployedContracts, fatalError} = this.props;
|
||||
const {
|
||||
compiledFiddle,
|
||||
profiledFiddle,
|
||||
loading,
|
||||
fiddleCompileError,
|
||||
fiddleDeployError,
|
||||
deployedFiddle,
|
||||
fatalError
|
||||
} = this.props;
|
||||
const {loadingMessage, value, readOnly} = this.state;
|
||||
let warnings = [];
|
||||
let errors = [];
|
||||
if (fiddle && fiddle.errors) {
|
||||
warnings = this._renderErrors(fiddle.errors, "warning");
|
||||
errors = this._renderErrors(fiddle.errors, "error");
|
||||
if (compiledFiddle && compiledFiddle.errors) {
|
||||
warnings = this._renderErrors(compiledFiddle.errors, "warning");
|
||||
errors = this._renderErrors(compiledFiddle.errors, "error");
|
||||
}
|
||||
const hasResult = Boolean(fiddle);
|
||||
const hasResult = Boolean(compiledFiddle);
|
||||
return (
|
||||
<React.Fragment>
|
||||
<h1 className="page-title">Fiddle</h1>
|
||||
|
@ -184,11 +192,11 @@ class FiddleContainer extends Component {
|
|||
isLoading={loading}
|
||||
loadingMessage={loadingMessage}
|
||||
showFatalError={Boolean(fatalError)}
|
||||
showFatalFiddle={Boolean(fiddleError)}
|
||||
showFatalFiddle={Boolean(fiddleCompileError)}
|
||||
showFatalFiddleDeploy={Boolean(fiddleDeployError)}
|
||||
onDeployClick={(e) => this._onDeployClick(e)}
|
||||
isVisible={Boolean(fatalError || hasResult || loading)}
|
||||
showDeploy={hasResult && Boolean(fiddle.compilationResult)}
|
||||
showDeploy={hasResult && Boolean(compiledFiddle.compilationResult)}
|
||||
onWarningsClick={(e) => this._onErrorSummaryClick(e, this.errorsCardRef)}
|
||||
onErrorsClick={(e) => this._onErrorSummaryClick(e, this.warningsCardRef)}
|
||||
onFatalClick={(e) => this._onErrorSummaryClick(e, this.fatalCardRef)}
|
||||
|
@ -211,17 +219,17 @@ class FiddleContainer extends Component {
|
|||
errorsCard={this._renderErrorsCard(errors, "error")}
|
||||
warningsCard={this._renderErrorsCard(warnings, "warning")}
|
||||
fatalErrorCard={this._renderFatalCard("Fatal error", fatalError)}
|
||||
fatalFiddleCard={this._renderFatalCard("Failed to compile", fiddleError)}
|
||||
fatalFiddleCard={this._renderFatalCard("Failed to compile", fiddleCompileError)}
|
||||
fatalFiddleDeployCard={this._renderFatalCard("Failed to deploy", fiddleDeployError)}
|
||||
compiledContractsCard={fiddle && fiddle.compilationResult && this._renderSuccessCard("Contract(s) compiled!",
|
||||
<ContractFunctions contractProfile={fiddle.compilationResult}
|
||||
contractFunctions={deployedContracts}
|
||||
compiledContractsCard={compiledFiddle && compiledFiddle.compilationResult && this._renderSuccessCard("Contract(s) compiled!",
|
||||
<ContractFunctions contractProfile={profiledFiddle}
|
||||
contractFunctions={deployedFiddle}
|
||||
onlyConstructor
|
||||
postContractFunction={this._onDeployClick}/>
|
||||
)}
|
||||
deployedContractsCard={deployedContracts && this._renderSuccessCard("Contract(s) deployed!",
|
||||
deployedContractsCard={deployedFiddle && this._renderSuccessCard("Contract(s) deployed!",
|
||||
<Button
|
||||
to={`/embark/contracts/${deployedContracts}/overview`}
|
||||
to={`/embark/contracts/${deployedFiddle}/overview`}
|
||||
RootComponent={NavLink}
|
||||
>Play with my contract(s)</Button>
|
||||
)}
|
||||
|
@ -232,27 +240,32 @@ class FiddleContainer extends Component {
|
|||
}
|
||||
}
|
||||
function mapStateToProps(state) {
|
||||
const fiddle = getFiddle(state);
|
||||
const compiledFiddle = getFiddleCompile(state);
|
||||
const deployedFiddle = getFiddleDeploy(state);
|
||||
const profiledFiddle = getFiddleProfile(state);
|
||||
return {
|
||||
fiddle: fiddle.data,
|
||||
deployedContracts: deployedFiddle.data,
|
||||
fiddleError: fiddle.error,
|
||||
compiledFiddle: compiledFiddle.data,
|
||||
deployedFiddle: deployedFiddle.data,
|
||||
profiledFiddle: profiledFiddle.data,
|
||||
fiddleCompileError: compiledFiddle.error,
|
||||
fiddleDeployError: deployedFiddle.error,
|
||||
fiddleProfileError: profiledFiddle.error,
|
||||
loading: state.loading,
|
||||
lastFiddle: fiddle.data ? fiddle.data.codeToCompile : undefined,
|
||||
lastFiddle: compiledFiddle.data ? compiledFiddle.data.codeToCompile : undefined,
|
||||
fatalError: state.errorMessage
|
||||
};
|
||||
}
|
||||
|
||||
FiddleContainer.propTypes = {
|
||||
fiddle: PropTypes.object,
|
||||
fiddleError: PropTypes.string,
|
||||
compiledFiddle: PropTypes.object,
|
||||
fiddleCompileError: PropTypes.string,
|
||||
fiddleDeployError: PropTypes.string,
|
||||
fiddleProfileError: PropTypes.string,
|
||||
loading: PropTypes.bool,
|
||||
postFiddle: PropTypes.func,
|
||||
postFiddleCompile: PropTypes.func,
|
||||
postFiddleDeploy: PropTypes.func,
|
||||
deployedContracts: PropTypes.string,
|
||||
deployedFiddle: PropTypes.string,
|
||||
profiledFiddle: PropTypes.object,
|
||||
fetchLastFiddle: PropTypes.func,
|
||||
lastFiddle: PropTypes.any,
|
||||
fatalError: PropTypes.string
|
||||
|
@ -261,8 +274,8 @@ FiddleContainer.propTypes = {
|
|||
export default connect(
|
||||
mapStateToProps,
|
||||
{
|
||||
postFiddle: fiddleAction.post,
|
||||
postFiddleDeploy: fiddleDeployAction.post,
|
||||
fetchLastFiddle: fiddleFileAction.request
|
||||
postFiddleCompile: fiddleCompile.post,
|
||||
postFiddleDeploy: fiddleDeploy.post,
|
||||
fetchLastFiddle: fiddleFile.request
|
||||
},
|
||||
)(FiddleContainer);
|
||||
|
|
|
@ -19,8 +19,9 @@ const entitiesDefaultState = {
|
|||
commands: [],
|
||||
messages: [],
|
||||
messageChannels: [],
|
||||
fiddles: [],
|
||||
fiddleCompiles: [],
|
||||
fiddleDeploys: [],
|
||||
fiddleProfiles: [],
|
||||
versions: [],
|
||||
plugins: [],
|
||||
ensRecords: [],
|
||||
|
|
|
@ -119,12 +119,12 @@ export function getMessages(state) {
|
|||
return messages;
|
||||
}
|
||||
|
||||
export function getFiddle(state) {
|
||||
const fiddleCompilation = last(state.entities.fiddles.sort((a, b) => { return (a.timestamp || 0) - (b.timestamp || 0); }));
|
||||
export function getFiddleCompile(state) {
|
||||
const fiddleCompilation = last(state.entities.fiddleCompiles.sort((a, b) => { return (a.timestamp || 0) - (b.timestamp || 0); }));
|
||||
const isNoTempFileError = Boolean(fiddleCompilation && fiddleCompilation.codeToCompile && fiddleCompilation.codeToCompile.error && fiddleCompilation.codeToCompile.error.indexOf('ENOENT') > -1);
|
||||
return {
|
||||
data: fiddleCompilation,
|
||||
error: isNoTempFileError ? undefined : state.errorEntities.fiddles
|
||||
error: isNoTempFileError ? undefined : state.errorEntities.fiddleCompiles
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -135,6 +135,15 @@ export function getFiddleDeploy(state) {
|
|||
};
|
||||
}
|
||||
|
||||
export function getFiddleProfile(state) {
|
||||
const fiddleProfile = last(state.entities.fiddleProfiles.sort((a, b) => { return (a.timestamp || 0) - (b.timestamp || 0); }));
|
||||
const isMissingContractError = Boolean(fiddleProfile && fiddleProfile.compiledContract && fiddleProfile.compiledContract.error && fiddleProfile.compiledContract.error.indexOf('MISSING_PARAM') > -1);
|
||||
return {
|
||||
data: fiddleProfile,
|
||||
error: isMissingContractError ? undefined : state.errorEntities.fiddleProfiles
|
||||
};
|
||||
}
|
||||
|
||||
export function getEnsRecords(state) {
|
||||
return state.entities.ensRecords;
|
||||
}
|
||||
|
|
|
@ -4,9 +4,9 @@ import {eventChannel} from 'redux-saga';
|
|||
import {all, call, fork, put, takeEvery, take} from 'redux-saga/effects';
|
||||
|
||||
const {account, accounts, block, blocks, transaction, transactions, processes, commands, processLogs,
|
||||
contracts, contract, contractProfile, messageSend, versions, plugins, messageListen, fiddle,
|
||||
contracts, contract, contractProfile, messageSend, versions, plugins, messageListen, fiddleCompile,
|
||||
fiddleDeploy, ensRecord, ensRecords, contractLogs, contractFile, contractFunction, contractDeploy,
|
||||
fiddleFile, files, gasOracle} = actions;
|
||||
fiddleFile, files, gasOracle, fiddleProfile} = actions;
|
||||
|
||||
function *doRequest(entity, apiFn, payload) {
|
||||
const {response, error} = yield call(apiFn, payload);
|
||||
|
@ -36,8 +36,9 @@ export const fetchContractFile = doRequest.bind(null, contractFile, api.fetchCon
|
|||
export const fetchLastFiddle = doRequest.bind(null, fiddleFile, api.fetchLastFiddle);
|
||||
export const postContractFunction = doRequest.bind(null, contractFunction, api.postContractFunction);
|
||||
export const postContractDeploy = doRequest.bind(null, contractDeploy, api.postContractDeploy);
|
||||
export const postFiddle = doRequest.bind(null, fiddle, api.postFiddle);
|
||||
export const postFiddleCompile = doRequest.bind(null, fiddleCompile, api.postFiddleCompile);
|
||||
export const postFiddleDeploy = doRequest.bind(null, fiddleDeploy, api.postFiddleDeploy);
|
||||
export const postFiddleProfile = doRequest.bind(null, fiddleProfile, api.postFiddleProfile);
|
||||
export const sendMessage = doRequest.bind(null, messageSend, api.sendMessage);
|
||||
export const fetchEnsRecord = doRequest.bind(null, ensRecord, api.fetchEnsRecord);
|
||||
export const postEnsRecord = doRequest.bind(null, ensRecords, api.postEnsRecord);
|
||||
|
@ -136,18 +137,26 @@ export function *watchListenToMessages() {
|
|||
yield takeEvery(actions.MESSAGE_LISTEN[actions.REQUEST], listenToMessages);
|
||||
}
|
||||
|
||||
export function *watchPostFiddle() {
|
||||
yield takeEvery(actions.FIDDLE[actions.REQUEST], postFiddle);
|
||||
export function *watchPostFiddleCompile() {
|
||||
yield takeEvery(actions.FIDDLE_COMPILE[actions.REQUEST], postFiddleCompile);
|
||||
}
|
||||
|
||||
export function *watchFetchLastFiddleSuccess() {
|
||||
yield takeEvery(actions.FIDDLE_FILE[actions.SUCCESS], postFiddle);
|
||||
yield takeEvery(actions.FIDDLE_FILE[actions.SUCCESS], postFiddleCompile);
|
||||
}
|
||||
|
||||
export function *watchPostFiddleDeploy() {
|
||||
yield takeEvery(actions.FIDDLE_DEPLOY[actions.REQUEST], postFiddleDeploy);
|
||||
}
|
||||
|
||||
export function *watchPostFiddleProfile() {
|
||||
yield takeEvery(actions.FIDDLE_PROFILE[actions.REQUEST], postFiddleProfile);
|
||||
}
|
||||
|
||||
export function *watchPostFiddleCompileSuccess() {
|
||||
yield takeEvery(actions.FIDDLE_COMPILE[actions.SUCCESS], postFiddleProfile);
|
||||
}
|
||||
|
||||
export function *watchFetchFiles() {
|
||||
yield takeEvery(actions.FILES[actions.REQUEST], fetchFiles);
|
||||
}
|
||||
|
@ -254,8 +263,9 @@ export default function *root() {
|
|||
fork(watchSendMessage),
|
||||
fork(watchFetchContract),
|
||||
fork(watchFetchTransaction),
|
||||
fork(watchPostFiddle),
|
||||
fork(watchPostFiddleCompile),
|
||||
fork(watchPostFiddleDeploy),
|
||||
fork(watchPostFiddleProfile),
|
||||
fork(watchFetchLastFiddle),
|
||||
fork(watchFetchLastFiddleSuccess),
|
||||
fork(watchFetchEnsRecord),
|
||||
|
|
|
@ -10,11 +10,10 @@ class GasEstimator {
|
|||
this.fuzzer = new ContractFuzzer(embark);
|
||||
}
|
||||
|
||||
estimateGas(contractName, cb) {
|
||||
fuzzAndEstimateGas(contract, cb){
|
||||
const self = this;
|
||||
let gasMap = {};
|
||||
self.events.request('contracts:contract', contractName, (contract) => {
|
||||
let fuzzMap = self.fuzzer.generateFuzz(3, contract);
|
||||
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"),
|
||||
(abiMethod, gasCb) => {
|
||||
|
@ -66,6 +65,15 @@ class GasEstimator {
|
|||
cb(null, gasMap, null);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
estimateGas(contractNameOrObj, cb) {
|
||||
if(typeof contractNameOrObj === 'object'){
|
||||
return this.fuzzAndEstimateGas(contractNameOrObj, cb);
|
||||
}
|
||||
|
||||
this.events.request('contracts:contract', contractNameOrObj, (contract) => {
|
||||
this.fuzzAndEstimateGas(contract, cb);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,36 +13,39 @@ class Profiler {
|
|||
this.registerApi();
|
||||
}
|
||||
|
||||
profileJSON(contractName, returnCb) {
|
||||
profileContract(contract, returnCb){
|
||||
const self = this;
|
||||
|
||||
let profileObj = {};
|
||||
profileObj.name = contractName;
|
||||
profileObj.name = contract.className;
|
||||
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;
|
||||
}
|
||||
if (!contract || !contract.deployedAddress) {
|
||||
return returnCb("-- couldn't profile " + contract.className + " - it's not deployed or could be an interface");
|
||||
}
|
||||
self.gasEstimator.estimateGas(contract, 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]
|
||||
});
|
||||
profileObj.methods.push({
|
||||
name: methodName,
|
||||
payable: abiMethod.payable,
|
||||
mutability: abiMethod.stateMutability,
|
||||
inputs: abiMethod.inputs || [],
|
||||
outputs: abiMethod.outputs || [],
|
||||
gasEstimates: gastimates && gastimates[methodName]
|
||||
});
|
||||
|
||||
returnCb(null, profileObj);
|
||||
});
|
||||
|
||||
returnCb(null, profileObj);
|
||||
});
|
||||
}
|
||||
|
||||
profileJSON(contractName, returnCb) {
|
||||
this.events.request('contracts:contract', contractName, (contract) => {
|
||||
this.profileContract(contract, returnCb);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -83,16 +86,32 @@ class Profiler {
|
|||
}
|
||||
|
||||
registerApi() {
|
||||
const self = this;
|
||||
|
||||
let plugin = this.plugins.createPlugin('profiler', {});
|
||||
plugin.registerAPICall(
|
||||
this.embark.registerAPICall(
|
||||
'get',
|
||||
'/embark-api/profiler/:contractName',
|
||||
(req, res) => {
|
||||
let contractName = req.params.contractName;
|
||||
|
||||
self.profileJSON(contractName, (err, table) => {
|
||||
this.profileJSON(contractName, (err, table) => {
|
||||
if (err) {
|
||||
return res.send({error: err.message});
|
||||
}
|
||||
res.send(table);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
this.embark.registerAPICall(
|
||||
'post',
|
||||
'/embark-api/profiler/profile',
|
||||
(req, res) => {
|
||||
let contract = req.body.compiledContract;
|
||||
|
||||
if(!contract){
|
||||
res.send({error: 'Body parameter \'compiledContract\' is required (MISSING_PARAM).'});
|
||||
}
|
||||
|
||||
this.profileJSON(contract, (err, table) => {
|
||||
if (err) {
|
||||
return res.send({error: err.message});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue