From 59d3a3be83b9c4594c710a5c8d35367810c5c9f6 Mon Sep 17 00:00:00 2001 From: emizzle Date: Fri, 24 Aug 2018 21:08:05 +1000 Subject: [PATCH] Fiddle deploy integration Fiddle is properly deploying now, except the source code needs to be saved to the filesystem in order to be recalled later. Fixes for handling errors on deploy and compilation. Update contract state UI for determining state / interface / deployed. --- embark-ui/src/actions/index.js | 4 +- embark-ui/src/components/Contract.js | 72 ++++++++++--------- embark-ui/src/components/ContractLayout.js | 4 +- embark-ui/src/components/Contracts.js | 8 ++- embark-ui/src/components/FiddleResults.js | 71 +++++++++++------- .../src/components/FiddleResultsSummary.js | 61 +++++++++------- embark-ui/src/containers/FiddleContainer.js | 23 +++--- embark-ui/src/reducers/selectors.js | 4 +- embark-ui/src/utils/presentation.js | 19 +++++ lib/modules/contracts_manager/index.js | 35 ++++++++- lib/modules/solidity/index.js | 10 ++- 11 files changed, 205 insertions(+), 106 deletions(-) create mode 100644 embark-ui/src/utils/presentation.js diff --git a/embark-ui/src/actions/index.js b/embark-ui/src/actions/index.js index 327c10d83..fd162838b 100644 --- a/embark-ui/src/actions/index.js +++ b/embark-ui/src/actions/index.js @@ -170,14 +170,14 @@ export const ensRecords = { export const FIDDLE = createRequestTypes('FIDDLE'); export const fiddle = { - request: (codeToCompile) => action(FIDDLE[REQUEST], {codeToCompile}), + post: (codeToCompile) => action(FIDDLE[REQUEST], {codeToCompile}), success: (fiddle) => action(FIDDLE[SUCCESS], {fiddles: [fiddle]}), failure: (error) => action(FIDDLE[FAILURE], {error}) }; export const FIDDLE_DEPLOY = createRequestTypes('FIDDLE_DEPLOY'); export const fiddleDeploy = { - request: (compiledCode) => action(FIDDLE_DEPLOY[REQUEST], {compiledCode}), + post: (compiledCode) => action(FIDDLE_DEPLOY[REQUEST], {compiledCode}), success: (response) => { return action(FIDDLE_DEPLOY[SUCCESS], {fiddleDeploys: [response.contractNames]}); }, diff --git a/embark-ui/src/components/Contract.js b/embark-ui/src/components/Contract.js index a5e22d16f..658e6dac0 100644 --- a/embark-ui/src/components/Contract.js +++ b/embark-ui/src/components/Contract.js @@ -6,40 +6,46 @@ import { Card, Table } from "tabler-react"; +import {formatContractForDisplay} from '../utils/presentation'; +import {withRouter} from 'react-router-dom'; -const Contract = ({contract}) => ( - - - - - - - - Name - Address - State - - - - - {(contract.name || contract.className)} - {(contract.address || contract.deployedAddress)} - {contract.deploy} - - -
-
-
-
-
-); - -Contract.propTypes = { - contract: PropTypes.object +const Contract = ({contract, match}) => { + const contractDisplay = formatContractForDisplay(contract); + return ( + + + + + + + + Name + Address + State + + + + + {(contract.name || contract.className)} + {contractDisplay.address} + {contractDisplay.state} + + +
+
+
+
+
+ ); }; -export default Contract; +Contract.propTypes = { + contract: PropTypes.object, + match: PropTypes.object +}; + +export default withRouter(Contract); diff --git a/embark-ui/src/components/ContractLayout.js b/embark-ui/src/components/ContractLayout.js index 930d609c0..13360ac9a 100644 --- a/embark-ui/src/components/ContractLayout.js +++ b/embark-ui/src/components/ContractLayout.js @@ -17,7 +17,7 @@ import ContractSourceContainer from '../containers/ContractSourceContainer'; const ContractLayout = ({match}) => ( - Contract +  
( icon="corner-left-up" RootComponent={NavLink} > - Back to {match.params.contractName} + Overview ( @@ -28,11 +29,12 @@ const Contracts = ({contracts}) => ( { contracts.map((contract) => { + const contractDisplay = formatContractForDisplay(contract); return ( - + {contract.className} - {contract.address || 'Interface or not set to deploy'} - {contract.deploy ? 'Deployed' : 'Not deployed'} + {contractDisplay.address} + {contractDisplay.state} ); }) diff --git a/embark-ui/src/components/FiddleResults.js b/embark-ui/src/components/FiddleResults.js index f3c581743..5bf2460da 100644 --- a/embark-ui/src/components/FiddleResults.js +++ b/embark-ui/src/components/FiddleResults.js @@ -57,29 +57,51 @@ class FiddleResults extends Component { } render() { - const {warnings, errors, fatal, isLoading, deployedContracts} = this.props; - + const {warnings, errors, fatalFiddle, fatalFiddleDeploy, isLoading, deployedContracts} = this.props; + const hasFatal = fatalFiddle || fatalFiddleDeploy; let renderings = []; - if(fatal){ - renderings.push( - - - - ); - } - else if(isLoading){ + if(isLoading){ renderings.push(
{loadingMessage}
); } - else { - if(hasResult && !errors.length){ - renderings.push( - - Compiled - this.props.onDeployClick(e)} /> - - ); - } - if(errors.length) renderings.push( + if(fatalFiddle) { + renderings.push( - {errors.length} error{errors.length > 1 ? "s" : ""} - - ); - if(warnings.length) renderings.push( - - {warnings.length} warning{warnings.length > 1 ? "s" : ""} + Compilation ); } + + if(fatalFiddleDeploy) { + renderings.push( + + Deployment + + ); + } + + if(errors.length) renderings.push( + + {errors.length} error{errors.length > 1 ? "s" : ""} + + ); + if(warnings.length) renderings.push( + + {warnings.length} warning{warnings.length > 1 ? "s" : ""} + + ); + if(hasResult && !errors.length){ + renderings.push( + + Compiled + this.props.onDeployClick(e)} /> + + ); + } + return (
{renderings} @@ -55,7 +63,8 @@ FiddleResultsSummary.propTypes = { isLoading: PropTypes.bool, loadingMessage: PropTypes.string, hasResult: PropTypes.bool, - fatal: PropTypes.string, + fatalFiddle: PropTypes.string, + fatalFiddleDeploy: PropTypes.string, onDeployClick: PropTypes.func }; diff --git a/embark-ui/src/containers/FiddleContainer.js b/embark-ui/src/containers/FiddleContainer.js index 6e9ed8e51..9dc410670 100644 --- a/embark-ui/src/containers/FiddleContainer.js +++ b/embark-ui/src/containers/FiddleContainer.js @@ -82,7 +82,7 @@ class FiddleContainer extends Component { } render() { - const {fiddle, loading, error, deployedContracts} = this.props; + const {fiddle, loading, fiddleError, fiddleDeployError, deployedContracts} = this.props; const {loadingMessage} = this.state; let renderings = []; let warnings = []; @@ -99,7 +99,8 @@ class FiddleContainer extends Component { isLoading={loading} loadingMessage={loadingMessage} hasResult={Boolean(fiddle)} - fatal={error} + fatalFiddle={fiddleError} + fatalFiddleDeploy={fiddleDeployError} onDeployClick={(e) => this._onDeployClick(e)} /> ); - if (fiddle || (this.state.value && error)) { + if (fiddle || (this.state.value && (fiddleError || fiddleDeployError))) { renderings.push( ); @@ -143,23 +145,26 @@ function mapStateToProps(state) { return { fiddle: fiddle.data, deployedContracts: deployedFiddle.data, - error: fiddle.error || deployedFiddle.error, + fiddleError: fiddle.error, + fiddleDeployError: deployedFiddle.error, loading: state.loading }; } FiddleContainer.propTypes = { fiddle: PropTypes.object, - error: PropTypes.string, + fiddleError: PropTypes.string, + fiddleDeployError: PropTypes.string, loading: PropTypes.bool, postFiddle: PropTypes.func, - postFiddleDeploy: PropTypes.func + postFiddleDeploy: PropTypes.func, + deployedContracts: PropTypes.object }; export default connect( mapStateToProps, { - postFiddle: fiddleAction.request, - postFiddleDeploy: fiddleDeployAction.request + postFiddle: fiddleAction.post, + postFiddleDeploy: fiddleDeployAction.post }, )(FiddleContainer); diff --git a/embark-ui/src/reducers/selectors.js b/embark-ui/src/reducers/selectors.js index 847f59ba9..9887a1810 100644 --- a/embark-ui/src/reducers/selectors.js +++ b/embark-ui/src/reducers/selectors.js @@ -107,14 +107,14 @@ export function getMessages(state) { export function getFiddle(state) { return { data: _.last(state.entities.fiddles), - error: _.last(state.errorEntities.fiddles) + error: state.errorEntities.fiddles }; } export function getFiddleDeploy(state) { return { data: _.last(state.entities.fiddleDeploys), - error: _.last(state.errorEntities.fiddleDeploys) + error: state.errorEntities.fiddleDeploys }; } diff --git a/embark-ui/src/utils/presentation.js b/embark-ui/src/utils/presentation.js new file mode 100644 index 000000000..43ff127e4 --- /dev/null +++ b/embark-ui/src/utils/presentation.js @@ -0,0 +1,19 @@ +export function formatContractForDisplay(contract) { + let address = (contract.address || contract.deployedAddress); + let state = 'Deployed'; + let stateColor = 'success'; + if (contract.deploy === false) { + address = 'Interface or set to not deploy'; + state = 'N/A'; + stateColor = 'info'; + } else if (contract.error) { + address = contract.error; + state = 'Error'; + stateColor = 'danger'; + } else if (!address) { + address = '...'; + state = 'Pending'; + stateColor = 'warning'; + } + return {address, state, stateColor}; +} diff --git a/lib/modules/contracts_manager/index.js b/lib/modules/contracts_manager/index.js index 916c45e93..c2fc69c5c 100644 --- a/lib/modules/contracts_manager/index.js +++ b/lib/modules/contracts_manager/index.js @@ -1,6 +1,7 @@ let toposort = require('toposort'); let async = require('async'); const cloneDeep = require('clone-deep'); +const _ = require('lodash'); let utils = require('../../utils/utils.js'); @@ -169,22 +170,44 @@ class ContractsManager { 'post', '/embark-api/contract/deploy', (req, res) => { + this.logger.trace(`POST request /embark-api/contract/deploy:\n ${JSON.stringify(req.body)}`); self.compiledContracts = Object.assign(self.compiledContracts, req.body.compiledContract); + const contractNames = Object.keys(req.body.compiledContract); self.build((err, _mgr) => { - const responseData = {errors: err, contractNames: Object.keys(req.body.compiledContract)}; + if(err){ + return res.send({error: err.message}); + } + // pick the compiled contracts that have been built + const builtContracts = _.pick(self.contracts, contractNames); + + // for each contract, deploy (in parallel) + async.eachOf(builtContracts, (contract, contractName, callback) => { + contract.args = []; /* TODO: override contract.args */ + contract.className = contractName; + self.events.request("deploy:contract", contract, (err) => { + callback(err); + }); + }, (err) => { + let responseData = {}; + if(err){ + responseData.error = err.message; + } + else responseData.result = contractNames; this.logger.trace(`POST response /embark-api/contract/deploy:\n ${JSON.stringify(responseData)}`); res.send(responseData); - }, false); + }); + }, false, false); } ); } - build(done, useContractFiles = true) { + build(done, useContractFiles = true, resetContracts = true) { let self = this; self.contracts = {}; let compilerOptions = {disableOptimizations: this.disableOptimizations}; + if(resetContracts) self.contracts = {}; async.waterfall([ function loadContractFiles(callback) { self.events.request("config:contractsFiles", (contractsFiles) => { @@ -210,6 +233,12 @@ class ContractsManager { }, function prepareContractsFromConfig(callback) { self.events.emit("status", __("Building...")); + + // if we are appending contracts (ie fiddle), we + // don't need to build a contract from config, so + // we can skip this entirely + if(!resetContracts) return callback(); + let className, contract; for (className in self.contractsConfig.contracts) { contract = self.contractsConfig.contracts[className]; diff --git a/lib/modules/solidity/index.js b/lib/modules/solidity/index.js index ced9c980f..ab30cc13c 100644 --- a/lib/modules/solidity/index.js +++ b/lib/modules/solidity/index.js @@ -1,5 +1,6 @@ let async = require('../../utils/async_extend.js'); let SolcW = require('./solcW.js'); +const fs = require('../../core/fs'); class Solidity { @@ -19,8 +20,12 @@ class Solidity { 'post', '/embark-api/contract/compile', (req, res) => { - const input = {'fiddler': {content: req.body.code.replace(/\r\n/g, '\n')}}; + const input = {'fiddle': {content: req.body.code.replace(/\r\n/g, '\n')}}; this.compile_solidity_code(input, {}, true, (errors, compilationResult) => { + // write code to filesystem so we can view the source after page refresh + const filePath = `.embark/fiddles/${Object.keys(compilationResult).join('_')}.sol`; + fs.writeFile(filePath, req.body.code, 'utf8'); // async, do not need to wait + const responseData = {errors: errors, compilationResult: compilationResult}; this.logger.trace(`POST response /embark-api/contract/compile:\n ${JSON.stringify(responseData)}`); res.send(responseData); @@ -122,7 +127,8 @@ class Solidity { let contract = json[contractFile][contractName]; const className = contractName; - const filename = contractFile; + let filename = contractFile; + if(filename === 'fiddle') filename = `.embark/fiddles/${className}.sol`; compiled_object[className] = {}; compiled_object[className].code = contract.evm.bytecode.object;