2017-07-27 13:24:23 +02:00
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
2016-11-11 15:00:04 +01:00
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
2017-07-21 15:46:53 +02:00
import React , { Component } from 'react' ;
import PropTypes from 'prop-types' ;
2017-03-02 12:24:54 +01:00
import { FormattedMessage } from 'react-intl' ;
2016-11-11 15:00:04 +01:00
import { observer } from 'mobx-react' ;
import { connect } from 'react-redux' ;
import moment from 'moment' ;
2016-12-21 15:12:40 +01:00
import { throttle } from 'lodash' ;
2016-11-11 15:00:04 +01:00
2017-07-27 13:24:23 +02:00
import { Actionbar , ActionbarExport , ActionbarImport , Button , Dropdown , Input , Loading , Page , Toggle , Tab } from '@parity/ui' ;
2017-05-12 12:06:16 +02:00
import { CancelIcon , ListIcon , SaveIcon , SendIcon , SettingsIcon } from '@parity/ui/Icons' ;
import Editor from '@parity/ui/Editor' ;
2017-04-28 11:21:05 +02:00
2017-07-21 15:46:53 +02:00
import DeployContract from '@parity/dapp-contracts/DeployContract' ;
2017-04-28 11:21:05 +02:00
import LoadContract from './LoadContract' ;
import SaveContract from './SaveContract' ;
2016-11-11 15:00:04 +01:00
2017-04-24 13:21:22 +02:00
import ContractDevelopStore from './store' ;
import styles from './contractDevelop.css' ;
2016-11-11 15:00:04 +01:00
2017-07-27 13:24:23 +02:00
import { Debugger , TransactButton , Contract , DropdownBond } from 'parity-reactive-ui' ;
import { Bond } from 'oo7' ;
import { bonds } from 'oo7-parity' ;
const traceOptions = [ { text : 'trace' , value : 'trace' } , { text : 'vmTrace' , value : 'vmTrace' } , { text : 'stateDiff' , value : 'stateDiff' } ] ;
2016-11-11 15:00:04 +01:00
@ observer
2017-04-24 13:21:22 +02:00
class ContractDevelop extends Component {
2016-11-11 15:00:04 +01:00
static propTypes = {
accounts : PropTypes . object . isRequired ,
2016-12-15 19:06:05 +01:00
worker : PropTypes . object ,
workerError : PropTypes . any
2016-11-11 15:00:04 +01:00
} ;
2017-04-24 13:21:22 +02:00
store = ContractDevelopStore . get ( ) ;
2016-11-11 15:00:04 +01:00
state = {
resizing : false ,
size : 65
} ;
2017-07-27 13:24:23 +02:00
debugDeploy = this . debugDeploy . bind ( this ) ;
2016-11-11 15:00:04 +01:00
componentWillMount ( ) {
2017-01-09 11:14:36 +01:00
const { worker } = this . props ;
2016-11-11 15:00:04 +01:00
2016-12-20 01:55:57 +01:00
if ( worker !== undefined ) {
2016-12-15 19:06:05 +01:00
this . store . setWorker ( worker ) ;
2016-11-11 15:00:04 +01:00
}
2016-12-21 15:12:40 +01:00
this . throttledResize = throttle ( this . applyResize , 100 , { leading : true } ) ;
2016-11-11 15:00:04 +01:00
}
componentDidMount ( ) {
this . store . setEditor ( this . refs . editor ) ;
2016-12-15 19:06:05 +01:00
if ( this . props . workerError ) {
this . store . setWorkerError ( this . props . workerError ) ;
}
2016-11-11 15:00:04 +01:00
// Wait for editor to be loaded
window . setTimeout ( ( ) => {
this . store . resizeEditor ( ) ;
} , 2000 ) ;
}
2016-12-15 19:06:05 +01:00
// Set the worker if not set before (eg. first page loading)
2016-11-11 15:00:04 +01:00
componentWillReceiveProps ( nextProps ) {
2016-12-20 01:55:57 +01:00
if ( this . props . worker === undefined && nextProps . worker !== undefined ) {
2016-12-15 19:06:05 +01:00
this . store . setWorker ( nextProps . worker ) ;
}
if ( this . props . workerError !== nextProps . workerError ) {
this . store . setWorkerError ( nextProps . workerError ) ;
2016-11-11 15:00:04 +01:00
}
}
render ( ) {
2017-07-27 13:24:23 +02:00
console . log ( 'render contractDevelopment' ) ;
2016-11-11 15:00:04 +01:00
const { sourcecode } = this . store ;
const { size , resizing } = this . state ;
const annotations = this . store . annotations
. slice ( )
. filter ( ( a ) => a . contract === '' ) ;
2017-07-27 13:24:23 +02:00
const panes = [
{ menuItem : 'Parameters' , render : ( ) => < div >
{ this . renderParameters ( ) }
< / d i v > } ,
{ menuItem : 'Debugger' , render : ( ) => < Tab panes = { [ { menuItem : 'Trace' , render : ( ) => this . renderDebugger ( ) } ,
{ menuItem : 'ShowTrace' , render : ( ) => < Debugger txBond = { this . store . contract . trace } / > } ] }
/ >
}
] ;
2016-11-11 15:00:04 +01:00
return (
< div className = { styles . outer } >
{ this . renderDeployModal ( ) }
{ this . renderSaveModal ( ) }
{ this . renderLoadModal ( ) }
{ this . renderActionBar ( ) }
< Page className = { styles . page } >
< div
className = { ` ${ styles . container } ${ resizing ? styles . resizing : '' } ` }
>
< div
className = { styles . editor }
style = { { flex : ` ${ size } % ` } }
>
2017-03-16 13:18:28 +01:00
< h2 > { this . renderTitle ( ) } < / h 2 >
2016-11-11 15:00:04 +01:00
< Editor
ref = 'editor'
onChange = { this . store . handleEditSourcecode }
onExecute = { this . store . handleCompile }
annotations = { annotations }
value = { sourcecode }
className = { styles . mainEditor }
/ >
< / d i v >
< div className = { styles . sliderContainer } >
< span
className = { styles . slider }
onMouseDown = { this . handleStartResize }
2016-11-25 19:48:06 +01:00
/ >
2016-11-11 15:00:04 +01:00
< / d i v >
< div
className = { styles . parameters }
style = { { flex : ` ${ 100 - size } % ` } }
>
2017-07-27 13:24:23 +02:00
< Tab panes = { panes } / >
2016-11-11 15:00:04 +01:00
< / d i v >
< / d i v >
< / P a g e >
< / d i v >
) ;
}
renderTitle ( ) {
const { selectedContract } = this . store ;
if ( ! selectedContract || ! selectedContract . name ) {
2017-03-02 12:24:54 +01:00
return (
< FormattedMessage
id = 'writeContract.title.new'
defaultMessage = 'New Solidity Contract'
/ >
) ;
2016-11-11 15:00:04 +01:00
}
return (
< span >
{ selectedContract . name }
< span
className = { styles . timestamp }
2017-03-02 12:24:54 +01:00
title = {
< FormattedMessage
id = 'writeContract.title.saved'
defaultMessage = 'saved @ {timestamp}'
vaules = { {
timestamp : ( new Date ( selectedContract . timestamp ) ) . toISOString ( )
} }
/ >
}
2016-11-11 15:00:04 +01:00
>
2017-03-02 12:24:54 +01:00
< FormattedMessage
id = 'writeContract.details.saved'
defaultMessage = '(saved {timestamp})'
values = { {
timestamp : moment ( selectedContract . timestamp ) . fromNow ( )
} }
/ >
2016-11-11 15:00:04 +01:00
< / s p a n >
< / s p a n >
) ;
}
2017-07-27 13:24:23 +02:00
debugDeploy ( contract ) {
const { contracts , contractIndex } = this . store ;
const bytecode = contract . bytecode ;
const abi = contract . interface ;
if ( ! contract . deployed ) {
let tx = bonds . deployContract ( bytecode , JSON . parse ( abi ) ) ;
tx . done ( s => {
console . log ( 'txDone!' ) ;
// address is undefined from s (How to become ? => TuT) , error because of triggering while triggering => can makeContract call? between here and next printout
let address = s . deployed . address ;
contract . deployed = bonds . makeContract ( address , JSON . parse ( abi ) , [ ] , true ) ;
contract . address = address ;
contract . trace = new Bond ( ) ;
contract . trace . tie ( v => {
console . log ( 'TIED to BOND' , v ) ;
// v.then(console.log);
} ) ;
contracts [ contractIndex ] = contract ;
console . log ( 'New Contract' , contract , 'index' , contractIndex ) ;
} ) ;
return tx ;
} else {
return null ;
}
}
renderDebugger ( ) {
const { contracts , compiled } = this . store ;
let traceMode = new Bond ( ) ;
const contractKeys = Object . keys ( contracts ) ;
return ( < div >
{ compiled ? < div >
< DropdownBond bond = { traceMode } options = { traceOptions } fluid multiple / >
{ contractKeys . map ( ( name , i ) => {
let c = contracts [ name ] ;
console . log ( 'contract' , c , 'index' , i , 'name' , name ) ;
return (
< div key = { i } >
{ c . deployed
? < Contract
contract = { c . deployed ? c . deployed : null }
trace = { c . trace }
traceMode = { traceMode }
contractName = { ` Contract ${ name } ${ c . address } ` }
/ >
: < TransactButton content = { ` Debug ${ name } ` } tx = { ( ) => this . debugDeploy ( c ) } statusText disabled = { c . deployed } / > }
< / d i v >
) ;
} ) }
< / d i v > : n u l l }
< / d i v > ) ;
}
2016-11-11 15:00:04 +01:00
renderActionBar ( ) {
const { sourcecode , selectedContract } = this . store ;
const filename = selectedContract && selectedContract . name
? selectedContract . name
. replace ( /[^a-z0-9]+/gi , '-' )
. replace ( /-$/ , '' )
. toLowerCase ( )
: 'contract.sol' ;
const extension = /\.sol$/ . test ( filename ) ? '' : '.sol' ;
const buttons = [
< Button
2017-03-02 12:24:54 +01:00
icon = { < CancelIcon / > }
label = {
< FormattedMessage
id = 'writeContract.buttons.new'
defaultMessage = 'New'
/ >
}
2016-11-11 15:00:04 +01:00
key = 'newContract'
onClick = { this . store . handleNewContract }
/ > ,
< Button
icon = { < ListIcon / > }
2017-03-02 12:24:54 +01:00
label = {
< FormattedMessage
id = 'writeContract.buttons.load'
defaultMessage = 'Load'
/ >
}
2016-11-11 15:00:04 +01:00
key = 'loadContract'
onClick = { this . store . handleOpenLoadModal }
/ > ,
< Button
icon = { < SaveIcon / > }
2017-03-02 12:24:54 +01:00
label = {
< FormattedMessage
id = 'writeContract.buttons.save'
defaultMessage = 'Save'
/ >
}
2016-11-11 15:00:04 +01:00
key = 'saveContract'
onClick = { this . store . handleSaveContract }
/ > ,
< ActionbarExport
key = 'exportSourcecode'
content = { sourcecode }
filename = { ` ${ filename } ${ extension } ` }
/ > ,
< ActionbarImport
key = 'importSourcecode'
2017-03-02 12:24:54 +01:00
title = {
< FormattedMessage
id = 'writeContract.buttons.import'
defaultMessage = 'Import Solidity'
/ >
}
2016-11-11 15:00:04 +01:00
onConfirm = { this . store . handleImport }
renderValidation = { this . renderImportValidation }
/ >
] ;
return (
< Actionbar
2017-03-02 12:24:54 +01:00
title = {
< FormattedMessage
id = 'writeContract.title.main'
defaultMessage = 'Write a Contract'
/ >
}
2016-11-11 15:00:04 +01:00
buttons = { buttons }
/ >
) ;
}
renderImportValidation = ( content ) => {
return (
< Editor
readOnly
value = { content }
maxLines = { 20 }
/ >
) ;
}
renderParameters ( ) {
2016-12-15 19:06:05 +01:00
const { compiling , contract , selectedBuild , loading , workerError } = this . store ;
2016-11-11 15:00:04 +01:00
if ( selectedBuild < 0 ) {
return (
< div className = { ` ${ styles . panel } ${ styles . centeredMessage } ` } >
2017-05-15 14:49:47 +02:00
< Loading / >
2017-03-02 12:24:54 +01:00
< p >
< FormattedMessage
id = 'writeContract.title.loading'
defaultMessage = 'Loading...'
/ >
< / p >
2016-11-11 15:00:04 +01:00
< / d i v >
) ;
}
2017-05-05 10:00:16 +02:00
let content ;
if ( workerError ) {
content = (
< div className = { styles . panel } >
< div className = { styles . centeredMessage } >
< p >
< FormattedMessage
id = 'writeContract.error.params'
defaultMessage = 'An error occurred with the following description'
/ >
< / p >
< div className = { styles . error } >
{ workerError . toString ( ) }
< / d i v >
< / d i v >
< / d i v >
) ;
} else if ( loading ) {
2016-11-11 15:00:04 +01:00
const { longVersion } = this . store . builds [ selectedBuild ] ;
2017-05-05 10:00:16 +02:00
content = (
2016-11-11 15:00:04 +01:00
< div className = { styles . panel } >
< div className = { styles . centeredMessage } >
2017-05-15 14:49:47 +02:00
< Loading / >
2017-03-02 12:24:54 +01:00
< p >
< FormattedMessage
id = 'writeContract.title.solidity'
defaultMessage = 'Loading Solidity {version}'
values = { {
version : longVersion
} }
/ >
< / p >
2016-11-11 15:00:04 +01:00
< / d i v >
< / d i v >
) ;
2017-05-05 10:00:16 +02:00
} else {
content = this . renderCompilation ( ) ;
2016-11-11 15:00:04 +01:00
}
return (
< div className = { styles . panel } >
< div >
< Button
icon = { < SettingsIcon / > }
2017-03-02 12:24:54 +01:00
label = {
< FormattedMessage
id = 'writeContract.buttons.compile'
defaultMessage = 'Compile'
/ >
}
2016-11-11 15:00:04 +01:00
onClick = { this . store . handleCompile }
primary = { false }
2017-05-10 15:24:24 +02:00
disabled = { compiling || this . store . isPristine }
2016-11-11 15:00:04 +01:00
/ >
{
contract
2017-01-18 13:05:01 +01:00
? (
2017-07-27 13:24:23 +02:00
< span >
< Button
disabled = { compiling || ! this . store . isPristine }
icon = { < SendIcon / > }
label = {
< FormattedMessage
id = 'writeContract.buttons.deploy'
defaultMessage = 'Deploy'
/ >
}
onClick = { this . store . handleOpenDeployModal }
primary = { false }
/ >
< / s p a n >
)
: null
2016-11-11 15:00:04 +01:00
}
2017-07-27 13:24:23 +02:00
2016-11-11 15:00:04 +01:00
< / d i v >
2016-12-20 02:11:04 +01:00
< div className = { styles . toggles } >
< div >
< Toggle
2017-03-02 12:24:54 +01:00
label = {
< FormattedMessage
id = 'writeContract.buttons.optimise'
defaultMessage = 'Optimise'
/ >
}
2016-12-20 02:11:04 +01:00
labelPosition = 'right'
onToggle = { this . store . handleOptimizeToggle }
toggled = { this . store . optimize }
/ >
< / d i v >
< div >
< Toggle
2017-03-02 12:24:54 +01:00
label = {
< FormattedMessage
id = 'writeContract.buttons.autoCompile'
defaultMessage = 'Auto-Compile'
/ >
}
2016-12-20 02:11:04 +01:00
labelPosition = 'right'
onToggle = { this . store . handleAutocompileToggle }
toggled = { this . store . autocompile }
/ >
< / d i v >
< / d i v >
2016-11-11 15:00:04 +01:00
{ this . renderSolidityVersions ( ) }
2017-05-05 10:00:16 +02:00
{ content }
2016-11-11 15:00:04 +01:00
< / d i v >
) ;
}
renderSolidityVersions ( ) {
const { builds , selectedBuild } = this . store ;
return (
< div >
2017-05-11 14:02:27 +02:00
< Dropdown
2017-03-02 12:24:54 +01:00
label = {
< FormattedMessage
id = 'writeContract.title.selectSolidity'
defaultMessage = 'Select a Solidity version'
/ >
}
2016-11-11 15:00:04 +01:00
value = { selectedBuild }
onChange = { this . store . handleSelectBuild }
2017-05-11 14:02:27 +02:00
options = {
builds . map ( ( build , index ) => {
return {
key : index ,
text : build . release ? build . version : build . longVersion ,
value : index ,
content :
build . release
? (
< span className = { styles . big } >
{ build . version }
< / s p a n >
)
: build . longVersion
} ;
} )
}
/ >
2016-11-11 15:00:04 +01:00
< / d i v >
) ;
}
renderDeployModal ( ) {
const { showDeployModal , contract , sourcecode } = this . store ;
if ( ! showDeployModal ) {
return null ;
}
return (
< DeployContract
abi = { contract . interface }
2017-04-19 18:00:05 +02:00
accounts = { this . props . accounts }
2016-11-11 15:00:04 +01:00
code = { ` 0x ${ contract . bytecode } ` }
source = { sourcecode }
onClose = { this . store . handleCloseDeployModal }
readOnly
/ >
) ;
}
renderLoadModal ( ) {
const { showLoadModal } = this . store ;
if ( ! showLoadModal ) {
return null ;
}
return (
< LoadContract
onLoad = { this . store . handleLoadContract }
onDelete = { this . store . handleDeleteContract }
onClose = { this . store . handleCloseLoadModal }
contracts = { this . store . savedContracts }
snippets = { this . store . snippets }
/ >
) ;
}
renderSaveModal ( ) {
const { showSaveModal , sourcecode } = this . store ;
if ( ! showSaveModal ) {
return null ;
}
return (
< SaveContract
sourcecode = { sourcecode }
onSave = { this . store . handleSaveNewContract }
onClose = { this . store . handleCloseSaveModal }
/ >
) ;
}
renderCompilation ( ) {
const { compiled , contracts , compiling , contractIndex , contract } = this . store ;
if ( compiling ) {
return (
< div className = { styles . centeredMessage } >
2017-05-15 14:49:47 +02:00
< Loading / >
2017-03-02 12:24:54 +01:00
< p >
< FormattedMessage
id = 'writeContract.compiling.busy'
defaultMessage = 'Compiling...'
/ >
< / p >
2016-11-11 15:00:04 +01:00
< / d i v >
) ;
}
if ( ! compiled ) {
return (
< div className = { styles . centeredMessage } >
2017-03-02 12:24:54 +01:00
< p >
< FormattedMessage
id = 'writeContract.compiling.action'
defaultMessage = 'Please compile the source code.'
/ >
< / p >
2016-11-11 15:00:04 +01:00
< / d i v >
) ;
}
if ( ! contracts ) {
return this . renderErrors ( ) ;
}
const contractKeys = Object . keys ( contracts ) ;
if ( contractKeys . length === 0 ) {
return (
< div className = { styles . centeredMessage } >
2017-03-02 12:24:54 +01:00
< p >
< FormattedMessage
id = 'writeContract.error.noContract'
defaultMessage = 'No contract has been found.'
/ >
< / p >
2016-11-11 15:00:04 +01:00
< / d i v >
) ;
}
return (
< div className = { styles . compilation } >
2017-05-11 14:02:27 +02:00
< Dropdown
2017-03-02 12:24:54 +01:00
label = {
< FormattedMessage
id = 'writeContract.title.contract'
defaultMessage = 'Select a contract'
/ >
}
2016-11-11 15:00:04 +01:00
value = { contractIndex }
onChange = { this . store . handleSelectContract }
2017-05-11 14:02:27 +02:00
options = {
contractKeys . map ( ( name , index ) => {
return {
key : index ,
value : index ,
text : name
} ;
} )
}
/ >
2016-11-11 15:00:04 +01:00
{ this . renderContract ( contract ) }
2017-03-02 12:24:54 +01:00
< h4 className = { styles . messagesHeader } >
< FormattedMessage
id = 'writeContract.title.messages'
defaultMessage = 'Compiler messages'
/ >
< / h 4 >
2016-11-11 15:00:04 +01:00
{ this . renderErrors ( ) }
< / d i v >
) ;
}
renderContract ( contract ) {
2017-03-21 17:02:41 +01:00
if ( ! contract ) {
return null ;
}
2016-11-11 15:00:04 +01:00
const { bytecode } = contract ;
const abi = contract . interface ;
2017-01-06 10:39:18 +01:00
const metadata = contract . metadata
? (
< Input
allowCopy
2017-03-02 12:24:54 +01:00
label = {
< FormattedMessage
id = 'writeContract.input.metadata'
defaultMessage = 'Metadata'
/ >
}
2017-01-06 10:39:18 +01:00
readOnly
value = { contract . metadata }
/ >
)
: null ;
2016-11-11 15:00:04 +01:00
return (
< div >
< Input
2017-01-06 10:39:18 +01:00
allowCopy
2017-03-02 12:24:54 +01:00
label = {
< FormattedMessage
id = 'writeContract.input.abi'
2017-04-11 16:43:15 +02:00
defaultMessage = 'ABI Definition'
2017-03-02 12:24:54 +01:00
/ >
}
2016-11-11 15:00:04 +01:00
readOnly
value = { abi }
/ >
< Input
2017-01-06 10:39:18 +01:00
allowCopy
2017-03-02 12:24:54 +01:00
label = {
< FormattedMessage
id = 'writeContract.input.code'
defaultMessage = 'Bytecode'
/ >
}
2016-11-11 15:00:04 +01:00
readOnly
value = { ` 0x ${ bytecode } ` }
/ >
2017-01-06 10:39:18 +01:00
{ metadata }
{ this . renderSwarmHash ( contract ) }
2016-11-11 15:00:04 +01:00
< / d i v >
) ;
}
2017-01-06 10:39:18 +01:00
renderSwarmHash ( contract ) {
if ( ! contract || ! contract . metadata ) {
return null ;
}
const { bytecode } = contract ;
// @see https://solidity.readthedocs.io/en/develop/miscellaneous.html#encoding-of-the-metadata-hash-in-the-bytecode
const hashRegex = /a165627a7a72305820([a-f0-9]{64})0029$/ ;
if ( ! hashRegex . test ( bytecode ) ) {
return null ;
}
const hash = hashRegex . exec ( bytecode ) [ 1 ] ;
return (
< Input
allowCopy
2017-03-02 12:24:54 +01:00
label = {
< FormattedMessage
id = 'writeContract.input.swarm'
defaultMessage = 'Swarm Metadata Hash'
/ >
}
2017-01-06 10:39:18 +01:00
readOnly
value = { ` ${ hash } ` }
/ >
) ;
}
2016-11-11 15:00:04 +01:00
renderErrors ( ) {
const { annotations } = this . store ;
const body = annotations . map ( ( annotation , index ) => {
const { text , row , column , contract , type , formal } = annotation ;
const classType = formal ? 'formal' : type ;
const classes = [ styles . message , styles [ classType ] ] ;
return (
< div key = { index } className = { styles . messageContainer } >
< div className = { classes . join ( ' ' ) } > { text } < / d i v >
< span className = { styles . errorPosition } >
{ contract ? ` [ ${ contract } ] ` : '' }
{ row } : { column }
< / s p a n >
< / d i v >
) ;
} ) ;
return (
< div className = { styles . errors } >
{ body }
< / d i v >
) ;
}
handleStartResize = ( ) => {
this . setState ( { resizing : true } ) ;
}
handleStopResize = ( ) => {
this . setState ( { resizing : false } ) ;
}
handleResize = ( event ) => {
if ( ! this . state . resizing ) {
return ;
}
const { pageX , currentTarget } = event ;
const { width , left } = currentTarget . getBoundingClientRect ( ) ;
const x = pageX - left ;
2016-12-21 15:12:40 +01:00
this . size = 100 * x / width ;
this . throttledResize ( ) ;
2016-11-11 15:00:04 +01:00
event . stopPropagation ( ) ;
}
2016-12-21 15:12:40 +01:00
applyResize = ( ) => {
this . setState ( { size : this . size } ) ;
}
2016-11-11 15:00:04 +01:00
}
function mapStateToProps ( state ) {
const { accounts } = state . personal ;
2017-01-09 11:14:36 +01:00
const { worker , error } = state . worker ;
2017-01-18 13:05:01 +01:00
return {
accounts ,
worker ,
workerError : error
} ;
2016-11-11 15:00:04 +01:00
}
export default connect (
2017-07-27 13:24:23 +02:00
mapStateToProps ,
null
2017-04-24 13:21:22 +02:00
) ( ContractDevelop ) ;