File Editor
This commit is contained in:
parent
a5a767f059
commit
53fe741e2c
File diff suppressed because it is too large
Load Diff
|
@ -125,6 +125,13 @@ export const contractDeploy = {
|
||||||
failure: (error) => action(CONTRACT_DEPLOY[FAILURE], {error})
|
failure: (error) => action(CONTRACT_DEPLOY[FAILURE], {error})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const CONTRACT_COMPILE = createRequestTypes('CONTRACT_COMPILE');
|
||||||
|
export const contractCompile = {
|
||||||
|
post: (code, name) => action(CONTRACT_COMPILE[REQUEST], {code, name}),
|
||||||
|
success: (result, payload) => action(CONTRACT_COMPILE[SUCCESS], {contractCompiles: [{...result, ...payload}]}),
|
||||||
|
failure: (error) => action(CONTRACT_COMPILE[FAILURE], {error})
|
||||||
|
};
|
||||||
|
|
||||||
export const VERSIONS = createRequestTypes('VERSIONS');
|
export const VERSIONS = createRequestTypes('VERSIONS');
|
||||||
export const versions = {
|
export const versions = {
|
||||||
request: () => action(VERSIONS[REQUEST]),
|
request: () => action(VERSIONS[REQUEST]),
|
||||||
|
@ -168,31 +175,6 @@ export const ensRecords = {
|
||||||
failure: (error) => action(ENS_RECORDS[FAILURE], {error})
|
failure: (error) => action(ENS_RECORDS[FAILURE], {error})
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FIDDLE = createRequestTypes('FIDDLE');
|
|
||||||
export const fiddle = {
|
|
||||||
post: (codeToCompile, timestamp) => action(FIDDLE[REQUEST], {codeToCompile, timestamp}),
|
|
||||||
success: (fiddle, payload) => {
|
|
||||||
return action(FIDDLE[SUCCESS], {fiddles: [{...fiddle, ...payload}]});
|
|
||||||
},
|
|
||||||
failure: (error) => action(FIDDLE[FAILURE], {error})
|
|
||||||
};
|
|
||||||
|
|
||||||
export const FIDDLE_DEPLOY = createRequestTypes('FIDDLE_DEPLOY');
|
|
||||||
export const fiddleDeploy = {
|
|
||||||
post: (compiledCode) => action(FIDDLE_DEPLOY[REQUEST], {compiledCode}),
|
|
||||||
success: (response) => {
|
|
||||||
return action(FIDDLE_DEPLOY[SUCCESS], {fiddleDeploys: response.result});
|
|
||||||
},
|
|
||||||
failure: (error) => action(FIDDLE_DEPLOY[FAILURE], {error})
|
|
||||||
};
|
|
||||||
|
|
||||||
export const FIDDLE_FILE = createRequestTypes('FIDDLE_FILE');
|
|
||||||
export const fiddleFile = {
|
|
||||||
request: () => action(FIDDLE_FILE[REQUEST]),
|
|
||||||
success: (codeToCompile) => action(FIDDLE_FILE[SUCCESS], {codeToCompile}),
|
|
||||||
failure: (error) => action(FIDDLE_FILE[FAILURE], {error})
|
|
||||||
};
|
|
||||||
|
|
||||||
export const FILES = createRequestTypes('FILES');
|
export const FILES = createRequestTypes('FILES');
|
||||||
export const files = {
|
export const files = {
|
||||||
request: () => action(FILES[REQUEST]),
|
request: () => action(FILES[REQUEST]),
|
||||||
|
@ -200,6 +182,43 @@ export const files = {
|
||||||
failure: (error) => action(FILES[FAILURE], {error})
|
failure: (error) => action(FILES[FAILURE], {error})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const FILE = createRequestTypes('FILE');
|
||||||
|
export const file = {
|
||||||
|
request: (file) => action(FILE[REQUEST], file),
|
||||||
|
success: (file) => action(FILE[SUCCESS], file),
|
||||||
|
failure: (error) => action(FILE[FAILURE], {error})
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SAVE_FILE = createRequestTypes('SAVE_FILE');
|
||||||
|
export const saveFile = {
|
||||||
|
request: ({name, path, content}) => {
|
||||||
|
return action(SAVE_FILE[REQUEST], {name, path, content});
|
||||||
|
},
|
||||||
|
success: () => action(SAVE_FILE[SUCCESS]),
|
||||||
|
failure: (error) => action(SAVE_FILE[FAILURE], {error})
|
||||||
|
};
|
||||||
|
|
||||||
|
export const REMOVE_FILE = createRequestTypes('REMOVE_FILE');
|
||||||
|
export const removeFile = {
|
||||||
|
request: ({name, path, content}) => action(REMOVE_FILE[REQUEST], {name, path, content}),
|
||||||
|
success: () => action(REMOVE_FILE[SUCCESS]),
|
||||||
|
failure: (error) => action(REMOVE_FILE[FAILURE], {error})
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CURRENT_FILE = createRequestTypes('CURRENT_FILE');
|
||||||
|
export const currentFile = {
|
||||||
|
request: () => action(CURRENT_FILE[REQUEST]),
|
||||||
|
success: (file) => action(CURRENT_FILE[SUCCESS], {currentFiles: [file]}),
|
||||||
|
failure: () => action(CURRENT_FILE[FAILURE])
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SAVE_CURRENT_FILE = createRequestTypes('SAVE_CURRENT_FILE');
|
||||||
|
export const saveCurrentFile = {
|
||||||
|
request: (file) => action(SAVE_CURRENT_FILE[REQUEST], file),
|
||||||
|
success: (file) => action(SAVE_CURRENT_FILE[SUCCESS], {currentFiles: [file]}),
|
||||||
|
failure: () => action(SAVE_CURRENT_FILE[FAILURE])
|
||||||
|
};
|
||||||
|
|
||||||
export const GAS_ORACLE = createRequestTypes('GAS_ORACLE');
|
export const GAS_ORACLE = createRequestTypes('GAS_ORACLE');
|
||||||
export const gasOracle = {
|
export const gasOracle = {
|
||||||
request: () => action(GAS_ORACLE[REQUEST]),
|
request: () => action(GAS_ORACLE[REQUEST]),
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import {Badge} from 'tabler-react';
|
|
||||||
|
|
||||||
const CompilerError = ({ index, onClick, errorType, row, errorMessage}) => (
|
|
||||||
<a
|
|
||||||
href="#editor"
|
|
||||||
className="list-group-item list-group-item-action"
|
|
||||||
onClick={onClick}
|
|
||||||
key={index}
|
|
||||||
>
|
|
||||||
<Badge color={errorType === "error" ? "danger" : errorType} className="mr-1" key={index}>
|
|
||||||
Line {row}
|
|
||||||
</Badge>
|
|
||||||
{errorMessage}
|
|
||||||
</a>
|
|
||||||
);
|
|
||||||
|
|
||||||
CompilerError.propTypes = {
|
|
||||||
index: PropTypes.number,
|
|
||||||
onClick: PropTypes.func,
|
|
||||||
errorType: PropTypes.string,
|
|
||||||
row: PropTypes.string,
|
|
||||||
errorMessage: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CompilerError;
|
|
|
@ -1,53 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import AceEditor from 'react-ace';
|
|
||||||
import 'brace/mode/javascript';
|
|
||||||
import 'brace/theme/tomorrow_night_blue';
|
|
||||||
import 'ace-mode-solidity/build/remix-ide/mode-solidity';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
class Fiddle extends React.Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.ace = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {onCodeChange, value, errors, warnings} = this.props;
|
|
||||||
const annotations = errors && errors.map((error) => { return error.annotation; }).concat(warnings.map(warning => { return warning.annotation; }));
|
|
||||||
return (
|
|
||||||
<React.Fragment>
|
|
||||||
|
|
||||||
<AceEditor
|
|
||||||
mode="solidity"
|
|
||||||
theme="tomorrow_night_blue"
|
|
||||||
name="fiddle"
|
|
||||||
height="60em"
|
|
||||||
width="100%"
|
|
||||||
onChange={onCodeChange}
|
|
||||||
value={value}
|
|
||||||
showGutter={true}
|
|
||||||
annotations={annotations}
|
|
||||||
ref={(ace) => { this.ace = ace; }}
|
|
||||||
setOptions={{
|
|
||||||
useWorker: false
|
|
||||||
}}
|
|
||||||
editorProps={{
|
|
||||||
$blockScrolling: Infinity,
|
|
||||||
enableLiveAutocompletion:true,
|
|
||||||
highlightSelectedWord: true
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Fiddle.propTypes = {
|
|
||||||
onCodeChange: PropTypes.func,
|
|
||||||
value: PropTypes.string,
|
|
||||||
errors: PropTypes.array,
|
|
||||||
warnings: PropTypes.array
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Fiddle;
|
|
|
@ -1,19 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import {Button} from 'tabler-react';
|
|
||||||
|
|
||||||
const FiddleDeployButton = ({ onDeployClick }) => (
|
|
||||||
<Button
|
|
||||||
color="dark"
|
|
||||||
size="sm"
|
|
||||||
icon="upload-cloud"
|
|
||||||
onClick={onDeployClick}>
|
|
||||||
Deploy
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
|
|
||||||
FiddleDeployButton.propTypes = {
|
|
||||||
onDeployClick: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default FiddleDeployButton;
|
|
|
@ -1,25 +1,26 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {Route, Switch} from 'react-router-dom';
|
|
||||||
import {
|
import {
|
||||||
Page,
|
Page,
|
||||||
Grid
|
Grid
|
||||||
} from "tabler-react";
|
} from "tabler-react";
|
||||||
|
|
||||||
import FiddleContainer from '../containers/FiddleContainer';
|
import TextEditorContainer from '../containers/TextEditorContainer';
|
||||||
import FileExplorerContainer from '../containers/FileExplorerContainer';
|
import FileExplorerContainer from '../containers/FileExplorerContainer';
|
||||||
|
|
||||||
const ExplorerLayout = () => (
|
class FiddleLayout extends React.Component {
|
||||||
<Grid.Row>
|
render() {
|
||||||
|
return (
|
||||||
|
<Grid.Row className="my-5">
|
||||||
<Grid.Col md={3}>
|
<Grid.Col md={3}>
|
||||||
<Page.Title className="my-5">Fiddle</Page.Title>
|
<Page.Title>Fiddle</Page.Title>
|
||||||
<FileExplorerContainer />
|
<FileExplorerContainer />
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
<Grid.Col md={9}>
|
<Grid.Col md={9}>
|
||||||
<Switch>
|
<TextEditorContainer />
|
||||||
<Route exact path="/embark/fiddle/" component={FiddleContainer} />
|
|
||||||
</Switch>
|
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
</Grid.Row>
|
</Grid.Row>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default ExplorerLayout;
|
export default FiddleLayout;
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
const FiddleResults = ({warningsCard, errorsCard, fatalFiddleCard, fatalFiddleDeployCard, deployedContractsCard, fatalErrorCard, forwardedRef}) => (
|
|
||||||
<div ref={forwardedRef}>
|
|
||||||
{fatalErrorCard}
|
|
||||||
{fatalFiddleCard}
|
|
||||||
{fatalFiddleDeployCard}
|
|
||||||
{deployedContractsCard}
|
|
||||||
{errorsCard}
|
|
||||||
{warningsCard}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
FiddleResults.propTypes = {
|
|
||||||
errorsCard: PropTypes.node,
|
|
||||||
warningsCard: PropTypes.node,
|
|
||||||
fatalFiddleCard: PropTypes.node,
|
|
||||||
fatalFiddleDeployCard: PropTypes.node,
|
|
||||||
deployedContractsCard: PropTypes.node,
|
|
||||||
fatalErrorCard: PropTypes.node,
|
|
||||||
forwardedRef: PropTypes.any
|
|
||||||
};
|
|
||||||
|
|
||||||
export default FiddleResults;
|
|
|
@ -1,69 +0,0 @@
|
||||||
import React, {Component} from 'react';
|
|
||||||
import {Badge, Icon, Loader} from 'tabler-react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import FiddleDeployButton from './FiddleDeployButton';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
|
|
||||||
class FiddleResultsSummary extends Component {
|
|
||||||
|
|
||||||
_renderFatal(fatalType, title) {
|
|
||||||
return <a className="badge-link" href={`#fatal-${fatalType}`} onClick={(e) => this.props.onFatalClick(e)}><Badge color="danger"><Icon name="slash" className="mr-1" />{title}</Badge></a>;
|
|
||||||
}
|
|
||||||
|
|
||||||
_renderError(errorType, numErrors) {
|
|
||||||
const color = errorType === 'error' ? 'danger' : 'warning';
|
|
||||||
const clickAction = errorType === 'error' ? this.props.onWarningsClick : this.props.onErrorsClick;
|
|
||||||
return <a className="badge-link" href={`#${errorType}`} onClick={(e) => clickAction(e)}><Badge color={color}>{numErrors} {errorType}{numErrors > 1 ? "s" : ""}</Badge></a>;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {numWarnings, numErrors, isLoading, loadingMessage, isVisible, showDeploy, showFatalFiddle, showFatalFiddleDeploy, showFatalError} = this.props;
|
|
||||||
const classes = classNames("compilation-summary", {
|
|
||||||
'visible': isVisible
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={classes}>
|
|
||||||
{isLoading &&
|
|
||||||
<Loader className="mr-1">
|
|
||||||
<span className="loader-text">{loadingMessage}</span>
|
|
||||||
</Loader>}
|
|
||||||
|
|
||||||
{showFatalError && this._renderFatal("error", "Error")}
|
|
||||||
|
|
||||||
{showFatalFiddle && this._renderFatal("compile", "Compilation")}
|
|
||||||
|
|
||||||
{showFatalFiddleDeploy && this._renderFatal("deploy", "Deployment")}
|
|
||||||
|
|
||||||
{numErrors > 0 && this._renderError("error", numErrors)}
|
|
||||||
|
|
||||||
{numWarnings > 0 && this._renderError("warning", numWarnings)}
|
|
||||||
|
|
||||||
{showDeploy &&
|
|
||||||
<React.Fragment key="success">
|
|
||||||
<Badge className="badge-link" color="success">Compiled</Badge>
|
|
||||||
<FiddleDeployButton onDeployClick={(e) => this.props.onDeployClick(e)} />
|
|
||||||
</React.Fragment>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FiddleResultsSummary.propTypes = {
|
|
||||||
isLoading: PropTypes.bool,
|
|
||||||
loadingMessage: PropTypes.string,
|
|
||||||
isVisible: PropTypes.bool,
|
|
||||||
showDeploy: PropTypes.bool,
|
|
||||||
showFatalError: PropTypes.bool,
|
|
||||||
showFatalFiddle: PropTypes.bool,
|
|
||||||
showFatalFiddleDeploy: PropTypes.bool,
|
|
||||||
onDeployClick: PropTypes.func,
|
|
||||||
numErrors: PropTypes.number,
|
|
||||||
numWarnings: PropTypes.number,
|
|
||||||
onWarningsClick: PropTypes.func.isRequired,
|
|
||||||
onErrorsClick: PropTypes.func.isRequired,
|
|
||||||
onFatalClick: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default FiddleResultsSummary;
|
|
|
@ -29,26 +29,35 @@ class FileExplorer extends React.Component {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {};
|
this.state = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
onToggle(node, toggled){
|
onToggle(node, toggled){
|
||||||
|
let oldNode = this.state.cursor;
|
||||||
|
if(oldNode) {
|
||||||
|
oldNode.active = false;
|
||||||
|
}
|
||||||
node.active = true;
|
node.active = true;
|
||||||
if(node.children) {
|
if(node.children) {
|
||||||
node.toggled = toggled;
|
node.toggled = toggled;
|
||||||
|
} else {
|
||||||
|
this.props.fetchFile(node);
|
||||||
}
|
}
|
||||||
this.setState({ cursor: node });
|
this.setState({ cursor: node });
|
||||||
}
|
}
|
||||||
|
|
||||||
render(){
|
render(){
|
||||||
return (
|
return (
|
||||||
<Treebeard
|
<Treebeard
|
||||||
data={this.props.files}
|
data={this.props.files}
|
||||||
decorators={decorators}
|
decorators={decorators}
|
||||||
onToggle={(node, toggled) => this.onToggle(node, toggled)}
|
onToggle={this.onToggle.bind(this)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FileExplorer.propTypes = {
|
FileExplorer.propTypes = {
|
||||||
files: PropTypes.array
|
files: PropTypes.array,
|
||||||
|
fetchFile: PropTypes.func
|
||||||
};
|
};
|
||||||
|
|
||||||
export default FileExplorer;
|
export default FileExplorer;
|
||||||
|
|
|
@ -1,80 +0,0 @@
|
||||||
/* eslint {jsx-a11y/anchor-has-content:"off"} */
|
|
||||||
import React from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import {Card, Icon, Dimmer} from 'tabler-react';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
|
|
||||||
class LoadingCardWithIcon extends React.Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
errorsCollapsed: false,
|
|
||||||
warningsCollapsed: false,
|
|
||||||
errorsFullscreen: false,
|
|
||||||
warningsFullscreen: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
_onToggle(e, type) {
|
|
||||||
const className = e.currentTarget.parentElement.className.replace('card-options', '').replace(' ', '');
|
|
||||||
const updatedState = {};
|
|
||||||
updatedState[className + type] = !(this.state[className + type]);
|
|
||||||
this.setState(updatedState);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
color,
|
|
||||||
className,
|
|
||||||
iconName,
|
|
||||||
headerTitle,
|
|
||||||
isLoading,
|
|
||||||
body,
|
|
||||||
showCardOptions = true,
|
|
||||||
cardOptionsClassName} = this.props;
|
|
||||||
|
|
||||||
const isFullscreen = Boolean(this.state[cardOptionsClassName + 'Fullscreen']);
|
|
||||||
const classes = classNames(className, {
|
|
||||||
'card-fullscreen': showCardOptions && Boolean(this.state[cardOptionsClassName + 'Fullscreen']),
|
|
||||||
'card-collapsed': showCardOptions && Boolean(this.state[cardOptionsClassName + 'Collapsed']) && !isFullscreen
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Card
|
|
||||||
statusColor={color}
|
|
||||||
statusSide="true"
|
|
||||||
className={classes}
|
|
||||||
isCollapsible={showCardOptions}
|
|
||||||
isFullscreenable={showCardOptions}>
|
|
||||||
<Card.Header>
|
|
||||||
<Card.Title color={color}>{iconName && <Icon name={iconName} className="mr-1" />}{headerTitle}</Card.Title>
|
|
||||||
{showCardOptions &&
|
|
||||||
<Card.Options className={cardOptionsClassName}>
|
|
||||||
<Card.OptionsItem key="0" type="collapse" icon="chevron-up" onClick={(e) => this._onToggle(e, 'Collapsed')} />
|
|
||||||
<Card.OptionsItem key="1" type="fullscreen" icon="maximize" onClick={(e) => this._onToggle(e, 'Fullscreen')} />
|
|
||||||
</Card.Options>
|
|
||||||
}
|
|
||||||
</Card.Header>
|
|
||||||
<Card.Body>
|
|
||||||
<Dimmer active={isLoading ? "active" : ""} loader>
|
|
||||||
{body}
|
|
||||||
</Dimmer>
|
|
||||||
</Card.Body>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LoadingCardWithIcon.propTypes = {
|
|
||||||
color: PropTypes.string.isRequired,
|
|
||||||
className: PropTypes.string.isRequired,
|
|
||||||
iconName: PropTypes.string,
|
|
||||||
headerTitle: PropTypes.any,
|
|
||||||
isLoading: PropTypes.bool.isRequired,
|
|
||||||
body: PropTypes.node,
|
|
||||||
showCardOptions: PropTypes.bool,
|
|
||||||
onOptionToggle: PropTypes.func,
|
|
||||||
cardOptionsClassName: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
export default LoadingCardWithIcon;
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
import React from 'react';
|
||||||
|
import AceEditor from 'react-ace';
|
||||||
|
import 'brace/mode/javascript';
|
||||||
|
import 'brace/theme/tomorrow_night_blue';
|
||||||
|
import 'ace-mode-solidity/build/remix-ide/mode-solidity';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
class TextEditor extends React.Component {
|
||||||
|
extractRowCol(errorMessage) {
|
||||||
|
const errorSplit = errorMessage.split(':');
|
||||||
|
if (errorSplit.length >= 3) {
|
||||||
|
return {row: errorSplit[1], col: errorSplit[2]};
|
||||||
|
}
|
||||||
|
return {row: 0, col: 0};
|
||||||
|
}
|
||||||
|
|
||||||
|
annotations() {
|
||||||
|
const {errors, warnings} = this.props.contractCompile;
|
||||||
|
return [].concat(errors).concat(warnings).filter((e) => e).map((e) => {
|
||||||
|
const rowCol = this.extractRowCol(e.formattedMessage);
|
||||||
|
return Object.assign({}, {
|
||||||
|
row: rowCol.row - 1,
|
||||||
|
column: rowCol.col - 1,
|
||||||
|
text: e.formattedMessage,
|
||||||
|
type: e.severity
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<AceEditor
|
||||||
|
mode="solidity"
|
||||||
|
theme="tomorrow_night_blue"
|
||||||
|
name="fiddle"
|
||||||
|
height="60em"
|
||||||
|
width="100%"
|
||||||
|
onChange={this.props.onFileContentChange}
|
||||||
|
value={this.props.value}
|
||||||
|
showGutter={true}
|
||||||
|
annotations={this.annotations()}
|
||||||
|
setOptions={{
|
||||||
|
useWorker: false
|
||||||
|
}}
|
||||||
|
editorProps={{
|
||||||
|
$blockScrolling: Infinity,
|
||||||
|
enableLiveAutocompletion:true,
|
||||||
|
highlightSelectedWord: true
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TextEditor.propTypes = {
|
||||||
|
onFileContentChange: PropTypes.func,
|
||||||
|
value: PropTypes.string,
|
||||||
|
contractCompile: PropTypes.object
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TextEditor;
|
|
@ -0,0 +1,29 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { NavLink } from 'react-router-dom';
|
||||||
|
import {Card, Icon, Button} from 'tabler-react';
|
||||||
|
|
||||||
|
const TextEditorContractDeploy = (props) => (
|
||||||
|
<Card statusColor="success"
|
||||||
|
statusSide
|
||||||
|
className="success-card">
|
||||||
|
<Card.Header>
|
||||||
|
<Card.Title color="success">
|
||||||
|
<Icon name="check" className="mr-1" />
|
||||||
|
Deploy Contract
|
||||||
|
</Card.Title>
|
||||||
|
</Card.Header>
|
||||||
|
<Card.Body>
|
||||||
|
<Button to={`/embark/contracts/${Object.keys(props.result)[0]}/deployment`}
|
||||||
|
RootComponent={NavLink}>
|
||||||
|
Deploy my contract(s)
|
||||||
|
</Button>
|
||||||
|
</Card.Body>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
|
||||||
|
TextEditorContractDeploy.propTypes = {
|
||||||
|
result: PropTypes.object
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TextEditorContractDeploy;
|
|
@ -0,0 +1,27 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import {Card, Icon, List} from 'tabler-react';
|
||||||
|
|
||||||
|
const TextEditorContractErrors = (props) => (
|
||||||
|
<Card statusColor="danger"
|
||||||
|
statusSide
|
||||||
|
className="errors-card">
|
||||||
|
<Card.Header>
|
||||||
|
<Card.Title color="danger">
|
||||||
|
<Icon name="alert-circle" className="mr-1" />
|
||||||
|
Failed to compile
|
||||||
|
</Card.Title>
|
||||||
|
</Card.Header>
|
||||||
|
<Card.Body>
|
||||||
|
<List.Group>
|
||||||
|
{props.errors.map((error, index) => <List.GroupItem key={index}>{error.formattedMessage}</List.GroupItem>)}
|
||||||
|
</List.Group>
|
||||||
|
</Card.Body>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
|
||||||
|
TextEditorContractErrors.propTypes = {
|
||||||
|
errors: PropTypes.array
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TextEditorContractErrors;
|
|
@ -0,0 +1,31 @@
|
||||||
|
import React, {Component} from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import {Grid, Badge, Icon} from 'tabler-react';
|
||||||
|
import TextEditorToolbar from './TextEditorToolbar';
|
||||||
|
|
||||||
|
class TextEditorContractToolbar extends Component {
|
||||||
|
render(){
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<TextEditorToolbar {...this.props} />
|
||||||
|
<Grid.Col md={6} className="text-right">
|
||||||
|
{this.props.compilingContract &&
|
||||||
|
<Badge color="warning"><Icon name="slash" className="mr-1" />compiling</Badge>}
|
||||||
|
{!this.props.compilingContract && this.props.contractCompile.result &&
|
||||||
|
<Badge color="success"><Icon name="check" className="mr-1" />compiled</Badge>}
|
||||||
|
</Grid.Col>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TextEditorContractToolbar.propTypes = {
|
||||||
|
currentFile: PropTypes.object,
|
||||||
|
contractCompile: PropTypes.object,
|
||||||
|
compilingContract: PropTypes.bool,
|
||||||
|
deploy: PropTypes.func,
|
||||||
|
save: PropTypes.func,
|
||||||
|
remove: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TextEditorContractToolbar;
|
|
@ -0,0 +1,27 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import {Card, Icon, List} from 'tabler-react';
|
||||||
|
|
||||||
|
const TextEditorContractWarnings = (props) => (
|
||||||
|
<Card statusColor="warning"
|
||||||
|
statusSide
|
||||||
|
className="warnings-card">
|
||||||
|
<Card.Header>
|
||||||
|
<Card.Title color="warning">
|
||||||
|
<Icon name="alert-triangle" className="mr-1" />
|
||||||
|
Warning during compilation
|
||||||
|
</Card.Title>
|
||||||
|
</Card.Header>
|
||||||
|
<Card.Body>
|
||||||
|
<List.Group>
|
||||||
|
{props.warnings.map((warning, index) => <List.GroupItem key={index}>{warning.formattedMessage}</List.GroupItem>)}
|
||||||
|
</List.Group>
|
||||||
|
</Card.Body>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
|
||||||
|
TextEditorContractWarnings.propTypes = {
|
||||||
|
warnings: PropTypes.array
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TextEditorContractWarnings;
|
|
@ -0,0 +1,21 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import {Grid, Button} from 'tabler-react';
|
||||||
|
|
||||||
|
const TextEditorToolbar = (props) => (
|
||||||
|
<Grid.Col md={6}>
|
||||||
|
<strong>{props.currentFile.name}</strong>
|
||||||
|
<span className="mx-2">|</span>
|
||||||
|
<Button color="green" size="sm" icon="save" onClick={props.save}>Save</Button>
|
||||||
|
<span className="mx-2">|</span>
|
||||||
|
<Button color="red" size="sm" icon="delete" onClick={props.remove}>Delete</Button>
|
||||||
|
</Grid.Col>
|
||||||
|
);
|
||||||
|
|
||||||
|
TextEditorToolbar.propTypes = {
|
||||||
|
currentFile: PropTypes.object,
|
||||||
|
save: PropTypes.func,
|
||||||
|
remove: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TextEditorToolbar;
|
|
@ -4,21 +4,21 @@ import PropTypes from 'prop-types';
|
||||||
import {withRouter} from 'react-router-dom';
|
import {withRouter} from 'react-router-dom';
|
||||||
import {Page} from "tabler-react";
|
import {Page} from "tabler-react";
|
||||||
|
|
||||||
import {contractFile as contractFileAction} from '../actions';
|
import {file as FileAction} from '../actions';
|
||||||
import DataWrapper from "../components/DataWrapper";
|
import DataWrapper from "../components/DataWrapper";
|
||||||
import Fiddle from "../components/Fiddle";
|
import TextEditor from "../components/TextEditor";
|
||||||
import {getContract, getContractFile} from "../reducers/selectors";
|
import {getContract, getCurrentFile} from "../reducers/selectors";
|
||||||
|
|
||||||
class ContractSourceContainer extends Component {
|
class ContractSourceContainer extends Component {
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.props.fetchContractFile(this.props.contract.filename);
|
this.props.fetchFile({path: this.props.contract.path});
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Page.Content title={`${this.props.contract.className} Source`}>
|
<Page.Content title={`${this.props.contract.className} Source`}>
|
||||||
<DataWrapper shouldRender={this.props.contractFile !== undefined } {...this.props} render={({contractFile}) => (
|
<DataWrapper shouldRender={this.props.file !== undefined } {...this.props} render={({file}) => (
|
||||||
<Fiddle value={contractFile.source} />
|
<TextEditor value={file.content} contractCompile={{}} />
|
||||||
)} />
|
)} />
|
||||||
</Page.Content>
|
</Page.Content>
|
||||||
);
|
);
|
||||||
|
@ -27,11 +27,11 @@ class ContractSourceContainer extends Component {
|
||||||
|
|
||||||
function mapStateToProps(state, props) {
|
function mapStateToProps(state, props) {
|
||||||
const contract = getContract(state, props.match.params.contractName);
|
const contract = getContract(state, props.match.params.contractName);
|
||||||
const contractFile = getContractFile(state, contract.filename);
|
const file = getCurrentFile(state);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
contract,
|
contract,
|
||||||
contractFile,
|
file,
|
||||||
error: state.errorMessage,
|
error: state.errorMessage,
|
||||||
loading: state.loading
|
loading: state.loading
|
||||||
};
|
};
|
||||||
|
@ -40,14 +40,14 @@ function mapStateToProps(state, props) {
|
||||||
ContractSourceContainer.propTypes = {
|
ContractSourceContainer.propTypes = {
|
||||||
match: PropTypes.object,
|
match: PropTypes.object,
|
||||||
contract: PropTypes.object,
|
contract: PropTypes.object,
|
||||||
contractFile: PropTypes.object,
|
file: PropTypes.object,
|
||||||
fetchContractFile: PropTypes.func,
|
fetchFile: PropTypes.func,
|
||||||
error: PropTypes.string
|
error: PropTypes.string
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withRouter(connect(
|
export default withRouter(connect(
|
||||||
mapStateToProps,
|
mapStateToProps,
|
||||||
{
|
{
|
||||||
fetchContractFile: contractFileAction.request
|
fetchFile: FileAction.request
|
||||||
}
|
}
|
||||||
)(ContractSourceContainer));
|
)(ContractSourceContainer));
|
||||||
|
|
|
@ -1,261 +0,0 @@
|
||||||
/* eslint multiline-ternary: "off" */
|
|
||||||
/* eslint operator-linebreak: "off" */
|
|
||||||
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
|
|
||||||
} 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 CompilerError from "../components/CompilerError";
|
|
||||||
import {List, Badge, Button} from 'tabler-react';
|
|
||||||
import {NavLink} from 'react-router-dom';
|
|
||||||
import LoadingCardWithIcon from '../components/LoadingCardWithIcon';
|
|
||||||
import {hashCode} from '../utils/utils';
|
|
||||||
|
|
||||||
class FiddleContainer extends Component {
|
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
value: undefined,
|
|
||||||
loadingMessage: 'Loading...',
|
|
||||||
readOnly: true
|
|
||||||
};
|
|
||||||
this.compileTimeout = null;
|
|
||||||
this.ace = null;
|
|
||||||
this.editor = null;
|
|
||||||
this.warningsCardRef = null;
|
|
||||||
this.errorsCardRef = null;
|
|
||||||
this.fatalCardRef = null;
|
|
||||||
this.deployedCardRef = null;
|
|
||||||
this.fiddleResultsRef = React.createRef();
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.setState({loadingMessage: 'Loading saved state...'});
|
|
||||||
this.props.fetchLastFiddle();
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
|
||||||
const {lastFiddle} = this.props;
|
|
||||||
if (this.state.value === '' && prevProps.lastFiddle === lastFiddle) return;
|
|
||||||
if ((!this.state.value && lastFiddle && !lastFiddle.error) && this.state.value !== lastFiddle) {
|
|
||||||
this._onCodeChange(lastFiddle, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_getRowCol(errorMessage) {
|
|
||||||
const errorSplit = errorMessage.split(':');
|
|
||||||
if (errorSplit.length >= 3) {
|
|
||||||
return {row: errorSplit[1], col: errorSplit[2]};
|
|
||||||
}
|
|
||||||
return {row: 0, col: 0};
|
|
||||||
}
|
|
||||||
|
|
||||||
_onCodeChange(newValue, immediate = false) {
|
|
||||||
this.setState({readOnly: false, value: newValue});
|
|
||||||
if (this.compileTimeout) clearTimeout(this.compileTimeout);
|
|
||||||
this.compileTimeout = setTimeout(() => {
|
|
||||||
this.setState({loadingMessage: 'Compiling...'});
|
|
||||||
this.props.postFiddle(newValue, Date.now());
|
|
||||||
}, immediate ? 0 : 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onErrorClick(e, annotation) {
|
|
||||||
e.preventDefault();
|
|
||||||
this.editor.gotoLine(annotation.row + 1);
|
|
||||||
scrollToComponent(this.ace);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onErrorSummaryClick(e, refName) {
|
|
||||||
scrollToComponent(this[refName]);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onDeployClick(_e) {
|
|
||||||
this.setState({loadingMessage: 'Deploying...'});
|
|
||||||
this.props.postFiddleDeploy(this.props.fiddle.compilationResult);
|
|
||||||
scrollToComponent(this.deployedCardRef || this.fiddleResultsRef.current); // deployedCardRef null on first Deploy click
|
|
||||||
}
|
|
||||||
|
|
||||||
_renderErrors(errors, errorType) {
|
|
||||||
return errors.reduce(
|
|
||||||
(errors, error, index) => {
|
|
||||||
if (error.severity === errorType) {
|
|
||||||
const errorRowCol = this._getRowCol(error.formattedMessage);
|
|
||||||
const annotation = Object.assign({}, {
|
|
||||||
row: errorRowCol.row - 1, // must be 0 based
|
|
||||||
column: errorRowCol.col - 1, // must be 0 based
|
|
||||||
text: error.formattedMessage, // text to show in tooltip
|
|
||||||
type: error.severity // "error"|"warning"|"info"
|
|
||||||
});
|
|
||||||
errors.push({
|
|
||||||
solcError: error,
|
|
||||||
node:
|
|
||||||
<CompilerError
|
|
||||||
onClick={(e) => { this._onErrorClick(e, annotation); }}
|
|
||||||
key={`${errorType}_${index}`}
|
|
||||||
index={index}
|
|
||||||
errorType={errorType}
|
|
||||||
row={errorRowCol.row}
|
|
||||||
errorMessage={error.formattedMessage} />,
|
|
||||||
annotation: annotation
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return errors;
|
|
||||||
}, []);
|
|
||||||
}
|
|
||||||
|
|
||||||
_renderErrorsCard(errors, errorType) {
|
|
||||||
const color = (errorType === "error" ? "danger" : errorType);
|
|
||||||
|
|
||||||
return (Boolean(errors.length) && <LoadingCardWithIcon
|
|
||||||
anchorId={errorType + "s"}
|
|
||||||
color={color}
|
|
||||||
className={errorType + "s-card "}
|
|
||||||
key={errorType + "s-card"}
|
|
||||||
showCardOptions={true}
|
|
||||||
isLoading={this.props.loading}
|
|
||||||
cardOptionsClassName={errorType + "s"}
|
|
||||||
body={
|
|
||||||
<List.Group>
|
|
||||||
{errors.map(error => { return error.node; })}
|
|
||||||
</List.Group>
|
|
||||||
}
|
|
||||||
headerTitle={
|
|
||||||
<React.Fragment>
|
|
||||||
<span className="mr-1">{errorType + "s"}</span><Badge color={color}>{errors.length}</Badge>
|
|
||||||
</React.Fragment>
|
|
||||||
}
|
|
||||||
ref={cardRef => { this[errorType + "sCardRef"] = cardRef; }}
|
|
||||||
/>);
|
|
||||||
}
|
|
||||||
|
|
||||||
_renderSuccessCard(title, body) {
|
|
||||||
return this._renderLoadingCard("success", "success-card", "check", title, body, (cardRef) => {
|
|
||||||
this.deployedCardRef = cardRef;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_renderFatalCard(title, body) {
|
|
||||||
return body && this._renderLoadingCard("danger", "fatal-card", "slash", title, body, (cardRef) => {
|
|
||||||
this.fatalCardRef = cardRef;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_renderLoadingCard(color, className, iconName, headerTitle, body, refCb) {
|
|
||||||
return (<LoadingCardWithIcon
|
|
||||||
color={color}
|
|
||||||
className={className}
|
|
||||||
iconName={iconName}
|
|
||||||
showCardOptions={false}
|
|
||||||
isLoading={this.props.loading}
|
|
||||||
body={body}
|
|
||||||
headerTitle={headerTitle}
|
|
||||||
key={hashCode([className, iconName, headerTitle].join(''))}
|
|
||||||
ref={refCb}
|
|
||||||
/>);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {fiddle, loading, fiddleError, fiddleDeployError, deployedContracts, 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");
|
|
||||||
}
|
|
||||||
const hasResult = Boolean(fiddle);
|
|
||||||
return (
|
|
||||||
<React.Fragment>
|
|
||||||
<h1 className="page-title">Fiddle</h1>
|
|
||||||
<p>Play around with contract code and deploy against your running node.</p>
|
|
||||||
<FiddleResultsSummary
|
|
||||||
numErrors={errors.length}
|
|
||||||
numWarnings={warnings.length}
|
|
||||||
isLoading={loading}
|
|
||||||
loadingMessage={loadingMessage}
|
|
||||||
showFatalError={Boolean(fatalError)}
|
|
||||||
showFatalFiddle={Boolean(fiddleError)}
|
|
||||||
showFatalFiddleDeploy={Boolean(fiddleDeployError)}
|
|
||||||
onDeployClick={(e) => this._onDeployClick(e)}
|
|
||||||
isVisible={Boolean(fatalError || hasResult || loading)}
|
|
||||||
showDeploy={hasResult && Boolean(fiddle.compilationResult)}
|
|
||||||
onWarningsClick={(e) => this._onErrorSummaryClick(e, "errorsCardRef")}
|
|
||||||
onErrorsClick={(e) => this._onErrorSummaryClick(e, "warningsCardRef")}
|
|
||||||
onFatalClick={(e) => this._onErrorSummaryClick(e, "fatalCardRef")}
|
|
||||||
/>
|
|
||||||
<Fiddle
|
|
||||||
value={value}
|
|
||||||
readOnly={readOnly}
|
|
||||||
onCodeChange={(n) => this._onCodeChange(n)}
|
|
||||||
errors={errors}
|
|
||||||
warnings={warnings}
|
|
||||||
ref={(fiddle) => {
|
|
||||||
if (fiddle) {
|
|
||||||
this.editor = fiddle.ace.editor;
|
|
||||||
this.ace = fiddle.ace;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<FiddleResults
|
|
||||||
key="results"
|
|
||||||
errorsCard={this._renderErrorsCard(errors, "error")}
|
|
||||||
warningsCard={this._renderErrorsCard(warnings, "warning")}
|
|
||||||
fatalErrorCard={this._renderFatalCard("Fatal error", fatalError)}
|
|
||||||
fatalFiddleCard={this._renderFatalCard("Failed to compile", fiddleError)}
|
|
||||||
fatalFiddleDeployCard={this._renderFatalCard("Failed to deploy", fiddleDeployError)}
|
|
||||||
deployedContractsCard={deployedContracts && this._renderSuccessCard("Contract(s) deployed!",
|
|
||||||
<Button
|
|
||||||
to={`/embark/contracts/${deployedContracts}/overview`}
|
|
||||||
RootComponent={NavLink}
|
|
||||||
>Play with my contract(s)</Button>
|
|
||||||
)}
|
|
||||||
forwardedRef={this.fiddleResultsRef}
|
|
||||||
/>
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function mapStateToProps(state) {
|
|
||||||
const fiddle = getFiddle(state);
|
|
||||||
const deployedFiddle = getFiddleDeploy(state);
|
|
||||||
return {
|
|
||||||
fiddle: fiddle.data,
|
|
||||||
deployedContracts: deployedFiddle.data,
|
|
||||||
fiddleError: fiddle.error,
|
|
||||||
fiddleDeployError: deployedFiddle.error,
|
|
||||||
loading: state.loading,
|
|
||||||
lastFiddle: fiddle.data ? fiddle.data.codeToCompile : undefined,
|
|
||||||
fatalError: state.errorMessage
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
FiddleContainer.propTypes = {
|
|
||||||
fiddle: PropTypes.object,
|
|
||||||
fiddleError: PropTypes.string,
|
|
||||||
fiddleDeployError: PropTypes.string,
|
|
||||||
loading: PropTypes.bool,
|
|
||||||
postFiddle: PropTypes.func,
|
|
||||||
postFiddleDeploy: PropTypes.func,
|
|
||||||
deployedContracts: PropTypes.string,
|
|
||||||
fetchLastFiddle: PropTypes.func,
|
|
||||||
lastFiddle: PropTypes.any,
|
|
||||||
fatalError: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(
|
|
||||||
mapStateToProps,
|
|
||||||
{
|
|
||||||
postFiddle: fiddleAction.post,
|
|
||||||
postFiddleDeploy: fiddleDeployAction.post,
|
|
||||||
fetchLastFiddle: fiddleFileAction.request
|
|
||||||
},
|
|
||||||
)(FiddleContainer);
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, {Component} from 'react';
|
import React, {Component} from 'react';
|
||||||
import {connect} from 'react-redux';
|
import {connect} from 'react-redux';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import {files as filesAction} from "../actions";
|
import {files as filesAction, file as fileAction} from "../actions";
|
||||||
|
|
||||||
import FileExplorer from '../components/FileExplorer';
|
import FileExplorer from '../components/FileExplorer';
|
||||||
import DataWrapper from "../components/DataWrapper";
|
import DataWrapper from "../components/DataWrapper";
|
||||||
|
@ -14,8 +14,8 @@ class FileExplorerContainer extends Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<DataWrapper shouldRender={this.props.files.length > 0} {...this.props} render={({files}) => (
|
<DataWrapper shouldRender={this.props.files.length > 0} {...this.props} render={({files, fetchFile}) => (
|
||||||
<FileExplorer files={files} />
|
<FileExplorer files={files} fetchFile={fetchFile} />
|
||||||
)} />
|
)} />
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -27,11 +27,13 @@ function mapStateToProps(state) {
|
||||||
|
|
||||||
FileExplorerContainer.propTypes = {
|
FileExplorerContainer.propTypes = {
|
||||||
files: PropTypes.array,
|
files: PropTypes.array,
|
||||||
fetchFiles: PropTypes.func
|
fetchFiles: PropTypes.func,
|
||||||
|
fetchFile: PropTypes.func
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
mapStateToProps,{
|
mapStateToProps,{
|
||||||
fetchFiles: filesAction.request
|
fetchFiles: filesAction.request,
|
||||||
|
fetchFile: fileAction.request
|
||||||
}
|
}
|
||||||
)(FileExplorerContainer);
|
)(FileExplorerContainer);
|
||||||
|
|
|
@ -0,0 +1,152 @@
|
||||||
|
/* eslint multiline-ternary: "off" */
|
||||||
|
/* eslint operator-linebreak: "off" */
|
||||||
|
import React, {Component} from 'react';
|
||||||
|
import {connect} from 'react-redux';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import {Grid} from 'tabler-react';
|
||||||
|
import TextEditor from '../components/TextEditor';
|
||||||
|
import TextEditorContractErrors from '../components/TextEditorContractErrors';
|
||||||
|
import TextEditorContractWarnings from '../components/TextEditorContractWarnings';
|
||||||
|
import TextEditorContractToolbar from '../components/TextEditorContractToolbar';
|
||||||
|
import TextEditorContractDeploy from '../components/TextEditorContractDeploy';
|
||||||
|
import TextEditorToolbar from '../components/TextEditorToolbar';
|
||||||
|
import {
|
||||||
|
currentFile as currentFileAction,
|
||||||
|
saveCurrentFile as saveCurrentFileAction,
|
||||||
|
saveFile as saveFileAction,
|
||||||
|
removeFile as removeFileAction,
|
||||||
|
contractCompile as contractCompileAction,
|
||||||
|
contractDeploy as contractDeployAction
|
||||||
|
} from '../actions';
|
||||||
|
import {getCurrentFile, getContractCompile} from '../reducers/selectors';
|
||||||
|
|
||||||
|
const DEFAULT_FILE = {name: 'newContract.sol', content: ''};
|
||||||
|
|
||||||
|
class TextEditorContainer extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {currentFile: this.props.currentFile};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
if(this.props.currentFile.content === '') {
|
||||||
|
this.props.fetchCurrentFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps) {
|
||||||
|
if(this.props.currentFile.path !== prevProps.currentFile.path) {
|
||||||
|
this.setState({currentFile: this.props.currentFile});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isContract() {
|
||||||
|
return this.state.currentFile.name.endsWith('.sol');
|
||||||
|
}
|
||||||
|
|
||||||
|
onFileContentChange(newContent) {
|
||||||
|
const newCurrentFile = this.state.currentFile;
|
||||||
|
newCurrentFile.content = newContent;
|
||||||
|
this.setState({currentFile: newCurrentFile});
|
||||||
|
if (!this.isContract()) return;
|
||||||
|
|
||||||
|
this.compileTimeout = setTimeout(() => {
|
||||||
|
this.props.compileContract(newContent, this.state.currentFile.name);
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
save() {
|
||||||
|
this.props.saveFile(this.state.currentFile);
|
||||||
|
this.props.saveCurrentFile(this.state.currentFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
remove() {
|
||||||
|
this.props.removeFile(this.state.currentFile);
|
||||||
|
this.setState({currentFile: DEFAULT_FILE});
|
||||||
|
}
|
||||||
|
|
||||||
|
renderContractFooter() {
|
||||||
|
if (!this.isContract()) return <React.Fragment />;
|
||||||
|
let components = [];
|
||||||
|
|
||||||
|
const {errors, warnings, result} = this.props.contractCompile;
|
||||||
|
if (errors && errors.length > 0) {
|
||||||
|
components.push(<TextEditorContractErrors key={1} errors={errors} />);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (warnings && warnings.length > 0) {
|
||||||
|
components.push(<TextEditorContractWarnings key={2} warnings={warnings} />);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
components.push(<TextEditorContractDeploy key={3} result={result}/>);
|
||||||
|
}
|
||||||
|
return <React.Fragment>{components}</React.Fragment>;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderToolbar(){
|
||||||
|
if (this.isContract()) {
|
||||||
|
return <TextEditorContractToolbar currentFile={this.state.currentFile}
|
||||||
|
contractCompile={this.props.contractCompile}
|
||||||
|
compilingContract={this.props.compilingContract}
|
||||||
|
save={() => this.save()}
|
||||||
|
remove={() => this.remove()} />;
|
||||||
|
}
|
||||||
|
return <TextEditorToolbar currentFile={this.state.currentFile}
|
||||||
|
contractCompile={this.props.contractCompile}
|
||||||
|
save={() => this.save()}
|
||||||
|
remove={() => this.remove()} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<Grid.Row className="my-2">
|
||||||
|
{this.renderToolbar()}
|
||||||
|
</Grid.Row>
|
||||||
|
<TextEditor value={this.state.currentFile.content}
|
||||||
|
contractCompile={this.props.contractCompile}
|
||||||
|
onFileContentChange={(newContent) => this.onFileContentChange(newContent)} />
|
||||||
|
{this.renderContractFooter()}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapStateToProps(state) {
|
||||||
|
const currentFile = getCurrentFile(state) || DEFAULT_FILE;
|
||||||
|
const contractCompile = getContractCompile(state, currentFile) || {};
|
||||||
|
return {
|
||||||
|
currentFile,
|
||||||
|
contractCompile,
|
||||||
|
compilingContract: state.compilingContract,
|
||||||
|
loading: state.loading,
|
||||||
|
error: state.errorMessage
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
TextEditorContainer.propTypes = {
|
||||||
|
currentFile: PropTypes.object,
|
||||||
|
contractCompile: PropTypes.object,
|
||||||
|
saveCurrentFile: PropTypes.func,
|
||||||
|
fetchCurrentFile: PropTypes.func,
|
||||||
|
saveFile: PropTypes.func,
|
||||||
|
removeFile: PropTypes.func,
|
||||||
|
deployContract: PropTypes.func,
|
||||||
|
compileContract: PropTypes.func,
|
||||||
|
compilingContract: PropTypes.bool,
|
||||||
|
loading: PropTypes.bool,
|
||||||
|
error: PropTypes.string
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
{
|
||||||
|
fetchCurrentFile: currentFileAction.request,
|
||||||
|
saveCurrentFile: saveCurrentFileAction.request,
|
||||||
|
saveFile: saveFileAction.request,
|
||||||
|
removeFile: removeFileAction.request,
|
||||||
|
deployContract: contractDeployAction.post,
|
||||||
|
compileContract: contractCompileAction.post
|
||||||
|
},
|
||||||
|
)(TextEditorContainer);
|
|
@ -1,5 +1,5 @@
|
||||||
import {combineReducers} from 'redux';
|
import {combineReducers} from 'redux';
|
||||||
import {REQUEST, SUCCESS} from "../actions";
|
import {REQUEST, SUCCESS, FAILURE, CONTRACT_COMPILE, FILES} from "../actions";
|
||||||
|
|
||||||
const BN_FACTOR = 10000;
|
const BN_FACTOR = 10000;
|
||||||
const voidAddress = '0x0000000000000000000000000000000000000000';
|
const voidAddress = '0x0000000000000000000000000000000000000000';
|
||||||
|
@ -12,20 +12,19 @@ const entitiesDefaultState = {
|
||||||
processLogs: [],
|
processLogs: [],
|
||||||
contracts: [],
|
contracts: [],
|
||||||
contractProfiles: [],
|
contractProfiles: [],
|
||||||
contractFiles: [],
|
|
||||||
contractFunctions: [],
|
contractFunctions: [],
|
||||||
contractDeploys: [],
|
contractDeploys: [],
|
||||||
|
contractCompiles: [],
|
||||||
contractLogs: [],
|
contractLogs: [],
|
||||||
commands: [],
|
commands: [],
|
||||||
messages: [],
|
messages: [],
|
||||||
messageChannels: [],
|
messageChannels: [],
|
||||||
fiddles: [],
|
|
||||||
fiddleDeploys: [],
|
|
||||||
versions: [],
|
versions: [],
|
||||||
plugins: [],
|
plugins: [],
|
||||||
ensRecords: [],
|
ensRecords: [],
|
||||||
files: [],
|
files: [],
|
||||||
gasOracleStats: []
|
gasOracleStats: [],
|
||||||
|
currentFiles: []
|
||||||
};
|
};
|
||||||
|
|
||||||
const sorter = {
|
const sorter = {
|
||||||
|
@ -61,9 +60,6 @@ const filtrer = {
|
||||||
contracts: function(contract, index, self) {
|
contracts: function(contract, index, self) {
|
||||||
return index === self.findIndex((t) => t.className === contract.className);
|
return index === self.findIndex((t) => t.className === contract.className);
|
||||||
},
|
},
|
||||||
contractFiles: function(contractFile, index, self) {
|
|
||||||
return index === self.findIndex((c) => c.filename === contractFile.filename);
|
|
||||||
},
|
|
||||||
accounts: function(account, index, self) {
|
accounts: function(account, index, self) {
|
||||||
return index === self.findIndex((t) => t.address === account.address);
|
return index === self.findIndex((t) => t.address === account.address);
|
||||||
},
|
},
|
||||||
|
@ -91,6 +87,9 @@ const filtrer = {
|
||||||
};
|
};
|
||||||
|
|
||||||
function entities(state = entitiesDefaultState, action) {
|
function entities(state = entitiesDefaultState, action) {
|
||||||
|
if (action.type === FILES[SUCCESS]) {
|
||||||
|
return {...state, files: action.files};
|
||||||
|
}
|
||||||
for (let name of Object.keys(state)) {
|
for (let name of Object.keys(state)) {
|
||||||
let filter = filtrer[name] || (() => true);
|
let filter = filtrer[name] || (() => true);
|
||||||
let sort = sorter[name] || (() => true);
|
let sort = sorter[name] || (() => true);
|
||||||
|
@ -137,9 +136,20 @@ function loading(_state = false, action) {
|
||||||
return action.type.endsWith(REQUEST);
|
return action.type.endsWith(REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function compilingContract(state = false, action) {
|
||||||
|
if(action.type === CONTRACT_COMPILE[REQUEST]) {
|
||||||
|
return true;
|
||||||
|
} else if (action.type === CONTRACT_COMPILE[FAILURE] || action.type === CONTRACT_COMPILE[SUCCESS]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
const rootReducer = combineReducers({
|
const rootReducer = combineReducers({
|
||||||
entities,
|
entities,
|
||||||
loading,
|
loading,
|
||||||
|
compilingContract,
|
||||||
errorMessage,
|
errorMessage,
|
||||||
errorEntities
|
errorEntities
|
||||||
});
|
});
|
||||||
|
|
|
@ -72,10 +72,6 @@ export function getContractProfile(state, contractName) {
|
||||||
return state.entities.contractProfiles.find((contractProfile => contractProfile.name === contractName));
|
return state.entities.contractProfiles.find((contractProfile => contractProfile.name === contractName));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getContractFile(state, filename) {
|
|
||||||
return state.entities.contractFiles.find((contractFile => contractFile.filename === filename));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getContractFunctions(state, contractName) {
|
export function getContractFunctions(state, contractName) {
|
||||||
return state.entities.contractFunctions.filter((contractFunction => contractFunction.contractName === contractName));
|
return state.entities.contractFunctions.filter((contractFunction => contractFunction.contractName === contractName));
|
||||||
}
|
}
|
||||||
|
@ -84,6 +80,17 @@ export function getContractDeploys(state, contractName) {
|
||||||
return state.entities.contractDeploys.filter((contractDeploy => contractDeploy.contractName === contractName));
|
return state.entities.contractDeploys.filter((contractDeploy => contractDeploy.contractName === contractName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getContractCompile(state, file) {
|
||||||
|
let contractCompile = state.entities.contractCompiles.reverse().find((contractCompile => contractCompile.name === file.name));
|
||||||
|
if (!contractCompile) return;
|
||||||
|
if (contractCompile.errors) {
|
||||||
|
contractCompile.warnings = contractCompile.errors.filter((error) => error.severity === 'warning');
|
||||||
|
contractCompile.errors = contractCompile.errors.filter((error) => error.severity === 'error');
|
||||||
|
}
|
||||||
|
|
||||||
|
return contractCompile;
|
||||||
|
}
|
||||||
|
|
||||||
export function getVersions(state) {
|
export function getVersions(state) {
|
||||||
return state.entities.versions;
|
return state.entities.versions;
|
||||||
}
|
}
|
||||||
|
@ -119,15 +126,6 @@ export function getMessages(state) {
|
||||||
return messages;
|
return messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getFiddle(state) {
|
|
||||||
const fiddleCompilation = last(state.entities.fiddles.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
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getFiddleDeploy(state) {
|
export function getFiddleDeploy(state) {
|
||||||
return {
|
return {
|
||||||
data: last(state.entities.fiddleDeploys),
|
data: last(state.entities.fiddleDeploys),
|
||||||
|
@ -150,3 +148,7 @@ export function isEnsEnabled(state) {
|
||||||
export function getFiles(state) {
|
export function getFiles(state) {
|
||||||
return state.entities.files;
|
return state.entities.files;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getCurrentFile(state) {
|
||||||
|
return last(state.entities.currentFiles);
|
||||||
|
}
|
||||||
|
|
|
@ -1,15 +1,11 @@
|
||||||
import * as actions from '../actions';
|
import * as actions from '../actions';
|
||||||
import * as api from '../api';
|
import * as api from '../services/api';
|
||||||
|
import * as storage from '../services/storage';
|
||||||
import {eventChannel} from 'redux-saga';
|
import {eventChannel} from 'redux-saga';
|
||||||
import {all, call, fork, put, takeEvery, take} from 'redux-saga/effects';
|
import {all, call, fork, put, takeEvery, take} from 'redux-saga/effects';
|
||||||
|
|
||||||
const {account, accounts, block, blocks, transaction, transactions, processes, commands, processLogs,
|
function *doRequest(entity, serviceFn, payload) {
|
||||||
contracts, contract, contractProfile, messageSend, versions, plugins, messageListen, fiddle,
|
const {response, error} = yield call(serviceFn, payload);
|
||||||
fiddleDeploy, ensRecord, ensRecords, contractLogs, contractFile, contractFunction, contractDeploy,
|
|
||||||
fiddleFile, files, gasOracle} = actions;
|
|
||||||
|
|
||||||
function *doRequest(entity, apiFn, payload) {
|
|
||||||
const {response, error} = yield call(apiFn, payload);
|
|
||||||
if(response) {
|
if(response) {
|
||||||
yield put(entity.success(response.data, payload));
|
yield put(entity.success(response.data, payload));
|
||||||
} else if (error) {
|
} else if (error) {
|
||||||
|
@ -17,32 +13,37 @@ function *doRequest(entity, apiFn, payload) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fetchPlugins = doRequest.bind(null, plugins, api.fetchPlugins);
|
export const fetchPlugins = doRequest.bind(null, actions.plugins, api.fetchPlugins);
|
||||||
export const fetchVersions = doRequest.bind(null, versions, api.fetchVersions);
|
export const fetchVersions = doRequest.bind(null, actions.versions, api.fetchVersions);
|
||||||
export const fetchAccount = doRequest.bind(null, account, api.fetchAccount);
|
export const fetchAccount = doRequest.bind(null, actions.account, api.fetchAccount);
|
||||||
export const fetchBlock = doRequest.bind(null, block, api.fetchBlock);
|
export const fetchBlock = doRequest.bind(null, actions.block, api.fetchBlock);
|
||||||
export const fetchTransaction = doRequest.bind(null, transaction, api.fetchTransaction);
|
export const fetchTransaction = doRequest.bind(null, actions.transaction, api.fetchTransaction);
|
||||||
export const fetchAccounts = doRequest.bind(null, accounts, api.fetchAccounts);
|
export const fetchAccounts = doRequest.bind(null, actions.accounts, api.fetchAccounts);
|
||||||
export const fetchBlocks = doRequest.bind(null, blocks, api.fetchBlocks);
|
export const fetchBlocks = doRequest.bind(null, actions.blocks, api.fetchBlocks);
|
||||||
export const fetchTransactions = doRequest.bind(null, transactions, api.fetchTransactions);
|
export const fetchTransactions = doRequest.bind(null, actions.transactions, api.fetchTransactions);
|
||||||
export const fetchProcesses = doRequest.bind(null, processes, api.fetchProcesses);
|
export const fetchProcesses = doRequest.bind(null, actions.processes, api.fetchProcesses);
|
||||||
export const postCommand = doRequest.bind(null, commands, api.postCommand);
|
export const postCommand = doRequest.bind(null, actions.commands, api.postCommand);
|
||||||
export const fetchProcessLogs = doRequest.bind(null, processLogs, api.fetchProcessLogs);
|
export const fetchProcessLogs = doRequest.bind(null, actions.processLogs, api.fetchProcessLogs);
|
||||||
export const fetchContractLogs = doRequest.bind(null, contractLogs, api.fetchContractLogs);
|
export const fetchContractLogs = doRequest.bind(null, actions.contractLogs, api.fetchContractLogs);
|
||||||
export const fetchContracts = doRequest.bind(null, contracts, api.fetchContracts);
|
export const fetchContracts = doRequest.bind(null, actions.contracts, api.fetchContracts);
|
||||||
export const fetchContract = doRequest.bind(null, contract, api.fetchContract);
|
export const fetchContract = doRequest.bind(null, actions.contract, api.fetchContract);
|
||||||
export const fetchContractProfile = doRequest.bind(null, contractProfile, api.fetchContractProfile);
|
export const fetchContractProfile = doRequest.bind(null, actions.contractProfile, api.fetchContractProfile);
|
||||||
export const fetchContractFile = doRequest.bind(null, contractFile, api.fetchContractFile);
|
export const postContractFunction = doRequest.bind(null, actions.contractFunction, api.postContractFunction);
|
||||||
export const fetchLastFiddle = doRequest.bind(null, fiddleFile, api.fetchLastFiddle);
|
export const postContractDeploy = doRequest.bind(null, actions.contractDeploy, api.postContractDeploy);
|
||||||
export const postContractFunction = doRequest.bind(null, contractFunction, api.postContractFunction);
|
export const postContractCompile = doRequest.bind(null, actions.contractCompile, api.postContractCompile);
|
||||||
export const postContractDeploy = doRequest.bind(null, contractDeploy, api.postContractDeploy);
|
export const sendMessage = doRequest.bind(null, actions.messageSend, api.sendMessage);
|
||||||
export const postFiddle = doRequest.bind(null, fiddle, api.postFiddle);
|
export const fetchEnsRecord = doRequest.bind(null, actions.ensRecord, api.fetchEnsRecord);
|
||||||
export const postFiddleDeploy = doRequest.bind(null, fiddleDeploy, api.postFiddleDeploy);
|
export const postEnsRecord = doRequest.bind(null, actions.ensRecords, api.postEnsRecord);
|
||||||
export const sendMessage = doRequest.bind(null, messageSend, api.sendMessage);
|
export const fetchFiles = doRequest.bind(null, actions.files, api.fetchFiles);
|
||||||
export const fetchEnsRecord = doRequest.bind(null, ensRecord, api.fetchEnsRecord);
|
export const fetchFile = doRequest.bind(null, actions.file, api.fetchFile);
|
||||||
export const postEnsRecord = doRequest.bind(null, ensRecords, api.postEnsRecord);
|
export const postFile = doRequest.bind(null, actions.saveFile, api.postFile);
|
||||||
export const fetchFiles = doRequest.bind(null, files, api.fetchFiles);
|
export const deleteFile = doRequest.bind(null, actions.removeFile, api.deleteFile);
|
||||||
export const fetchEthGas = doRequest.bind(null, gasOracle, api.getEthGasAPI);
|
export const fetchEthGas = doRequest.bind(null, actions.gasOracle, api.getEthGasAPI);
|
||||||
|
|
||||||
|
export const fetchCurrentFile = doRequest.bind(null, actions.currentFile, storage.fetchCurrentFile);
|
||||||
|
export const postCurrentFile = doRequest.bind(null, actions.saveCurrentFile, storage.postCurrentFile);
|
||||||
|
export const deleteCurrentFile = doRequest.bind(null, null, storage.deleteCurrentFile);
|
||||||
|
|
||||||
|
|
||||||
export function *watchFetchTransaction() {
|
export function *watchFetchTransaction() {
|
||||||
yield takeEvery(actions.TRANSACTION[actions.REQUEST], fetchTransaction);
|
yield takeEvery(actions.TRANSACTION[actions.REQUEST], fetchTransaction);
|
||||||
|
@ -96,14 +97,6 @@ export function *watchFetchContractProfile() {
|
||||||
yield takeEvery(actions.CONTRACT_PROFILE[actions.REQUEST], fetchContractProfile);
|
yield takeEvery(actions.CONTRACT_PROFILE[actions.REQUEST], fetchContractProfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function *watchFetchContractFile() {
|
|
||||||
yield takeEvery(actions.CONTRACT_FILE[actions.REQUEST], fetchContractFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function *watchFetchLastFiddle() {
|
|
||||||
yield takeEvery(actions.FIDDLE_FILE[actions.REQUEST], fetchLastFiddle);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function *watchPostContractFunction() {
|
export function *watchPostContractFunction() {
|
||||||
yield takeEvery(actions.CONTRACT_FUNCTION[actions.REQUEST], postContractFunction);
|
yield takeEvery(actions.CONTRACT_FUNCTION[actions.REQUEST], postContractFunction);
|
||||||
}
|
}
|
||||||
|
@ -112,6 +105,10 @@ export function *watchPostContractDeploy() {
|
||||||
yield takeEvery(actions.CONTRACT_DEPLOY[actions.REQUEST], postContractDeploy);
|
yield takeEvery(actions.CONTRACT_DEPLOY[actions.REQUEST], postContractDeploy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function *watchPostContractCompile() {
|
||||||
|
yield takeEvery(actions.CONTRACT_COMPILE[actions.REQUEST], postContractCompile);
|
||||||
|
}
|
||||||
|
|
||||||
export function *watchFetchVersions() {
|
export function *watchFetchVersions() {
|
||||||
yield takeEvery(actions.VERSIONS[actions.REQUEST], fetchVersions);
|
yield takeEvery(actions.VERSIONS[actions.REQUEST], fetchVersions);
|
||||||
}
|
}
|
||||||
|
@ -136,22 +133,39 @@ export function *watchListenToMessages() {
|
||||||
yield takeEvery(actions.MESSAGE_LISTEN[actions.REQUEST], listenToMessages);
|
yield takeEvery(actions.MESSAGE_LISTEN[actions.REQUEST], listenToMessages);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function *watchPostFiddle() {
|
|
||||||
yield takeEvery(actions.FIDDLE[actions.REQUEST], postFiddle);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function *watchFetchLastFiddleSuccess() {
|
|
||||||
yield takeEvery(actions.FIDDLE_FILE[actions.SUCCESS], postFiddle);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function *watchPostFiddleDeploy() {
|
|
||||||
yield takeEvery(actions.FIDDLE_DEPLOY[actions.REQUEST], postFiddleDeploy);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function *watchFetchFiles() {
|
export function *watchFetchFiles() {
|
||||||
yield takeEvery(actions.FILES[actions.REQUEST], fetchFiles);
|
yield takeEvery(actions.FILES[actions.REQUEST], fetchFiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function *watchFetchFile() {
|
||||||
|
yield takeEvery(actions.FILE[actions.REQUEST], fetchFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function *watchPostFile() {
|
||||||
|
yield takeEvery(actions.SAVE_FILE[actions.REQUEST], postFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function *watchDeleteFile() {
|
||||||
|
yield takeEvery(actions.REMOVE_FILE[actions.REQUEST], deleteFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function *watchDeleteFileSuccess() {
|
||||||
|
yield takeEvery(actions.REMOVE_FILE[actions.SUCCESS], fetchFiles);
|
||||||
|
yield takeEvery(actions.REMOVE_FILE[actions.SUCCESS], deleteCurrentFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function *watchFetchFileSuccess() {
|
||||||
|
yield takeEvery(actions.FILE[actions.SUCCESS], postCurrentFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function *watchFetchCurrentFile() {
|
||||||
|
yield takeEvery(actions.CURRENT_FILE[actions.REQUEST], fetchCurrentFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function *watchPostCurrentFile() {
|
||||||
|
yield takeEvery(actions.SAVE_CURRENT_FILE[actions.REQUEST], postCurrentFile);
|
||||||
|
}
|
||||||
|
|
||||||
export function *watchFetchEthGas() {
|
export function *watchFetchEthGas() {
|
||||||
yield takeEvery(actions.GAS_ORACLE[actions.REQUEST], fetchEthGas);
|
yield takeEvery(actions.GAS_ORACLE[actions.REQUEST], fetchEthGas);
|
||||||
}
|
}
|
||||||
|
@ -186,7 +200,7 @@ export function *listenToProcessLogs(action) {
|
||||||
const channel = yield call(createChannel, socket);
|
const channel = yield call(createChannel, socket);
|
||||||
while (true) {
|
while (true) {
|
||||||
const processLog = yield take(channel);
|
const processLog = yield take(channel);
|
||||||
yield put(processLogs.success([processLog]));
|
yield put(actions.processLogs.success([processLog]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,7 +213,7 @@ export function *listenToContractLogs() {
|
||||||
const channel = yield call(createChannel, socket);
|
const channel = yield call(createChannel, socket);
|
||||||
while (true) {
|
while (true) {
|
||||||
const contractLog = yield take(channel);
|
const contractLog = yield take(channel);
|
||||||
yield put(contractLogs.success([contractLog]));
|
yield put(actions.contractLogs.success([contractLog]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,7 +226,7 @@ export function *listenGasOracle() {
|
||||||
const channel = yield call(createChannel, socket);
|
const channel = yield call(createChannel, socket);
|
||||||
while (true) {
|
while (true) {
|
||||||
const gasOracleStats = yield take(channel);
|
const gasOracleStats = yield take(channel);
|
||||||
yield put(gasOracle.success(gasOracleStats));
|
yield put(actions.gasOracle.success(gasOracleStats));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,7 +239,7 @@ export function *listenToMessages(action) {
|
||||||
const channel = yield call(createChannel, socket);
|
const channel = yield call(createChannel, socket);
|
||||||
while (true) {
|
while (true) {
|
||||||
const message = yield take(channel);
|
const message = yield take(channel);
|
||||||
yield put(messageListen.success([{channel: action.messageChannels[0], message: message.data, time: message.time}]));
|
yield put(actions.messageListen.success([{channel: action.messageChannels[0], message: message.data, time: message.time}]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,20 +261,23 @@ export default function *root() {
|
||||||
fork(watchFetchBlocks),
|
fork(watchFetchBlocks),
|
||||||
fork(watchFetchContracts),
|
fork(watchFetchContracts),
|
||||||
fork(watchFetchContractProfile),
|
fork(watchFetchContractProfile),
|
||||||
fork(watchFetchContractFile),
|
|
||||||
fork(watchPostContractFunction),
|
fork(watchPostContractFunction),
|
||||||
fork(watchPostContractDeploy),
|
fork(watchPostContractDeploy),
|
||||||
|
fork(watchPostContractCompile),
|
||||||
fork(watchListenToMessages),
|
fork(watchListenToMessages),
|
||||||
fork(watchSendMessage),
|
fork(watchSendMessage),
|
||||||
fork(watchFetchContract),
|
fork(watchFetchContract),
|
||||||
fork(watchFetchTransaction),
|
fork(watchFetchTransaction),
|
||||||
fork(watchPostFiddle),
|
|
||||||
fork(watchPostFiddleDeploy),
|
|
||||||
fork(watchFetchLastFiddle),
|
|
||||||
fork(watchFetchLastFiddleSuccess),
|
|
||||||
fork(watchFetchEnsRecord),
|
fork(watchFetchEnsRecord),
|
||||||
fork(watchPostEnsRecords),
|
fork(watchPostEnsRecords),
|
||||||
fork(watchFetchFiles),
|
fork(watchFetchFiles),
|
||||||
|
fork(watchFetchFile),
|
||||||
|
fork(watchPostFile),
|
||||||
|
fork(watchDeleteFile),
|
||||||
|
fork(watchDeleteFileSuccess),
|
||||||
|
fork(watchFetchFileSuccess),
|
||||||
|
fork(watchFetchCurrentFile),
|
||||||
|
fork(watchPostCurrentFile),
|
||||||
fork(watchFetchEthGas),
|
fork(watchFetchEthGas),
|
||||||
fork(watchListenGasOracle)
|
fork(watchListenGasOracle)
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -20,6 +20,16 @@ function post(path, params) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function destroy(path, params) {
|
||||||
|
return axios.delete(constants.httpEndpoint + path, params)
|
||||||
|
.then((response) => {
|
||||||
|
return {response, error: null};
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
return {response: null, error: error.message || 'Something bad happened'};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function postCommand(payload) {
|
export function postCommand(payload) {
|
||||||
return post('/command', payload);
|
return post('/command', payload);
|
||||||
}
|
}
|
||||||
|
@ -76,6 +86,10 @@ export function postContractDeploy(payload) {
|
||||||
return post(`/contract/${payload.contractName}/deploy`, payload);
|
return post(`/contract/${payload.contractName}/deploy`, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function postContractCompile(payload) {
|
||||||
|
return post('/contract/compile', payload);
|
||||||
|
}
|
||||||
|
|
||||||
export function fetchVersions() {
|
export function fetchVersions() {
|
||||||
return get('/versions');
|
return get('/versions');
|
||||||
}
|
}
|
||||||
|
@ -104,16 +118,24 @@ export function postEnsRecord(payload) {
|
||||||
return post('/ens/register', payload);
|
return post('/ens/register', payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fetchContractFile(payload) {
|
|
||||||
return get('/files/contracts', {params: payload});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getEthGasAPI() {
|
export function getEthGasAPI() {
|
||||||
return get('/blockchain/gas/oracle', {});
|
return get('/blockchain/gas/oracle', {});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fetchLastFiddle() {
|
export function fetchFiles() {
|
||||||
return get('/files/lastfiddle', {params: 'temp'});
|
return get('/files');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fetchFile(payload) {
|
||||||
|
return get('/file', {params: payload});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function postFile(payload) {
|
||||||
|
return post('/files', payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteFile(payload) {
|
||||||
|
return destroy('/file', {params: payload});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function listenToChannel(channel) {
|
export function listenToChannel(channel) {
|
||||||
|
@ -135,15 +157,3 @@ export function webSocketBlockHeader() {
|
||||||
export function websocketGasOracle() {
|
export function websocketGasOracle() {
|
||||||
return new WebSocket(`${constants.wsEndpoint}/blockchain/gas/oracle`);
|
return new WebSocket(`${constants.wsEndpoint}/blockchain/gas/oracle`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function postFiddle(payload) {
|
|
||||||
return post('/contract/compile', payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function postFiddleDeploy(payload) {
|
|
||||||
return post('/contract/deploy', {compiledContract: payload.compiledCode});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function fetchFiles() {
|
|
||||||
return get('/files');
|
|
||||||
}
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
export function postCurrentFile(file) {
|
||||||
|
return new Promise(function(resolve) {
|
||||||
|
localStorage.setItem('currentFile', JSON.stringify(file));
|
||||||
|
resolve({response: {data: file}});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fetchCurrentFile() {
|
||||||
|
return new Promise(function(resolve) {
|
||||||
|
resolve({response: {data: JSON.parse(localStorage.getItem('currentFile'))}});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteCurrentFile() {
|
||||||
|
return new Promise(function(resolve) {
|
||||||
|
localStorage.removeItem('currentFile');
|
||||||
|
resolve({});
|
||||||
|
});
|
||||||
|
}
|
|
@ -2,7 +2,8 @@ let toposort = require('toposort');
|
||||||
let async = require('async');
|
let async = require('async');
|
||||||
const cloneDeep = require('clone-deep');
|
const cloneDeep = require('clone-deep');
|
||||||
|
|
||||||
let utils = require('../../utils/utils.js');
|
const utils = require('../../utils/utils.js');
|
||||||
|
const fs = require('../../core/fs');
|
||||||
|
|
||||||
// TODO: create a contract object
|
// TODO: create a contract object
|
||||||
|
|
||||||
|
@ -268,6 +269,7 @@ class ContractsManager {
|
||||||
contract.abiDefinition = compiledContract.abiDefinition;
|
contract.abiDefinition = compiledContract.abiDefinition;
|
||||||
contract.filename = compiledContract.filename;
|
contract.filename = compiledContract.filename;
|
||||||
contract.originalFilename = compiledContract.originalFilename || ("contracts/" + contract.filename);
|
contract.originalFilename = compiledContract.originalFilename || ("contracts/" + contract.filename);
|
||||||
|
contract.path = fs.dappPath(contract.originalFilename);
|
||||||
|
|
||||||
contract.gas = (contractConfig && contractConfig.gas) || self.contractsConfig.gas || 'auto';
|
contract.gas = (contractConfig && contractConfig.gas) || self.contractsConfig.gas || 'auto';
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
let async = require('../../utils/async_extend.js');
|
let async = require('../../utils/async_extend.js');
|
||||||
let SolcW = require('./solcW.js');
|
let SolcW = require('./solcW.js');
|
||||||
const fs = require('../../core/fs');
|
|
||||||
|
|
||||||
class Solidity {
|
class Solidity {
|
||||||
|
|
||||||
|
@ -20,18 +19,12 @@ class Solidity {
|
||||||
'post',
|
'post',
|
||||||
'/embark-api/contract/compile',
|
'/embark-api/contract/compile',
|
||||||
(req, res) => {
|
(req, res) => {
|
||||||
if(typeof req.body.codeToCompile !== 'string'){
|
if(typeof req.body.code !== 'string'){
|
||||||
return res.send({error: 'Body parameter \'codeToCompile\' must be a string'});
|
return res.send({error: 'Body parameter \'code\' must be a string'});
|
||||||
}
|
}
|
||||||
const input = {'fiddle': {content: req.body.codeToCompile.replace(/\r\n/g, '\n')}};
|
const input = {[req.body.name]: {content: req.body.code.replace(/\r\n/g, '\n')}};
|
||||||
this.compile_solidity_code(input, {}, true, (errors, compilationResult) => {
|
this.compile_solidity_code(input, {}, true, (errors, result) => {
|
||||||
// write code to filesystem so we can view the source after page refresh
|
const responseData = {errors: errors, result: result};
|
||||||
const className = !compilationResult ? 'temp' : Object.keys(compilationResult).join('_');
|
|
||||||
this._writeFiddleToFile(req.body.codeToCompile, className, Boolean(compilationResult), (err) => {
|
|
||||||
if(err) this.logger.trace('Error writing fiddle to filesystem: ', err);
|
|
||||||
}); // 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)}`);
|
this.logger.trace(`POST response /embark-api/contract/compile:\n ${JSON.stringify(responseData)}`);
|
||||||
res.send(responseData);
|
res.send(responseData);
|
||||||
});
|
});
|
||||||
|
@ -39,26 +32,6 @@ class Solidity {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_writeFiddleToFile(code, className, isCompiled, cb){
|
|
||||||
fs.mkdirp('.embark/fiddles', (err) => {
|
|
||||||
if(err) return cb(err);
|
|
||||||
|
|
||||||
// always write to temp.sol file
|
|
||||||
const filePath = Solidity._getFiddlePath('temp');
|
|
||||||
fs.writeFile(filePath, code, 'utf8', cb);
|
|
||||||
|
|
||||||
// if it's compiled, also write to [classname].sol
|
|
||||||
if(isCompiled){
|
|
||||||
const filePath = Solidity._getFiddlePath(className);
|
|
||||||
fs.writeFile(filePath, code, 'utf8', cb);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static _getFiddlePath(className){
|
|
||||||
return fs.dappPath(`.embark/fiddles/${className}.sol`);
|
|
||||||
}
|
|
||||||
|
|
||||||
_compile(jsonObj, returnAllErrors, callback) {
|
_compile(jsonObj, returnAllErrors, callback) {
|
||||||
const self = this;
|
const self = this;
|
||||||
self.solcW.compile(jsonObj, function (err, output) {
|
self.solcW.compile(jsonObj, function (err, output) {
|
||||||
|
@ -156,7 +129,6 @@ class Solidity {
|
||||||
|
|
||||||
const className = contractName;
|
const className = contractName;
|
||||||
let filename = contractFile;
|
let filename = contractFile;
|
||||||
if(filename === 'fiddle') filename = Solidity._getFiddlePath(className);
|
|
||||||
|
|
||||||
compiled_object[className] = {};
|
compiled_object[className] = {};
|
||||||
compiled_object[className].code = contract.evm.bytecode.object;
|
compiled_object[className].code = contract.evm.bytecode.object;
|
||||||
|
|
|
@ -17,51 +17,62 @@ class Pipeline {
|
||||||
this.plugins = options.plugins;
|
this.plugins = options.plugins;
|
||||||
this.pipelinePlugins = this.plugins.getPluginsFor('pipeline');
|
this.pipelinePlugins = this.plugins.getPluginsFor('pipeline');
|
||||||
|
|
||||||
const self = this;
|
|
||||||
self.events.setCommandHandler("files:contract", (filename, cb) => {
|
|
||||||
// handle case where we have a fiddle file and not a file stored in the dapp
|
|
||||||
if(filename.indexOf('.embark/fiddles') > -1){
|
|
||||||
return fs.readFile(filename, 'utf8', (err, source) => {
|
|
||||||
if (err) return cb({error: err});
|
|
||||||
cb(source);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
let file = self.contractsFiles.find((file) => file.filename === filename);
|
|
||||||
if (!file) {
|
|
||||||
return cb({error: filename + " not found"});
|
|
||||||
}
|
|
||||||
file.content(cb);
|
|
||||||
});
|
|
||||||
|
|
||||||
let plugin = this.plugins.createPlugin('deployment', {});
|
let plugin = this.plugins.createPlugin('deployment', {});
|
||||||
plugin.registerAPICall(
|
plugin.registerAPICall(
|
||||||
'get',
|
'get',
|
||||||
'/embark-api/files/contracts/',
|
'/embark-api/file',
|
||||||
(req, res) => {
|
(req, res) => {
|
||||||
self.events.request('files:contract', req.query.filename, res.send.bind(res));
|
if (!fs.existsSync(req.query.path) || !req.query.path.startsWith(fs.dappPath())) {
|
||||||
|
return res.send({error: 'Path is invalid'});
|
||||||
|
}
|
||||||
|
const name = path.basename(req.query.path);
|
||||||
|
const content = fs.readFileSync(req.query.path, 'utf8');
|
||||||
|
res.send({name, content, path: req.query.path});
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
plugin.registerAPICall(
|
||||||
|
'post',
|
||||||
|
'/embark-api/files',
|
||||||
|
(req, res) => {
|
||||||
|
try {
|
||||||
|
this.apiGuardBadFile(req.body.path);
|
||||||
|
} catch (error) {
|
||||||
|
return res.send({error: error.message});
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.writeFileSync(req.body.path, req.body.content, { encoding: 'utf8'});
|
||||||
|
const name = path.basename(req.body.path);
|
||||||
|
res.send({name, path: req.body.path, content: req.body.content});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
plugin.registerAPICall(
|
||||||
|
'delete',
|
||||||
|
'/embark-api/file',
|
||||||
|
(req, res) => {
|
||||||
|
try {
|
||||||
|
this.apiGuardBadFile(req.query.path);
|
||||||
|
} catch (error) {
|
||||||
|
return res.send({error: error.message});
|
||||||
|
}
|
||||||
|
fs.removeSync(req.query.path);
|
||||||
|
res.send();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
plugin.registerAPICall(
|
plugin.registerAPICall(
|
||||||
'get',
|
'get',
|
||||||
'/embark-api/files/lastfiddle',
|
'/embark-api/files',
|
||||||
(req, res) => {
|
(req, res) => {
|
||||||
fs.readFile(fs.dappPath('.embark/fiddles/temp.sol'), 'utf8', (err, source) => {
|
const rootPath = fs.dappPath();
|
||||||
if (err) return res.send({error: err.message});
|
const walk = (dir, filelist = []) => fs.readdirSync(dir).map(name => {
|
||||||
res.send(source);
|
let isRoot = rootPath === dir;
|
||||||
});
|
if (fs.statSync(path.join(dir, name)).isDirectory()) {
|
||||||
|
return { isRoot, name, dirname: dir, children: walk(path.join(dir, name), filelist)};
|
||||||
}
|
}
|
||||||
);
|
return {name, isRoot, path: path.join(dir, name), dirname: dir};
|
||||||
|
|
||||||
plugin.registerAPICall(
|
|
||||||
'get',
|
|
||||||
'/embark-api/files/',
|
|
||||||
(req, res) => {
|
|
||||||
const walk = (dir, filelist = []) => fs.readdirSync(dir).map(file => {
|
|
||||||
if (fs.statSync(path.join(dir, file)).isDirectory()) {
|
|
||||||
return {name: file, children: walk(path.join(dir, file), filelist)};
|
|
||||||
}
|
|
||||||
return {name: file};
|
|
||||||
});
|
});
|
||||||
const files = walk(fs.dappPath());
|
const files = walk(fs.dappPath());
|
||||||
res.send(files);
|
res.send(files);
|
||||||
|
@ -69,6 +80,13 @@ class Pipeline {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
apiGuardBadFile(pathToCheck) {
|
||||||
|
const dir = path.dirname(pathToCheck);
|
||||||
|
if (!fs.existsSync(pathToCheck) || !dir.startsWith(fs.dappPath())) {
|
||||||
|
throw new Error('Path is invalid');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
build(abi, contractsJSON, path, callback) {
|
build(abi, contractsJSON, path, callback) {
|
||||||
let self = this;
|
let self = this;
|
||||||
const importsList = {};
|
const importsList = {};
|
||||||
|
|
|
@ -3951,13 +3951,11 @@
|
||||||
},
|
},
|
||||||
"balanced-match": {
|
"balanced-match": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"bundled": true,
|
"bundled": true
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"brace-expansion": {
|
"brace-expansion": {
|
||||||
"version": "1.1.11",
|
"version": "1.1.11",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"optional": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"balanced-match": "^1.0.0",
|
"balanced-match": "^1.0.0",
|
||||||
"concat-map": "0.0.1"
|
"concat-map": "0.0.1"
|
||||||
|
@ -3970,18 +3968,15 @@
|
||||||
},
|
},
|
||||||
"code-point-at": {
|
"code-point-at": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"bundled": true,
|
"bundled": true
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"concat-map": {
|
"concat-map": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"bundled": true,
|
"bundled": true
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"console-control-strings": {
|
"console-control-strings": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"bundled": true,
|
"bundled": true
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"core-util-is": {
|
"core-util-is": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
|
@ -4084,8 +4079,7 @@
|
||||||
},
|
},
|
||||||
"inherits": {
|
"inherits": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"bundled": true,
|
"bundled": true
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"ini": {
|
"ini": {
|
||||||
"version": "1.3.5",
|
"version": "1.3.5",
|
||||||
|
@ -4095,7 +4089,6 @@
|
||||||
"is-fullwidth-code-point": {
|
"is-fullwidth-code-point": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"optional": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"number-is-nan": "^1.0.0"
|
"number-is-nan": "^1.0.0"
|
||||||
}
|
}
|
||||||
|
@ -4108,20 +4101,17 @@
|
||||||
"minimatch": {
|
"minimatch": {
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"optional": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"brace-expansion": "^1.1.7"
|
"brace-expansion": "^1.1.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"minimist": {
|
"minimist": {
|
||||||
"version": "0.0.8",
|
"version": "0.0.8",
|
||||||
"bundled": true,
|
"bundled": true
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"minipass": {
|
"minipass": {
|
||||||
"version": "2.2.4",
|
"version": "2.2.4",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"optional": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"safe-buffer": "^5.1.1",
|
"safe-buffer": "^5.1.1",
|
||||||
"yallist": "^3.0.0"
|
"yallist": "^3.0.0"
|
||||||
|
@ -4138,7 +4128,6 @@
|
||||||
"mkdirp": {
|
"mkdirp": {
|
||||||
"version": "0.5.1",
|
"version": "0.5.1",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"optional": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"minimist": "0.0.8"
|
"minimist": "0.0.8"
|
||||||
}
|
}
|
||||||
|
@ -4211,8 +4200,7 @@
|
||||||
},
|
},
|
||||||
"number-is-nan": {
|
"number-is-nan": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"bundled": true,
|
"bundled": true
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"object-assign": {
|
"object-assign": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
|
@ -4222,7 +4210,6 @@
|
||||||
"once": {
|
"once": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"optional": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
}
|
}
|
||||||
|
@ -4328,7 +4315,6 @@
|
||||||
"string-width": {
|
"string-width": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"optional": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"code-point-at": "^1.0.0",
|
"code-point-at": "^1.0.0",
|
||||||
"is-fullwidth-code-point": "^1.0.0",
|
"is-fullwidth-code-point": "^1.0.0",
|
||||||
|
|
Loading…
Reference in New Issue