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 &&
+
+ }
+
+ );
+ }
+}
+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%;
+}