Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save adibas03/8a934b76c342136eb0f51396a23981c6 to your computer and use it in GitHub Desktop.
Save adibas03/8a934b76c342136eb0f51396a23981c6 to your computer and use it in GitHub Desktop.

Revisions

  1. adibas03 created this gist Sep 9, 2016.
    413 changes: 413 additions & 0 deletions .atom\packages\atom-ethereum-interface\lib\ethereum-interface.coffee
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,413 @@
    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()