Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Integrated testrpc falback if no local geth
AtomSolidityView = require './ethereum-interface-view'
{CompositeDisposable} = require 'atom'
Web3 = require 'web3'
React = require 'react'
ReactDOM = require 'react-dom'
try
TestRPC = require "ethereumjs-testrpc";
catch a
console.log(a);
{MessagePanelView, PlainMessageView, LineMessageView} = require 'atom-message-panel'
Coinbase = ''
Password = ''
rpcAddress = atom.config.get('atom-ethereum-interface.rpcAddress')
console.log(web3,Web3,TestRPC);
if typeof web3 != 'undefined'
web3 = new Web3(web3.currentProvider)
else
web3 = new Web3(new (Web3.providers.HttpProvider)(rpcAddress))
console.log(web3);
module.exports = AtomSolidity =
atomSolidityView: null
modalPanel: null
subscriptions: null
activate: (state) ->
@atomSolidityView = new AtomSolidityView(state.atomSolidityViewState)
@modalPanel = atom.workspace.addRightPanel(item: @atomSolidityView.getElement(), visible: false)
atom.config.observe 'atom-ethereum-interface.rpcAddress', (newValue) ->
# TODO: add url validation
urlPattern = new RegExp('(http)://?')
if urlPattern.test(newValue)
rpcAddress = newValue
# Empty global variable compiled
@compiled = {}
# Events subscribed to in atom's system can be easily cleaned up with a CompositeDisposable
@subscriptions = new CompositeDisposable
# Register command that toggles this view
@subscriptions.add atom.commands.add 'atom-workspace', 'eth-interface:compile': => @compile()
@subscriptions.add atom.commands.add 'atom-workspace', 'eth-interface:build': => @build()
@subscriptions.add atom.commands.add 'atom-workspace', 'eth-interface:create': => @create()
@subscriptions.add atom.commands.add 'atom-workspace', 'eth-interface:toggle': => @toggleView()
deactivate: ->
@modalPanel.destroy()
@subscriptions.dispose()
@atomSolidityView.destroy()
serialize: ->
atomSolidityViewState: @atomSolidityView.serialize()
checkConnection: (callback)->
that = this
if !web3.isConnected
if typeof TestRPC != 'undefined'
web = web3.setProvider(TestRPC.provider());
callback(null, true)
else
callback('Error could not connect to local geth instance!', null)
else
# If passphrase is not already set
if Password == ''
# Set coinbase
# List all accounts and set selected as coinbase
accounts = web3.eth.accounts
that.getBaseAccount accounts, (err, callback) ->
if err
console.log err
else
Coinbase = callback.account
Password = callback.password
# Check if account is locked ? then prompt for password
that.checkUnlock (err, callback) ->
callback(null, true)
callback(null, true)
checkUnlock: (Coinbase, callback) ->
# web3.personal.unlockAccount("Coinbase", password)
console.log "In checkUnlock"
toggleView: ->
if @modalPanel.isVisible()
@modalPanel.hide()
else
@modalPanel.show()
showErrorMessage: (line, message, callback) ->
messages = new MessagePanelView(title: 'Solidity compiler messages')
messages.attach()
messages.add new LineMessageView(line: line, message: message, className: 'red-message')
getBaseAccount: (accounts, callback) ->
# Here we will select baseAccount for rest of the operations
# we will also get password for that account
that = this
createAddressList = React.createClass(
displayName: 'addressList'
getInitialState: ->
{ account: accounts[0], password: Password }
_handleChange: (event) ->
this.setState { account: event.target.value }
_handlePasswordChange: (event) ->
this.setState { password: event.target.value }
_handlePassword: (event) ->
event.preventDefault()
# Return account and password
callback(null, this.state)
render: ->
# create dropdown list for accounts
React.createElement 'div', { htmlFor: 'acc-n-pass', className: 'icon icon-link' },
React.createElement 'select', { onChange: this._handleChange, value: this.state.account }, accounts.map (account, i) ->
React.createElement 'option', { value: account }, account #options are address
React.createElement 'form', { onSubmit: this._handlePassword, className: 'icon icon-lock' },
React.createElement 'input', { type: 'password', uniqueName: "password", placeholder: "Password", value: this.state.password, onChange: this._handlePasswordChange }
React.createElement 'input', { type: 'submit', value: 'Unlock' }
)
ReactDOM.render React.createElement(createAddressList), document.getElementById('accounts-list')
callback(null, { account: accounts[0], password: '' })
compile: ->
that = this
editor = atom.workspace.getActiveTextEditor()
source = editor.getText()
@checkConnection (error, callback) ->
if error
console.error error
that.showErrorMessage 0, 'Error could not connect to local geth instance!'
else
web3.eth.defaultAccount = Coinbase
console.log "Using coinbase: " + web3.eth.defaultAccount
###
# TODO: Handle Compilation asynchronously and handle errors
###
that.compiled = web3.eth.compile.solidity(source)
# Clean View before creating
that.atomSolidityView.destroyCompiled()
console.log that.compiled
# Create inpus for every contract
for contractName of that.compiled
# Get estimated gas
estimatedGas = web3.eth.estimateGas { from: web3.eth.defaultAccount, data: that.compiled[contractName].code, gas: 1000000 }
###
# TODO: Use asynchronous call
web3.eth.estimateGas({from: '0xmyaccout...', data: "0xc6888fa1fffffffffff…..", gas: 500000 }, function(err, result){
if(!err && result !=== 500000) { … }
});
###
# contractName is the name of contract in JSON object
bytecode = that.compiled[contractName].code
# Get contract abi
ContractABI = that.compiled[contractName].info.abiDefinition
# get constructors for rendering display
inputs = []
for abiObj of ContractABI
if ContractABI[abiObj].type is "constructor" && ContractABI[abiObj].inputs.length > 0
inputs = ContractABI[abiObj].inputs
# Create view
that.atomSolidityView.setContractView(contractName, bytecode, ContractABI, inputs, estimatedGas)
# Show contract code
if not that.modalPanel.isVisible()
that.modalPanel.show()
return
build: ->
that = this
constructVars = []
i = 0
console.log @compiled
for contractName of @compiled
variables = []
estimatedGas = 0
if document.getElementById(contractName + '_create')
# contractName is the name of contract in JSON object
bytecode = @compiled[contractName].code
# Get contract abi
ContractABI = @compiled[contractName].info.abiDefinition
# Collect variable inputs
inputVars = if document.getElementById(contractName + '_inputs') then document.getElementById(contractName + '_inputs').getElementsByTagName('input')
if inputVars
while i < inputVars.length
if inputVars.item(i).getAttribute('id') == contractName + '_gas'
estimatedGas = inputVars.item(i).value
inputVars.item(i).readOnly = true
break
inputObj = {
"varName": inputVars.item(i).getAttribute('id'),
"varValue": inputVars.item(i).value
}
variables[i] = inputObj
inputVars.item(i).readOnly = true
if inputVars.item(i).nextSibling.getAttribute('id') == contractName + '_create'
break
else
i++
constructVars[contractName] = {
'contractName': contractName,
'inputVariables': variables,
'estimatedGas': estimatedGas
}
# Create React element for create button
createButton = React.createClass(
displayName: 'createButton'
_handleSubmit: ->
console.log constructVars
that.create(that.compiled[Object.keys(this.refs)[0]].info.abiDefinition, that.compiled[Object.keys(this.refs)[0]].code, constructVars[Object.keys(this.refs)[0]], Object.keys(this.refs)[0], constructVars[Object.keys(this.refs)[0]].estimatedGas)
render: ->
React.createElement('form', { onSubmit: this._handleSubmit },
React.createElement('input', {type: 'submit', value: 'Create', ref: contractName, className: 'btn btn-primary inline-block-tight'}, null))
)
ReactDOM.render React.createElement(createButton, null), document.getElementById(contractName + '_create')
prepareEnv: (contractName, callback) ->
if document.getElementById(@contractName + '_create')
document.getElementById(@contractName + '_create').style.visibility = 'hidden'
document.getElementById(@contractName + '_stat').innerText = 'transaction sent, waiting for confirmation...'
callback(null, true)
else
e = new Error('Could not parse input')
callback(e, null)
# our asyncLoop
asyncLoop: (iterations, func, callback) ->
index = 0
done = false
cycle =
next: ->
if done
return
if index < iterations
index++
func cycle
else
done = true
callback()
iteration: ->
index - 1
break: ->
done = true
callback()
cycle.next()
cycle
# Construct function buttons from abi
constructFunctions: (@contractABI, callback) ->
for contractFunction in contractABI
if contractFunction.type = 'function' and contractFunction.name != null and contractFunction.name != undefined
@createChilds contractFunction, (error, childInputs) ->
if !error
callback(null, [contractFunction.name, childInputs])
else
callback(null, [null, null])
createChilds: (contractFunction, callback) ->
reactElements = []
i = 0
if contractFunction.inputs.length > 0
while i < contractFunction.inputs.length
reactElements[i] = [contractFunction.inputs[i].type, contractFunction.inputs[i].name]
i++
callback(null, reactElements)
# Construct react child inputs
create: (@abi, @code, @constructVars, @contractName, @estimatedGas) ->
that = this
@estimatedGas = if @estimatedGas > 0 then @estimatedGas else 1000000
if Password == ''
e = new Error('Empty password')
console.error ("Empty password")
@showErrorMessage 0, "No password provided"
return
# hide create button
@prepareEnv @contractName, (err, callback) ->
if err
console.error err
else
# Use coinbase
web3.eth.defaultAccount = Coinbase
console.log "Using coinbase: " + web3.eth.defaultAccount
# set variables and render display
constructorS = []
for i in that.constructVars.inputVariables
constructorS.push i.varValue
web3.personal.unlockAccount(web3.eth.defaultAccount, Password)
web3.eth.contract(that.abi).new constructorS.toString(), { data: that.code, from: web3.eth.defaultAccount, gas: that.estimatedGas }, (err, contract) ->
if err
console.error err
that.showErrorMessage 129, err
return
# callback fires twice, we only want the second call when the contract is deployed
else if contract.address
myContract = contract
console.log 'address: ' + myContract.address
document.getElementById(that.contractName + '_stat').innerText = 'Mined!'
document.getElementById(that.contractName + '_stat').setAttribute('class', 'icon icon-zap') # Add icon class
document.getElementById(that.contractName + '_address').innerText = myContract.address
document.getElementById(that.contractName + '_address').setAttribute('class', 'icon icon-key') # Add icon class
# Check every key, if it is a function create call buttons,
# for every function there could be many call methods,
# for every method there cpould be many inputs
# Innermost callback will have inputs for all abi objects
# Lets think the Innermost function
# Construct view for function call view
functionABI = React.createClass(
displayName: 'callFunctions'
getInitialState: ->
{ childFunctions: [] }
componentDidMount: ->
self = this
that.constructFunctions that.abi, (error, childFunctions) ->
if !error
self.state.childFunctions.push(childFunctions)
self.forceUpdate()
_handleChange: (childFunction, event) ->
console.log event.target.value
this.setState { value: event.target.value }
_handleSubmit: (childFunction, event) ->
# Get arguments ready here
that.argsToArray this.refs, childFunction, (error, argArray) ->
if !error
that.call(myContract, childFunction, argArray)
render: ->
self = this
React.createElement 'div', { htmlFor: 'contractFunctions' }, this.state.childFunctions.map((childFunction, i) ->
React.createElement 'form', { onSubmit: self._handleSubmit.bind(this, childFunction[0]), key: i, ref: childFunction[0] },
React.createElement 'input', { type: 'submit', readOnly: 'true', value: childFunction[0], className: 'text-subtle call-button' }
childFunction[1].map((childInput, j) ->
React.createElement 'input', { tye: 'text', handleChange: self._handleChange, placeholder: childInput[0] + ' ' + childInput[1], className: 'call-button-values' }#, ref: if childFunction[0] then childFunction[0][j] else "Constructor" }
)
)
)
ReactDOM.render React.createElement(functionABI), document.getElementById(that.contractName + '_call')
else if !contract.address
contractStat = React.createClass(
render: ->
React.createElement 'div', { htmlFor: 'contractStat' },
React.createElement 'span', { className: 'inline-block highlight' }, 'TransactionHash: '
React.createElement 'pre', { className: 'large-code' }, contract.transactionHash
React.createElement 'span', { className: 'stat-mining stat-mining-align' }, 'waiting to be mined '
React.createElement 'span', { className: 'loading loading-spinner-tiny inline-block stat-mining-align' }
)
ReactDOM.render React.createElement(contractStat), document.getElementById(that.contractName + '_stat')
# document.getElementById(that.contractName + '_stat').innerText = "Contract transaction send: TransactionHash: " + contract.transactionHash + " waiting to be mined..."
console.log "Contract transaction send: TransactionHash: " + contract.transactionHash + " waiting to be mined..."
showOutput: (address, output) ->
messages = new MessagePanelView(title: 'Solidity compiler output')
messages.attach()
address = 'Contract address: ' + address
output = 'Contract output: ' + output
messages.add new PlainMessageView(message: address, className: 'green-message')
messages.add new PlainMessageView(message: output, className: 'green-message')
argsToArray: (@reactElements, @childFunction, callback) ->
that = this
# For every childNodes of childFunction
# Get value of childFunction
# Trim value having name of the function
args = new Array()
@asyncLoop @reactElements[@childFunction].childNodes.length, ((cycle) ->
if that.reactElements[that.childFunction][cycle.iteration()].type != 'submit'
args.push(that.reactElements[that.childFunction][cycle.iteration()].value)
cycle.next()
), ->
callback(null, args)
checkArray: (@arguments, callback) ->
# TODO: Check for empty elements and remove them
# TODO: remove any unwanted element that has no text in it
callback(null, @arguments)
call: (@myContract, @functionName, @arguments) ->
that = this
console.log @myContract
console.log @functionName
console.log @arguments
@checkArray @arguments, (error, args) ->
if !error
if args.length > 0
web3.personal.unlockAccount(web3.eth.defaultAccount, Password)
result = that.myContract[that.functionName].apply(this, args)
else
web3.personal.unlockAccount(web3.eth.defaultAccount, Password)
result = that.myContract[that.functionName]()
console.log result
that.showOutput that.myContract.address, result
toggle: ->
if @modalPanel.isVisible()
@modalPanel.hide()
else
@modalPanel.show()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.