diff --git a/README.md b/README.md index bf404c7..582710f 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ [![Greenkeeper badge](https://badges.greenkeeper.io/0mkara/etheratom.svg)](https://greenkeeper.io/) [![Build Status](https://travis-ci.org/0mkara/etheratom.svg?branch=master)](https://travis-ci.org/0mkara/etheratom) +[![telegram](https://png.icons8.com/color/24/000000/telegram-app.png)](https://t.me/etheratom) Etheratom is a package for hackable Atom editor. It uses web3js to interact with Ethereum node. diff --git a/contracts/main.sol b/contracts/main.sol new file mode 100644 index 0000000..97f76f7 --- /dev/null +++ b/contracts/main.sol @@ -0,0 +1,18 @@ +pragma solidity ^0.4.23; +import '../contracts/mortal.sol'; + +contract Main is Mortal { + mapping (bytes32 => string) public fiddle_data; + event NewFiddle(address indexed _user, bytes32 indexed _id); + + // share some given input + function share(string _code) public { + bytes32 _id = keccak256(msg.sender, _code); + fiddle_data[_id] = _code; + emit NewFiddle(msg.sender, _id); + } + // get code for given bytes32 id + function get_fiddle(bytes32 _id) view public returns (string) { + return fiddle_data[_id]; + } +} diff --git a/contracts/mortal.sol b/contracts/mortal.sol new file mode 100644 index 0000000..51d1631 --- /dev/null +++ b/contracts/mortal.sol @@ -0,0 +1,12 @@ +pragma solidity ^0.4.18; + +contract Mortal { + /* Define variable owner of the type address */ + address owner; + + /* This function is executed at initialization and sets the owner of the contract */ + function mortal() public { owner = msg.sender; } + + /* Function to recover the funds on the contract */ + function kill() public { if (msg.sender == owner) selfdestruct(owner); } +} diff --git a/lib/components/FunctionABI/index.js b/lib/components/FunctionABI/index.js index 72a3054..a63e2c8 100644 --- a/lib/components/FunctionABI/index.js +++ b/lib/components/FunctionABI/index.js @@ -37,7 +37,8 @@ class FunctionABI extends React.Component { const contract = instances[contractName]; try { const result = await this.helpers.call({ coinbase, password, contract, abiItem }); - this.helpers.showOutput({ address: contract.options.address, data: result }); + const block = await this.helpers.getBlock(result.blockNumber); + this.helpers.showOutput({ address: contract.options.address, data: result, block: block }); } catch(e) { console.log(e); this.helpers.showPanelError(e); @@ -54,7 +55,13 @@ class FunctionABI extends React.Component { } } const result = await this.helpers.call({ coinbase, password, contract, abiItem: methodItem, params }); + if(result.blockNumber) { + const block = await this.helpers.getBlock(result.blockNumber); + this.helpers.showOutput({ address: contract.options.address, data: result, block: block }); + return; + } this.helpers.showOutput({ address: contract.options.address, data: result }); + return; } catch (e) { console.log(e); this.helpers.showPanelError(e); diff --git a/lib/components/ShareBox/index.js b/lib/components/ShareBox/index.js new file mode 100644 index 0000000..b25ed29 --- /dev/null +++ b/lib/components/ShareBox/index.js @@ -0,0 +1,207 @@ +'use babel' +// Copyright 2018 Etheratom Authors +// This file is part of Etheratom. + +// Etheratom 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. + +// Etheratom 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 Etheratom. If not, see . +import React from 'react' +import { connect } from 'react-redux' +import { Collapse } from 'react-collapse' + +class ShareBox extends React.Component { + constructor(props) { + super(props); + this.helpers = props.helpers; + this.state = { + shared: false, + shareLink: null, + gas: 0, + gasLimit: 0, + snippet: null, + txHash: null, + code: null, + isOpened: false, + toggleBtnStyle: 'btn icon icon-unfold inline-block-tight', + togglePreviewTxt: 'Preview Snippet' + } + this._handleShare = this._handleShare.bind(this); + this._handleLoad = this._handleLoad.bind(this); + this._handleCopy = this._handleCopy.bind(this); + this._handleChange = this._handleChange.bind(this); + this._handleGasChange = this._handleGasChange.bind(this); + this._togglePreview = this._togglePreview.bind(this); + this.watchSyncEvents(); + } + async watchSyncEvents() { + const that = this; + const { coinbase } = this.props; + that.helpers.snptEvents.NewFiddle({ filter: { _user: coinbase } }) + .on('data', data => { + console.log(data); + const fiddleId = data.returnValues._id; + that.setState({ shared: true, shareLink: fiddleId }); + }) + .on('error', e => { + throw e; + }) + } + async componentDidMount() { + const { coinbase } = this.props; + atom.workspace.observeActiveTextEditor(async (editor) => { + if(!editor || !editor.getBuffer()) { + return + } + const code = editor.getText(); + const maxGas = await this.helpers.getGasLimit(); + this.setState({ code, gasLimit: maxGas }); + try { + const gasEstm = await this.helpers.getShareGasEstm(coinbase, code); + this.setState({ gas: gasEstm }); + } catch(e) { + this.setState({ gas: 0 }); + throw e; + } + return; + }); + } + async _handleShare() { + const that = this; + const { coinbase, password } = this.props; + const { gas, code } = this.state; + this.setState({ txHash: null, shareLink: null, snippet: null }); + that.helpers.shareCode(coinbase, password, code, gas) + .then(txHash => { + this.setState({ txHash: txHash }); + }) + .catch(e => { + throw e; + }); + } + async _handleLoad() { + try { + const { shareLink } = this.state; + const { coinbase } = this.props; + const code = await this.helpers.getSnippet(coinbase, shareLink); + this.setState({ snippet: code }); + } catch(e) { + throw e; + } + } + async _handleCopy() { + const { shareLink } = this.state; + atom.clipboard.write(shareLink); + } + async _handleChange(event) { + this.setState({ shared: false, shareLink: event.target.value }); + } + async _handleGasChange(event) { + this.setState({ gas: event.target.value }); + } + _togglePreview() { + const { isOpened } = this.state; + this.setState({ isOpened: !isOpened }); + if(!isOpened) { + this.setState({ + toggleBtnStyle: 'btn btn-success icon icon-fold inline-block-tight', + togglePreviewTxt: 'Hide Snippet' + }); + } else { + this.setState({ + toggleBtnStyle: 'btn icon icon-unfold inline-block-tight', + togglePreviewTxt: 'Preview Snippet' + }); + } + } + render() { + const { shareLink, shared, gas, snippet, txHash, code, toggleBtnStyle, togglePreviewTxt, isOpened, gasLimit } = this.state; + return ( +
+
+ + { + !shared && + + } + { + shared && + + } +
+
+
+ + +
+
+ Adjust gas estimate. + Max: { gasLimit } + +
+ { + txHash && +
+ Transaction: { txHash } +
+ } +
+ + { + code && +
+
+                                {code}
+                            
+
+ } +
+ { + snippet && +
+
+                            {snippet}
+                        
+
+ } +
+ ); + } +} + +const mapStateToProps = ({ account }) => { + const { coinbase, password } = account; + return { coinbase, password }; +} + +export default connect(mapStateToProps, {})(ShareBox); diff --git a/lib/components/TabView/index.js b/lib/components/TabView/index.js index 4c58f86..288fd72 100644 --- a/lib/components/TabView/index.js +++ b/lib/components/TabView/index.js @@ -21,6 +21,8 @@ import Contracts from '../Contracts' import TxAnalyzer from '../TxAnalyzer' import Events from '../Events' import NodeControl from '../NodeControl' +import Web3Utilities from '../Web3Utilities' +import ShareBox from '../ShareBox' class TabView extends React.Component { constructor(props) { @@ -79,6 +81,12 @@ class TabView extends React.Component { } + +
Web3 Utilities
+
+ +
Share
+
Node
@@ -97,6 +105,12 @@ class TabView extends React.Component { + + + + + + @@ -106,6 +120,9 @@ class TabView extends React.Component {

Etheratom news #Etheratom

+

+ Etheratom telegram t.me/etheratom +

Contact: 0mkar@protonmail.com

diff --git a/lib/components/TxAnalyzer/index.js b/lib/components/TxAnalyzer/index.js index 0185ab5..0ee3147 100644 --- a/lib/components/TxAnalyzer/index.js +++ b/lib/components/TxAnalyzer/index.js @@ -80,7 +80,13 @@ class TxAnalyzer extends React.Component {
- +
diff --git a/lib/components/Web3Utilities/index.js b/lib/components/Web3Utilities/index.js new file mode 100644 index 0000000..33420d8 --- /dev/null +++ b/lib/components/Web3Utilities/index.js @@ -0,0 +1,290 @@ +'use babel' +// Copyright 2018 Etheratom Authors +// This file is part of Etheratom. + +// Etheratom 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. + +// Etheratom 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 Etheratom. If not, see . +import React from 'react' +import { connect } from 'react-redux' +import { Collapse } from 'react-collapse' +import ReactJson from 'react-json-view' +import VirtualList from 'react-tiny-virtual-list' + +class Web3Utilities extends React.Component { + constructor(props) { + super(props); + this.helpers = props.helpers; + this.state = { + input: null, + output: null + }; + this._hexToUtf8 = this._hexToUtf8.bind(this); + this._handleInputChange = this._handleInputChange.bind(this); + this._toHex = this._toHex.bind(this); + this._toChecksumAddress = this._toChecksumAddress.bind(this); + this._hexToBytes = this._hexToBytes.bind(this); + this._hexToNumber = this._hexToNumber.bind(this); + this._bytesToHex = this._bytesToHex.bind(this); + this._padLeft32 = this._padLeft32.bind(this); + this._padRight32 = this._padRight32.bind(this); + this._padLeft64 = this._padLeft64.bind(this); + this._padRight64 = this._padRight64.bind(this); + this._hexToAscii = this._hexToAscii.bind(this); + // abi encoders & decoders + this._encFuncSig = this._encFuncSig.bind(this); + this._encEvntSig = this._encEvntSig.bind(this); + this._encFuncCall = this._encFuncCall.bind(this); + this._encParams = this._encParams.bind(this); + this._decParams = this._decParams.bind(this); + this._decLog = this._decLog.bind(this); + } + _handleInputChange(event) { + this.setState({ input: event.target.value }); + } + async _hexToUtf8() { + try { + const { input } = this.state; + const decoded = await this.helpers.hexToUtf8(input); + this.setState({ output: decoded }); + } catch(e) { + this.helpers.showPanelError(e); + } + } + async _hexToAscii() { + try { + const { input } = this.state; + const ascii = await this.helpers.hexToAscii(input); + this.setState({ output: ascii }); + } catch(e) { + this.helpers.showPanelError(e); + } + } + async _toHex() { + try { + const { input } = this.state; + const encoded = await this.helpers.toHex(input); + this.setState({ output: encoded }); + } catch(e) { + this.helpers.showPanelError(e); + } + } + async _toChecksumAddress() { + try { + const { input } = this.state; + const chkSmAddr = await this.helpers.toChecksumAddress(input); + this.setState({ output: chkSmAddr }); + } catch(e) { + this.helpers.showPanelError(e); + } + } + async _hexToNumber() { + try { + const { input } = this.state; + const number = await this.helpers.hexToNumber(input); + this.setState({ output: number }); + } catch(e) { + this.helpers.showPanelError(e); + } + } + async _hexToBytes() { + try { + const { input } = this.state; + const bytes = await this.helpers.hexToBytes(input); + this.setState({ output: bytes }); + } catch(e) { + this.helpers.showPanelError(e); + } + } + async _bytesToHex() { + try { + const { input } = this.state; + const hex = await this.helpers.bytesToHex(input); + this.setState({ output: hex }); + } catch(e) { + this.helpers.showPanelError(e); + } + } + async _padLeft32() { + try { + const { input } = this.state; + const leftPadded = await this.helpers.padLeft(input, 32); + this.setState({ output: leftPadded }); + } catch(e) { + this.helpers.showPanelError(e); + } + } + async _padRight32() { + try { + const { input } = this.state; + const rightPadded = await this.helpers.padRight(input, 32); + this.setState({ output: rightPadded }); + } catch(e) { + this.helpers.showPanelError(e); + } + } + async _padLeft64() { + try { + const { input } = this.state; + const leftPadded = await this.helpers.padLeft(input, 64); + this.setState({ output: leftPadded }); + } catch(e) { + this.helpers.showPanelError(e); + } + } + async _padRight64() { + try { + const { input } = this.state; + const rightPadded = await this.helpers.padRight(input, 64); + this.setState({ output: rightPadded }); + } catch(e) { + this.helpers.showPanelError(e); + } + } + async _encFuncSig() { + try { + const { input } = this.state; + const encodedFn = await this.helpers.encFuncSig(input); + this.setState({ output: encodedFn }); + } catch(e) { + this.helpers.showPanelError(e); + } + } + async _encEvntSig() { + try { + const { input } = this.state; + const encodedEvnt = await this.helpers.encEvntSig(input); + this.setState({ output: encodedEvnt }); + } catch(e) { + this.helpers.showPanelError(e); + } + } + async _encFuncCall() { + try { + const { input } = this.state; + const encodedFnCl = await this.helpers.encFuncCall(input); + this.setState({ output: encodedFnCl }); + } catch(e) { + this.helpers.showPanelError(e); + } + } + async _encParams() { + try { + const { input } = this.state; + const encodedParams = await this.helpers.encParams(input); + this.setState({ output: encodedParams }); + } catch(e) { + this.helpers.showPanelError(e); + } + } + async _decParams() { + try { + const { input } = this.state; + const decodedParams = await this.helpers.decParams(input); + this.setState({ output: decodedParams }); + } catch(e) { + this.helpers.showPanelError(e); + } + } + async _decLog() { + try { + const { input } = this.state; + const decodedLog = await this.helpers.decLog(input); + this.setState({ output: decodedLog }); + } catch(e) { + this.helpers.showPanelError(e); + } + } + render() { + return ( +
+
+
+ +
+
+ + + + + + + + + + + +
+
+ + + + + + +
+
+ { + this.state.output && +
+
{this.state.output}
+
+ } +
+ ); + } +} +const mapStateToProps = () => { + return { }; +} + +export default connect(mapStateToProps, {})(Web3Utilities); diff --git a/lib/ethereum-interface-view.js b/lib/ethereum-interface-view.js index 8d9d637..e45f90a 100644 --- a/lib/ethereum-interface-view.js +++ b/lib/ethereum-interface-view.js @@ -104,6 +104,13 @@ export default AtomSolidityView = (() => { buttonNode.appendChild(compileButton); mainNode.appendChild(buttonNode); + /*shareNode = document.createElement('div'); + att = document.createAttribute('id'); + att.value = 'share-box'; + shareNode.setAttributeNode(att); + shareNode.classList.add('block'); + mainNode.appendChild(shareNode);*/ + tabNode = document.createElement('div'); att = document.createAttribute('id'); att.value = 'tab_view'; diff --git a/lib/web3/methods.js b/lib/web3/methods.js index 2891172..65665ca 100644 --- a/lib/web3/methods.js +++ b/lib/web3/methods.js @@ -18,14 +18,28 @@ // methods.js are collection of various functions used to execute calls on web3 import Solc from 'solc' import Web3 from 'web3' -import ethJSABI from 'ethereumjs-abi' -import EthJSTX from 'ethereumjs-tx' import EventEmitter from 'events' import { MessagePanelView, PlainMessageView, LineMessageView } from 'atom-message-panel' export default class Web3Helpers { constructor(web3) { this.web3 = web3; + + // Etheratom contract, bytecode & abi + this.bytecode = '608060405234801561001057600080fd5b5061051f806100206000396000f30060806040526004361061006c5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166327b53401811461007157806341c0e1b5146100fe57806348c5e7bc146101155780637f630b4c1461012d578063f1eae25c14610186575b600080fd5b34801561007d57600080fd5b5061008960043561019b565b6040805160208082528351818301528351919283929083019185019080838360005b838110156100c35781810151838201526020016100ab565b50505050905090810190601f1680156100f05780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561010a57600080fd5b50610113610235565b005b34801561012157600080fd5b50610089600435610276565b34801561013957600080fd5b506040805160206004803580820135601f81018490048402850184019095528484526101139436949293602493928401919081908401838280828437509497506103199650505050505050565b34801561019257600080fd5b50610113610421565b60016020818152600092835260409283902080548451600294821615610100026000190190911693909304601f810183900483028401830190945283835291929083018282801561022d5780601f106102025761010080835404028352916020019161022d565b820191906000526020600020905b81548152906001019060200180831161021057829003601f168201915b505050505081565b6000543373ffffffffffffffffffffffffffffffffffffffff908116911614156102745760005473ffffffffffffffffffffffffffffffffffffffff16ff5b565b60008181526001602081815260409283902080548451600260001995831615610100029590950190911693909304601f8101839004830284018301909452838352606093909183018282801561030d5780601f106102e25761010080835404028352916020019161030d565b820191906000526020600020905b8154815290600101906020018083116102f057829003601f168201915b50505050509050919050565b60003382604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c0100000000000000000000000002815260140182805190602001908083835b6020831061038d5780518252601f19909201916020918201910161036e565b51815160209384036101000a600019018019909216911617905260408051929094018290039091206000818152600183529390932088519397506103d996509450870192506104589050565b50604051819073ffffffffffffffffffffffffffffffffffffffff3316907f6eb913f8381f04b5834f6f06c6b4b88df26f76a9ca6796ccd1c423a8281c851c90600090a35050565b6000805473ffffffffffffffffffffffffffffffffffffffff19163373ffffffffffffffffffffffffffffffffffffffff16179055565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061049957805160ff19168380011785556104c6565b828001600101855582156104c6579182015b828111156104c65782518255916020019190600101906104ab565b506104d29291506104d6565b5090565b6104f091905b808211156104d257600081556001016104dc565b905600a165627a7a72305820069778fb50b89b88f77ce5e177c54524efd1ecec6ef957ce6f7b164d392706d60029'; + this.createSnptContract(); + } + async createSnptContract() { + const abi = [{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"fiddle_data","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"kill","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_id","type":"bytes32"}],"name":"get_fiddle","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_code","type":"string"}],"name":"share","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"mortal","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_user","type":"address"},{"indexed":true,"name":"_id","type":"bytes32"}],"name":"NewFiddle","type":"event"}]; + const SnippetServiceAddr = "0xBb114b958D1A3a0Ca6Ec260e11dc66Ba14a845bA"; + const gasPrice = await this.web3.eth.getGasPrice(); + this.snptContract = await new this.web3.eth.Contract(abi, SnippetServiceAddr, { + data: '0x' + this.bytecode, + gasPrice: this.web3.utils.toHex(gasPrice) + }); + } + get snptEvents() { + return this.snptContract.events; } async compileWeb3(sources) { // compile solidity using solcjs @@ -81,7 +95,7 @@ export default class Web3Helpers { } async getSyncStat() { try { - return this.web3.eth.isSyncing(); + return await this.web3.eth.isSyncing(); } catch(e) { throw e; } @@ -107,7 +121,7 @@ export default class Web3Helpers { try { const gasPrice = await this.web3.eth.getGasPrice(); const contract = await new this.web3.eth.Contract(abi, { - from: this.web3.eth.defaultAccount, + from: coinbase, data: '0x' + code, gas: this.web3.utils.toHex(gasSupply), gasPrice: this.web3.utils.toHex(gasPrice) @@ -162,13 +176,14 @@ export default class Web3Helpers { } async call({...arguments}) { console.log("%c Web3 calling functions... ", 'background: rgba(36, 194, 203, 0.3); color: #EF525B'); + console.log(arguments); + const that = this; const coinbase = arguments.coinbase; const password = arguments.password; const contract = arguments.contract; const abiItem = arguments.abiItem; var params = arguments.params || []; - this.web3.eth.defaultAccount = coinbase; try { // Prepare params for call params = params.map(param => { @@ -183,6 +198,7 @@ export default class Web3Helpers { // Handle fallback if(abiItem.type === 'fallback') { + console.log("%c Calling fallback function... ", 'background: rgba(36, 194, 203, 0.3); color: #EF525B'); if(password) { await this.web3.eth.personal.unlockAccount(coinbase, password); } @@ -191,25 +207,31 @@ export default class Web3Helpers { to: contract.options.address, value: abiItem.payableValue || 0 }) + console.log(result); return result; } - - if(abiItem.constant === false || abiItem.payable === true) { - if(password) { - await this.web3.eth.personal.unlockAccount(coinbase, password); - } - if(params.length > 0) { - const result = await contract.methods[abiItem.name](...params).send({ from: coinbase, value: abiItem.payableValue }); - return result; - } - const result = await contract.methods[abiItem.name]().send({ from: coinbase, value: abiItem.payableValue }); - return result; - } - if(params.length > 0) { + // Handle constant calls + if(abiItem.constant === true) { + console.log("%c Calling constant function default... ", 'background: rgba(36, 194, 203, 0.3); color: #EF525B'); const result = await contract.methods[abiItem.name](...params).call({ from: coinbase }); + console.log(result); return result; } - const result = await contract.methods[abiItem.name]().call({ from: coinbase }); + // handle default payable non-constant methods + if(password) { + await this.web3.eth.personal.unlockAccount(coinbase, password); + } + console.log("%c Calling payable function... ", 'background: rgba(36, 194, 203, 0.3); color: #EF525B'); + const result = await contract.methods[abiItem.name](...params).send({ from: coinbase, value: abiItem.payableValue }) + .on('transactionHash', (hash) => { + // show method tx hash & pending loader + console.log(hash); + that.showPanelTx(hash); + }) + .on('receipt', (receipt) => { + return receipt; + }) + console.log(result); return result; } catch(e) { @@ -238,17 +260,33 @@ export default class Web3Helpers { messages.attach(); messages.add(new PlainMessageView({ message: err_message, className: 'red-message' })); } + showPanelTx(txHash) { + const messages = new MessagePanelView({ title: 'Etheratom transaction' }); + messages.attach(); + messages.add(new PlainMessageView({ message: 'New transaction: ' + txHash, className: 'green-message' })); + return; + } showOutput({...arguments}) { const address = arguments.address; const data = arguments.data; + const block = arguments.block; + const messages = new MessagePanelView({ title: 'Etheratom output' }); messages.attach(); messages.add(new PlainMessageView({ message: 'Contract address: ' + address, className: 'green-message' })); + if(block) { + const rawMessage = `
Block:
${JSON.stringify(block, null, 4)}
`; + messages.add(new PlainMessageView({ + message: rawMessage, + raw: true, + className: 'green-message' + })); + } if(data instanceof Object) { - const rawMessage = `
Contract output:
${JSON.stringify(data, null, 4)}
` + const rawMessage = `
Contract output:
${JSON.stringify(data, null, 4)}
`; messages.add(new PlainMessageView({ message: rawMessage, raw: true, @@ -284,22 +322,154 @@ export default class Web3Helpers { async getAccounts() { try { return await this.web3.eth.getAccounts(); - } catch (e) { + } catch(e) { throw e; } } async getMining() { try { return await this.web3.eth.isMining(); - } catch (e) { + } catch(e) { throw e; } } async getHashrate() { try { return await this.web3.eth.getHashrate(); - } catch (e) { + } catch(e) { + throw e; + } + } + async getBlock(blockNumber) { + try { + return await this.web3.eth.getBlock(blockNumber); + } catch(e) { + throw e; + } + } + async hexToUtf8(input) { + try { + return await this.web3.utils.hexToUtf8(input); + } catch(e) { + throw e; + } + } + async hexToAscii(input) { + try { + return await this.web3.utils.hexToAscii(input); + } catch(e) { + throw e; + } + } + async toHex(input) { + try { + return await this.web3.utils.toHex(input); + } catch(e) { throw e; } } + async toChecksumAddress(input) { + try { + return await this.web3.utils.toChecksumAddress(input); + } catch(e) { + throw e; + } + } + async hexToNumber(input) { + try { + return await this.web3.utils.hexToNumber(input); + } catch(e) { + throw e; + } + } + async hexToBytes(input) { + try { + return await this.web3.utils.hexToBytes(input); + } catch(e) { + throw e; + } + } + async bytesToHex(input) { + try { + return await this.web3.utils.bytesToHex(input); + } catch(e) { + throw e; + } + } + async padLeft(input, padding) { + try { + return await this.web3.utils.padLeft(input, padding); + } catch(e) { + throw e; + } + } + async padRight(input, padding) { + try { + return await this.web3.utils.padRight(input, padding); + } catch(e) { + throw e; + } + } + // Code sharing + async getShareGasEstm(coinbase, code) { + const maxGas = await this.getGasLimit(); + return await this.snptContract.methods.share(code).estimateGas({ gas: maxGas, from: coinbase }); + } + async shareCode(coinbase, password, code, gas) { + const that = this; + const { bytecode, abi, SnippetServiceAddr } = this; + const { snptContract } = this; + + return new Promise(async (resolve, reject) => { + if(!coinbase) { + const error = new Error('No coinbase selected!'); + reject(error); + } + try { + if(password) { + const unlocked = await that.web3.eth.personal.unlockAccount(coinbase, password); + } + snptContract.options.from = coinbase; + snptContract.options.gas = that.web3.utils.toHex(gas); + + snptContract.methods.share(code).send({ from: coinbase }) + .on('transactionHash', txHash => { + resolve(txHash); + }) + .on('error', e => { + reject(e); + }); + } catch (e) { + console.log(e); + reject(e); + } + }); + } + async getSnippet(coinbase, id) { + const { snptContract } = this; + try { + return await snptContract.methods.get_fiddle(id).call({ from: coinbase }); + } catch(e) { + throw e; + } + } + // abi encoders & decoders + async encFuncSig(input) { + return await this.web3.eth.abi.encodeFunctionSignature(input); + } + async encEvntSig(input) { + return await this.web3.eth.abi.encodeEventSignature(input); + } + async encFuncCall(input) { + return await this.web3.eth.abi.encodeFunctionCall(input); + } + async encParams(input) { + return await this.web3.eth.abi.encodeParameters(input); + } + async decParams(input) { + return await this.web3.eth.abi.decodeParameters(input); + } + async decLog(input) { + return await this.web3.eth.abi.decodeLog(input); + } } diff --git a/lib/web3/web3.js b/lib/web3/web3.js index 8d4b37b..330d7a2 100644 --- a/lib/web3/web3.js +++ b/lib/web3/web3.js @@ -142,8 +142,8 @@ export default class Web3Env { // newBlockHeaders subscriber this.web3.eth.subscribe('newBlockHeaders') .on("data", (blocks) => { - console.log("%c newBlockHeaders:data ", 'background: rgba(36, 194, 203, 0.3); color: #EF525B'); - console.log(blocks); + //console.log("%c newBlockHeaders:data ", 'background: rgba(36, 194, 203, 0.3); color: #EF525B'); + //console.log(blocks); }) .on('error', (e) => { console.log("%c newBlockHeaders:error ", 'background: rgba(36, 194, 203, 0.3); color: #EF525B'); @@ -152,8 +152,8 @@ export default class Web3Env { // pendingTransactions subscriber this.web3.eth.subscribe('pendingTransactions') .on("data", (transaction) => { - console.log("%c pendingTransactions:data ", 'background: rgba(36, 194, 203, 0.3); color: #EF525B'); - console.log(transaction); + //console.log("%c pendingTransactions:data ", 'background: rgba(36, 194, 203, 0.3); color: #EF525B'); + //console.log(transaction); this.store.dispatch({ type: ADD_PENDING_TRANSACTION, payload: transaction }); }) .on('error', (e) => { @@ -241,7 +241,7 @@ export default class Web3Env { if(!this.web3Subscriptions) { return } - this.compileSubscriptions.add(atom.workspace.observeTextEditors((editor) => { + this.compileSubscriptions.add(atom.workspace.observeActiveTextEditor((editor) => { if(!editor || !editor.getBuffer()) { return } @@ -270,7 +270,6 @@ export default class Web3Env { var sources = {}; sources[filename] = { content: editor.getText() } sources = await combineSource(dir, sources); - console.log(sources); try { // Reset redux store this.store.dispatch({ type: SET_COMPILED, payload: null }); diff --git a/styles/atom-solidity.less b/styles/atom-solidity.less index a08dea7..b6c248a 100644 --- a/styles/atom-solidity.less +++ b/styles/atom-solidity.less @@ -45,6 +45,17 @@ width: 100%; margin-top: 10px; padding: 0px 8px; + + .padded { + padding: 5px; + } + } + + .row-thin { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; } .icon { @@ -312,3 +323,7 @@ .no-header { text-align: center; } + +.gas-supply { + width: 25%; +}