From 2524879e96176ced5422814e249b03409fb1165f Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Mon, 24 Dec 2018 11:35:06 -0500 Subject: [PATCH 01/50] remove unused variable --- src/app.js | 3 +-- src/universal-dapp.js | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/app.js b/src/app.js index a0bcec313e..1031227f92 100644 --- a/src/app.js +++ b/src/app.js @@ -314,7 +314,6 @@ Please make a backup of your contracts and start using http://remix.ethereum.org // ----------------- UniversalDApp ----------------- var udapp = new UniversalDApp({ - removable: false, removable_instances: true }) registry.put({api: udapp, name: 'udapp'}) @@ -434,7 +433,7 @@ Please make a backup of your contracts and start using http://remix.ethereum.org self._components.righthandpanel.init() self._components.righthandpanel.event.register('resize', delta => self._adjustLayout('right', delta)) - var txLogger = new TxLogger() // eslint-disable-line + var txLogger = new TxLogger() // eslint-disable-line var queryParams = new QueryParams() diff --git a/src/universal-dapp.js b/src/universal-dapp.js index 7c77508a65..8c4a3653df 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -27,7 +27,6 @@ function UniversalDApp (opts, localRegistry) { self.data = {} self._components = {} self._components.registry = localRegistry || globalRegistry - self.removable = opts.removable self.removable_instances = opts.removable_instances self._deps = { config: self._components.registry.get('config').api, From d069e5cef5613f8a535de09c8aa488b1a4480db0 Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Mon, 24 Dec 2018 11:37:40 -0500 Subject: [PATCH 02/50] remove unused variable (value is always true) --- src/app.js | 4 +--- src/universal-dapp-ui.js | 6 ++---- src/universal-dapp.js | 1 - 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/app.js b/src/app.js index 1031227f92..4eec5ccf0c 100644 --- a/src/app.js +++ b/src/app.js @@ -313,9 +313,7 @@ Please make a backup of your contracts and start using http://remix.ethereum.org registry.put({api: self._components.compilersArtefacts, name: 'compilersartefacts'}) // ----------------- UniversalDApp ----------------- - var udapp = new UniversalDApp({ - removable_instances: true - }) + var udapp = new UniversalDApp({}) registry.put({api: udapp, name: 'udapp'}) var udappUI = new UniversalDAppUI(udapp) diff --git a/src/universal-dapp-ui.js b/src/universal-dapp-ui.js index a900ff2cd9..92096ce2bb 100644 --- a/src/universal-dapp-ui.js +++ b/src/universal-dapp-ui.js @@ -38,10 +38,8 @@ UniversalDAppUI.prototype.renderInstanceFromABI = function (contractABI, address ${copyToClipboard(() => address)} ` - if (self.udapp.removable_instances) { - var close = yo`
` - title.appendChild(close) - } + var close = yo`
` + title.appendChild(close) function remove () { instance.remove() diff --git a/src/universal-dapp.js b/src/universal-dapp.js index 8c4a3653df..e21bfa1bbd 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -27,7 +27,6 @@ function UniversalDApp (opts, localRegistry) { self.data = {} self._components = {} self._components.registry = localRegistry || globalRegistry - self.removable_instances = opts.removable_instances self._deps = { config: self._components.registry.get('config').api, compilersartefacts: self._components.registry.get('compilersartefacts').api, From 3eaa2583d8ff6427617e01859cc023085609234b Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Mon, 24 Dec 2018 11:39:11 -0500 Subject: [PATCH 03/50] remove unused params --- src/app.js | 2 +- src/universal-dapp.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app.js b/src/app.js index 4eec5ccf0c..03cc182d51 100644 --- a/src/app.js +++ b/src/app.js @@ -313,7 +313,7 @@ Please make a backup of your contracts and start using http://remix.ethereum.org registry.put({api: self._components.compilersArtefacts, name: 'compilersartefacts'}) // ----------------- UniversalDApp ----------------- - var udapp = new UniversalDApp({}) + var udapp = new UniversalDApp() registry.put({api: udapp, name: 'udapp'}) var udappUI = new UniversalDAppUI(udapp) diff --git a/src/universal-dapp.js b/src/universal-dapp.js index e21bfa1bbd..2f2ab1653c 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -21,12 +21,12 @@ var modalDialog = require('./app/ui/modaldialog') var typeConversion = remixLib.execution.typeConversion var confirmDialog = require('./app/execution/confirmDialog') -function UniversalDApp (opts, localRegistry) { +function UniversalDApp () { this.event = new EventManager() var self = this self.data = {} self._components = {} - self._components.registry = localRegistry || globalRegistry + self._components.registry = globalRegistry self._deps = { config: self._components.registry.get('config').api, compilersartefacts: self._components.registry.get('compilersartefacts').api, From 5663cb3655ef53e90ef424a9fd0f6c91874f4313 Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Mon, 24 Dec 2018 11:54:27 -0500 Subject: [PATCH 04/50] move single function from util file into udapp --- src/app/ui/util.js | 23 ----------------------- src/universal-dapp.js | 21 +++++++++++++++++++-- 2 files changed, 19 insertions(+), 25 deletions(-) delete mode 100644 src/app/ui/util.js diff --git a/src/app/ui/util.js b/src/app/ui/util.js deleted file mode 100644 index 41f14324ec..0000000000 --- a/src/app/ui/util.js +++ /dev/null @@ -1,23 +0,0 @@ -var TreeView = require('./TreeView') -var ethJSUtil = require('ethereumjs-util') -var BN = ethJSUtil.BN -var remixLib = require('remix-lib') -var txFormat = remixLib.execution.txFormat - -module.exports = { - decodeResponseToTreeView: function (response, fnabi) { - var treeView = new TreeView({ - extractData: (item, parent, key) => { - var ret = {} - if (BN.isBN(item)) { - ret.self = item.toString(10) - ret.children = [] - } else { - ret = treeView.extractDataDefault(item, parent, key) - } - return ret - } - }) - return treeView.render(txFormat.decodeResponse(response, fnabi)) - } -} diff --git a/src/universal-dapp.js b/src/universal-dapp.js index 2f2ab1653c..fc9670fd21 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -14,13 +14,30 @@ var txFormat = remixLib.execution.txFormat var txHelper = remixLib.execution.txHelper var executionContext = require('./execution-context') var modalCustom = require('./app/ui/modal-dialog-custom') -var uiUtil = require('./app/ui/util') var globalRegistry = require('./global/registry') var modalDialog = require('./app/ui/modaldialog') var typeConversion = remixLib.execution.typeConversion var confirmDialog = require('./app/execution/confirmDialog') +var TreeView = require('./app/ui/TreeView') + +function decodeResponseToTreeView (response, fnabi) { + var treeView = new TreeView({ + extractData: (item, parent, key) => { + var ret = {} + if (BN.isBN(item)) { + ret.self = item.toString(10) + ret.children = [] + } else { + ret = treeView.extractDataDefault(item, parent, key) + } + return ret + } + }) + return treeView.render(txFormat.decodeResponse(response, fnabi)) +} + function UniversalDApp () { this.event = new EventManager() var self = this @@ -219,7 +236,7 @@ UniversalDApp.prototype.call = function (isUserAction, args, value, lookupOnly, } } if (lookupOnly) { - var decoded = uiUtil.decodeResponseToTreeView(executionContext.isVM() ? txResult.result.vm.return : ethJSUtil.toBuffer(txResult.result), args.funABI) + var decoded = decodeResponseToTreeView(executionContext.isVM() ? txResult.result.vm.return : ethJSUtil.toBuffer(txResult.result), args.funABI) outputCb(decoded) } } else { From 0cf33086bc20dea8331f3316e16c809c6ab77dfc Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Mon, 24 Dec 2018 14:07:27 -0500 Subject: [PATCH 05/50] move confirm dialog requirement out of universal-dapp logic --- src/app/tabs/run-tab.js | 154 ++++++++++++++++++++++++++++++++++----- src/recorder.js | 89 ++++++++++++++++++---- src/universal-dapp-ui.js | 72 +++++++++++++++++- src/universal-dapp.js | 77 +++----------------- 4 files changed, 288 insertions(+), 104 deletions(-) diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index 4067e69e63..5d5cda5272 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -7,6 +7,7 @@ var csjs = require('csjs-inject') var txExecution = remixLib.execution.txExecution var txFormat = remixLib.execution.txFormat var txHelper = remixLib.execution.txHelper +var typeConversion = remixLib.execution.typeConversion var EventManager = require('../../lib/events') var globlalRegistry = require('../../global/registry') var helper = require('../../lib/helper.js') @@ -24,6 +25,8 @@ var modalDialog = require('../ui/modaldialog') var CompilerAbstract = require('../compiler/compiler-abstract') var tootip = require('../ui/tooltip') +var confirmDialog = require('../execution/confirmDialog') + function runTab (opts, localRegistry) { /* ------------------------- VARIABLES @@ -394,28 +397,85 @@ function contractDropdown (events, self) { data.linkReferences = selectedContract.contract.object.evm.bytecode.linkReferences data.contractABI = selectedContract.contract.object.abi } - self._deps.udapp.createContract(data, (error, txResult) => { - if (!error) { - var isVM = executionContext.isVM() - if (isVM) { - var vmError = txExecution.checkVMError(txResult) - if (vmError.error) { - self._deps.logCallback(vmError.message) + self._deps.udapp.createContract(data, + + (network, tx, gasEstimation, continueTxExecution, cancelCb) => { + if (network.name !== 'Main') { + return continueTxExecution(null) + } + var amount = executionContext.web3().fromWei(typeConversion.toInt(tx.value), 'ether') + var content = confirmDialog(tx, amount, gasEstimation, self, + (gasPrice, cb) => { + let txFeeText, priceStatus + // TODO: this try catch feels like an anti pattern, can/should be + // removed, but for now keeping the original logic + try { + var fee = executionContext.web3().toBigNumber(tx.gas).mul(executionContext.web3().toBigNumber(executionContext.web3().toWei(gasPrice.toString(10), 'gwei'))) + txFeeText = ' ' + executionContext.web3().fromWei(fee.toString(10), 'ether') + ' Ether' + priceStatus = true + } catch (e) { + txFeeText = ' Please fix this issue before sending any transaction. ' + e.message + priceStatus = false + } + cb(txFeeText, priceStatus) + }, + (cb) => { + executionContext.web3().eth.getGasPrice((error, gasPrice) => { + var warnMessage = ' Please fix this issue before sending any transaction. ' + if (error) { + return cb('Unable to retrieve the current network gas price.' + warnMessage + error) + } + try { + var gasPriceValue = executionContext.web3().fromWei(gasPrice.toString(10), 'gwei') + cb(null, gasPriceValue) + } catch (e) { + cb(warnMessage + e.message, null, false) + } + }) + } + ) + modalDialog('Confirm transaction', content, + { label: 'Confirm', + fn: () => { + self._deps.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked) + // TODO: check if this is check is still valid given the refactor + if (!content.gasPriceStatus) { + cancelCb('Given gas price is not correct') + } else { + var gasPrice = executionContext.web3().toWei(content.querySelector('#gasprice').value, 'gwei') + continueTxExecution(gasPrice) + } + }}, { + label: 'Cancel', + fn: () => { + return cancelCb('Transaction canceled by user.') + } + }) + }, + + (error, txResult) => { + if (!error) { + var isVM = executionContext.isVM() + if (isVM) { + var vmError = txExecution.checkVMError(txResult) + if (vmError.error) { + self._deps.logCallback(vmError.message) + return + } + } + if (txResult.result.status && txResult.result.status === '0x0') { + self._deps.logCallback(`creation of ${selectedContract.name} errored: transaction execution failed`) return } + var noInstancesText = self._view.noInstancesText + if (noInstancesText.parentNode) { noInstancesText.parentNode.removeChild(noInstancesText) } + var address = isVM ? txResult.result.createdAddress : txResult.result.contractAddress + instanceContainer.appendChild(self._deps.udappUI.renderInstance(selectedContract.contract.object, address, selectContractNames.value)) + } else { + self._deps.logCallback(`creation of ${selectedContract.name} errored: ${error}`) } - if (txResult.result.status && txResult.result.status === '0x0') { - self._deps.logCallback(`creation of ${selectedContract.name} errored: transaction execution failed`) - return - } - var noInstancesText = self._view.noInstancesText - if (noInstancesText.parentNode) { noInstancesText.parentNode.removeChild(noInstancesText) } - var address = isVM ? txResult.result.createdAddress : txResult.result.contractAddress - instanceContainer.appendChild(self._deps.udappUI.renderInstance(selectedContract.contract.object, address, selectContractNames.value)) - } else { - self._deps.logCallback(`creation of ${selectedContract.name} errored: ${error}`) } - }) + ) } // DEPLOY INSTANCE @@ -439,7 +499,63 @@ function contractDropdown (events, self) { self._deps.logCallback(msg) }, (data, runTxCallback) => { // called for libraries deployment - self._deps.udapp.runTx(data, runTxCallback) + self._deps.udapp.runTx(data, + + (network, tx, gasEstimation, continueTxExecution, cancelCb) => { + if (network.name !== 'Main') { + return continueTxExecution(null) + } + var amount = executionContext.web3().fromWei(typeConversion.toInt(tx.value), 'ether') + var content = confirmDialog(tx, amount, gasEstimation, self, + (gasPrice, cb) => { + let txFeeText, priceStatus + // TODO: this try catch feels like an anti pattern, can/should be + // removed, but for now keeping the original logic + try { + var fee = executionContext.web3().toBigNumber(tx.gas).mul(executionContext.web3().toBigNumber(executionContext.web3().toWei(gasPrice.toString(10), 'gwei'))) + txFeeText = ' ' + executionContext.web3().fromWei(fee.toString(10), 'ether') + ' Ether' + priceStatus = true + } catch (e) { + txFeeText = ' Please fix this issue before sending any transaction. ' + e.message + priceStatus = false + } + cb(txFeeText, priceStatus) + }, + (cb) => { + executionContext.web3().eth.getGasPrice((error, gasPrice) => { + var warnMessage = ' Please fix this issue before sending any transaction. ' + if (error) { + return cb('Unable to retrieve the current network gas price.' + warnMessage + error) + } + try { + var gasPriceValue = executionContext.web3().fromWei(gasPrice.toString(10), 'gwei') + cb(null, gasPriceValue) + } catch (e) { + cb(warnMessage + e.message, null, false) + } + }) + } + ) + modalDialog('Confirm transaction', content, + { label: 'Confirm', + fn: () => { + self._deps.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked) + // TODO: check if this is check is still valid given the refactor + if (!content.gasPriceStatus) { + cancelCb('Given gas price is not correct') + } else { + var gasPrice = executionContext.web3().toWei(content.querySelector('#gasprice').value, 'gwei') + continueTxExecution(gasPrice) + } + }}, { + label: 'Cancel', + fn: () => { + return cancelCb('Transaction canceled by user.') + } + }) + }, + + runTxCallback) }) } else { if (Object.keys(selectedContract.contract.object.evm.bytecode.linkReferences).length) self._deps.logCallback(`linking ${JSON.stringify(selectedContract.contract.object.evm.bytecode.linkReferences, null, '\t')} using ${JSON.stringify(contractMetadata.linkReferences, null, '\t')}`) diff --git a/src/recorder.js b/src/recorder.js index 32ac7b648e..2231d92d00 100644 --- a/src/recorder.js +++ b/src/recorder.js @@ -4,9 +4,13 @@ var ethutil = require('ethereumjs-util') var executionContext = require('./execution-context') var format = remixLib.execution.txFormat var txHelper = remixLib.execution.txHelper +var typeConversion = remixLib.execution.typeConversion var async = require('async') var modal = require('./app/ui/modal-dialog-custom') +var modalDialog = require('./app/ui/modaldialog') +var confirmDialog = require('./app/execution/confirmDialog') + /** * Record transaction as long as the user create them. * @@ -238,22 +242,79 @@ class Recorder { self.logCallBack(`(${index}) data: ${data.data}`) record.data = { dataHex: data.data, funArgs: tx.record.parameters, funAbi: fnABI, contractBytecode: tx.record.bytecode, contractName: tx.record.contractName } } - udapp.runTx(record, function (err, txResult) { - if (err) { - console.error(err) - self.logCallBack(err + '. Execution failed at ' + index) - } else { - var address = executionContext.isVM() ? txResult.result.createdAddress : txResult.result.contractAddress - if (address) { - address = addressToString(address) - // save back created addresses for the convertion from tokens to real adresses - self.data._createdContracts[address] = tx.timestamp - self.data._createdContractsReverse[tx.timestamp] = address - newContractFn(abi, address, record.contractName) + udapp.runTx(record, + + (network, tx, gasEstimation, continueTxExecution, cancelCb) => { + if (network.name !== 'Main') { + return continueTxExecution(null) + } + var amount = executionContext.web3().fromWei(typeConversion.toInt(tx.value), 'ether') + var content = confirmDialog(tx, amount, gasEstimation, self, + (gasPrice, cb) => { + let txFeeText, priceStatus + // TODO: this try catch feels like an anti pattern, can/should be + // removed, but for now keeping the original logic + try { + var fee = executionContext.web3().toBigNumber(tx.gas).mul(executionContext.web3().toBigNumber(executionContext.web3().toWei(gasPrice.toString(10), 'gwei'))) + txFeeText = ' ' + executionContext.web3().fromWei(fee.toString(10), 'ether') + ' Ether' + priceStatus = true + } catch (e) { + txFeeText = ' Please fix this issue before sending any transaction. ' + e.message + priceStatus = false + } + cb(txFeeText, priceStatus) + }, + (cb) => { + executionContext.web3().eth.getGasPrice((error, gasPrice) => { + var warnMessage = ' Please fix this issue before sending any transaction. ' + if (error) { + return cb('Unable to retrieve the current network gas price.' + warnMessage + error) + } + try { + var gasPriceValue = executionContext.web3().fromWei(gasPrice.toString(10), 'gwei') + cb(null, gasPriceValue) + } catch (e) { + cb(warnMessage + e.message, null, false) + } + }) + } + ) + modalDialog('Confirm transaction', content, + { label: 'Confirm', + fn: () => { + udapp._deps.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked) + // TODO: check if this is check is still valid given the refactor + if (!content.gasPriceStatus) { + cancelCb('Given gas price is not correct') + } else { + var gasPrice = executionContext.web3().toWei(content.querySelector('#gasprice').value, 'gwei') + continueTxExecution(gasPrice) + } + }}, { + label: 'Cancel', + fn: () => { + return cancelCb('Transaction canceled by user.') + } + }) + }, + + function (err, txResult) { + if (err) { + console.error(err) + self.logCallBack(err + '. Execution failed at ' + index) + } else { + var address = executionContext.isVM() ? txResult.result.createdAddress : txResult.result.contractAddress + if (address) { + address = addressToString(address) + // save back created addresses for the convertion from tokens to real adresses + self.data._createdContracts[address] = tx.timestamp + self.data._createdContractsReverse[tx.timestamp] = address + newContractFn(abi, address, record.contractName) + } } + cb(err) } - cb(err) - }) + ) }, () => { self.setListen(true); self.clearAll() }) } } diff --git a/src/universal-dapp-ui.js b/src/universal-dapp-ui.js index 92096ce2bb..8b14445b8a 100644 --- a/src/universal-dapp-ui.js +++ b/src/universal-dapp-ui.js @@ -7,6 +7,13 @@ var helper = require('./lib/helper') var copyToClipboard = require('./app/ui/copy-to-clipboard') var css = require('./universal-dapp-styles') var MultiParamManager = require('./multiParamManager') +var remixLib = require('remix-lib') +var typeConversion = remixLib.execution.typeConversion + +var executionContext = require('./execution-context') + +var modalDialog = require('./app/ui/modaldialog') +var confirmDialog = require('./app/execution/confirmDialog') function UniversalDAppUI (udapp, opts = {}) { this.udapp = udapp @@ -90,10 +97,67 @@ UniversalDAppUI.prototype.getCallButton = function (args) { var outputOverride = yo`
` // show return value function clickButton (valArr, inputsValues) { - self.udapp.call(true, args, inputsValues, lookupOnly, (decoded) => { - outputOverride.innerHTML = '' - outputOverride.appendChild(decoded) - }) + self.udapp.call(true, args, inputsValues, lookupOnly, + + (network, tx, gasEstimation, continueTxExecution, cancelCb) => { + if (network.name !== 'Main') { + return continueTxExecution(null) + } + var amount = executionContext.web3().fromWei(typeConversion.toInt(tx.value), 'ether') + var content = confirmDialog(tx, amount, gasEstimation, self, + (gasPrice, cb) => { + let txFeeText, priceStatus + // TODO: this try catch feels like an anti pattern, can/should be + // removed, but for now keeping the original logic + try { + var fee = executionContext.web3().toBigNumber(tx.gas).mul(executionContext.web3().toBigNumber(executionContext.web3().toWei(gasPrice.toString(10), 'gwei'))) + txFeeText = ' ' + executionContext.web3().fromWei(fee.toString(10), 'ether') + ' Ether' + priceStatus = true + } catch (e) { + txFeeText = ' Please fix this issue before sending any transaction. ' + e.message + priceStatus = false + } + cb(txFeeText, priceStatus) + }, + (cb) => { + executionContext.web3().eth.getGasPrice((error, gasPrice) => { + var warnMessage = ' Please fix this issue before sending any transaction. ' + if (error) { + return cb('Unable to retrieve the current network gas price.' + warnMessage + error) + } + try { + var gasPriceValue = executionContext.web3().fromWei(gasPrice.toString(10), 'gwei') + cb(null, gasPriceValue) + } catch (e) { + cb(warnMessage + e.message, null, false) + } + }) + } + ) + modalDialog('Confirm transaction', content, + { label: 'Confirm', + fn: () => { + self._deps.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked) + // TODO: check if this is check is still valid given the refactor + if (!content.gasPriceStatus) { + cancelCb('Given gas price is not correct') + } else { + var gasPrice = executionContext.web3().toWei(content.querySelector('#gasprice').value, 'gwei') + continueTxExecution(gasPrice) + } + }}, { + label: 'Cancel', + fn: () => { + return cancelCb('Transaction canceled by user.') + } + }) + }, + + (decoded) => { + outputOverride.innerHTML = '' + outputOverride.appendChild(decoded) + } + ) } var multiParamManager = new MultiParamManager(lookupOnly, args.funABI, (valArray, inputsValues, domEl) => { diff --git a/src/universal-dapp.js b/src/universal-dapp.js index fc9670fd21..21a915dca7 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -13,13 +13,10 @@ var txExecution = remixLib.execution.txExecution var txFormat = remixLib.execution.txFormat var txHelper = remixLib.execution.txHelper var executionContext = require('./execution-context') -var modalCustom = require('./app/ui/modal-dialog-custom') var globalRegistry = require('./global/registry') +var modalCustom = require('./app/ui/modal-dialog-custom') var modalDialog = require('./app/ui/modaldialog') -var typeConversion = remixLib.execution.typeConversion -var confirmDialog = require('./app/execution/confirmDialog') - var TreeView = require('./app/ui/TreeView') function decodeResponseToTreeView (response, fnabi) { @@ -205,7 +202,7 @@ UniversalDApp.prototype.pendingTransactionsCount = function () { return Object.keys(this.txRunner.pendingTxs).length } -UniversalDApp.prototype.call = function (isUserAction, args, value, lookupOnly, outputCb) { +UniversalDApp.prototype.call = function (isUserAction, args, value, lookupOnly, confirmationCb, outputCb) { const self = this var logMsg if (isUserAction) { @@ -225,7 +222,7 @@ UniversalDApp.prototype.call = function (isUserAction, args, value, lookupOnly, } } if (args.funABI.type === 'fallback') data.dataHex = value - self.callFunction(args.address, data, args.funABI, (error, txResult) => { + self.callFunction(args.address, data, args.funABI, confirmationCb, (error, txResult) => { if (!error) { var isVM = executionContext.isVM() if (isVM) { @@ -250,7 +247,7 @@ UniversalDApp.prototype.call = function (isUserAction, args, value, lookupOnly, self._deps.logCallback(msg) }, (data, runTxCallback) => { // called for libraries deployment - self.runTx(data, runTxCallback) + self.runTx(data, confirmationCb, runTxCallback) }) } @@ -260,8 +257,8 @@ UniversalDApp.prototype.call = function (isUserAction, args, value, lookupOnly, * @param {String} data - data to send with the transaction ( return of txFormat.buildData(...) ). * @param {Function} callback - callback. */ -UniversalDApp.prototype.createContract = function (data, callback) { - this.runTx({data: data, useCall: false}, (error, txResult) => { +UniversalDApp.prototype.createContract = function (data, confirmationCb, callback) { + this.runTx({data: data, useCall: false}, confirmationCb, (error, txResult) => { // see universaldapp.js line 660 => 700 to check possible values of txResult (error case) callback(error, txResult) }) @@ -275,8 +272,8 @@ UniversalDApp.prototype.createContract = function (data, callback) { * @param {Object} funAbi - abi definition of the function to call. * @param {Function} callback - callback. */ -UniversalDApp.prototype.callFunction = function (to, data, funAbi, callback) { - this.runTx({to: to, data: data, useCall: funAbi.constant}, (error, txResult) => { +UniversalDApp.prototype.callFunction = function (to, data, funAbi, confirmationCb, callback) { + this.runTx({to: to, data: data, useCall: funAbi.constant}, confirmationCb, (error, txResult) => { // see universaldapp.js line 660 => 700 to check possible values of txResult (error case) callback(error, txResult) }) @@ -318,7 +315,7 @@ UniversalDApp.prototype.silentRunTx = function (tx, cb) { cb) } -UniversalDApp.prototype.runTx = function (args, cb) { +UniversalDApp.prototype.runTx = function (args, confirmationCb, cb) { const self = this async.waterfall([ function getGasLimit (next) { @@ -364,61 +361,7 @@ UniversalDApp.prototype.runTx = function (args, cb) { var timestamp = Date.now() self.event.trigger('initiatingTransaction', [timestamp, tx, payLoad]) - self.txRunner.rawRun(tx, - - (network, tx, gasEstimation, continueTxExecution, cancelCb) => { - if (network.name !== 'Main') { - return continueTxExecution(null) - } - var amount = executionContext.web3().fromWei(typeConversion.toInt(tx.value), 'ether') - var content = confirmDialog(tx, amount, gasEstimation, self, - (gasPrice, cb) => { - let txFeeText, priceStatus - // TODO: this try catch feels like an anti pattern, can/should be - // removed, but for now keeping the original logic - try { - var fee = executionContext.web3().toBigNumber(tx.gas).mul(executionContext.web3().toBigNumber(executionContext.web3().toWei(gasPrice.toString(10), 'gwei'))) - txFeeText = ' ' + executionContext.web3().fromWei(fee.toString(10), 'ether') + ' Ether' - priceStatus = true - } catch (e) { - txFeeText = ' Please fix this issue before sending any transaction. ' + e.message - priceStatus = false - } - cb(txFeeText, priceStatus) - }, - (cb) => { - executionContext.web3().eth.getGasPrice((error, gasPrice) => { - var warnMessage = ' Please fix this issue before sending any transaction. ' - if (error) { - return cb('Unable to retrieve the current network gas price.' + warnMessage + error) - } - try { - var gasPriceValue = executionContext.web3().fromWei(gasPrice.toString(10), 'gwei') - cb(null, gasPriceValue) - } catch (e) { - cb(warnMessage + e.message, null, false) - } - }) - } - ) - modalDialog('Confirm transaction', content, - { label: 'Confirm', - fn: () => { - self._deps.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked) - // TODO: check if this is check is still valid given the refactor - if (!content.gasPriceStatus) { - cancelCb('Given gas price is not correct') - } else { - var gasPrice = executionContext.web3().toWei(content.querySelector('#gasprice').value, 'gwei') - continueTxExecution(gasPrice) - } - }}, { - label: 'Cancel', - fn: () => { - return cancelCb('Transaction canceled by user.') - } - }) - }, + self.txRunner.rawRun(tx, confirmationCb, (error, continueTxExecution, cancelCb) => { if (error) { var msg = typeof error !== 'string' ? error.message : error From 6de3a31d7ce64d32ae3c48fbc1352373fecc53d3 Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Mon, 24 Dec 2018 14:40:25 -0500 Subject: [PATCH 06/50] move treeview ui out of the udapp logic --- src/universal-dapp-ui.js | 172 +++++++++++++++++++++++++++------------ src/universal-dapp.js | 68 ---------------- 2 files changed, 118 insertions(+), 122 deletions(-) diff --git a/src/universal-dapp-ui.js b/src/universal-dapp-ui.js index 8b14445b8a..54179197b2 100644 --- a/src/universal-dapp-ui.js +++ b/src/universal-dapp-ui.js @@ -3,22 +3,43 @@ var $ = require('jquery') var yo = require('yo-yo') +var ethJSUtil = require('ethereumjs-util') +var BN = ethJSUtil.BN var helper = require('./lib/helper') var copyToClipboard = require('./app/ui/copy-to-clipboard') var css = require('./universal-dapp-styles') var MultiParamManager = require('./multiParamManager') var remixLib = require('remix-lib') var typeConversion = remixLib.execution.typeConversion +var txExecution = remixLib.execution.txExecution +var txFormat = remixLib.execution.txFormat var executionContext = require('./execution-context') var modalDialog = require('./app/ui/modaldialog') var confirmDialog = require('./app/execution/confirmDialog') +var TreeView = require('./app/ui/TreeView') function UniversalDAppUI (udapp, opts = {}) { this.udapp = udapp } +function decodeResponseToTreeView (response, fnabi) { + var treeView = new TreeView({ + extractData: (item, parent, key) => { + var ret = {} + if (BN.isBN(item)) { + ret.self = item.toString(10) + ret.children = [] + } else { + ret = treeView.extractDataDefault(item, parent, key) + } + return ret + } + }) + return treeView.render(txFormat.decodeResponse(response, fnabi)) +} + UniversalDAppUI.prototype.renderInstance = function (contract, address, contractName) { var noInstances = document.querySelector('[class^="noInstancesText"]') if (noInstances) { @@ -97,67 +118,110 @@ UniversalDAppUI.prototype.getCallButton = function (args) { var outputOverride = yo`
` // show return value function clickButton (valArr, inputsValues) { - self.udapp.call(true, args, inputsValues, lookupOnly, + var logMsg + if (!args.funABI.constant) { + logMsg = `transact to ${args.contractName}.${(args.funABI.name) ? args.funABI.name : '(fallback)'}` + } else { + logMsg = `call to ${args.contractName}.${(args.funABI.name) ? args.funABI.name : '(fallback)'}` + } - (network, tx, gasEstimation, continueTxExecution, cancelCb) => { - if (network.name !== 'Main') { - return continueTxExecution(null) - } - var amount = executionContext.web3().fromWei(typeConversion.toInt(tx.value), 'ether') - var content = confirmDialog(tx, amount, gasEstimation, self, - (gasPrice, cb) => { - let txFeeText, priceStatus - // TODO: this try catch feels like an anti pattern, can/should be - // removed, but for now keeping the original logic + var value = inputsValues + + var confirmationCb = (network, tx, gasEstimation, continueTxExecution, cancelCb) => { + if (network.name !== 'Main') { + return continueTxExecution(null) + } + var amount = executionContext.web3().fromWei(typeConversion.toInt(tx.value), 'ether') + var content = confirmDialog(tx, amount, gasEstimation, self.udapp, + (gasPrice, cb) => { + let txFeeText, priceStatus + // TODO: this try catch feels like an anti pattern, can/should be + // removed, but for now keeping the original logic + try { + var fee = executionContext.web3().toBigNumber(tx.gas).mul(executionContext.web3().toBigNumber(executionContext.web3().toWei(gasPrice.toString(10), 'gwei'))) + txFeeText = ' ' + executionContext.web3().fromWei(fee.toString(10), 'ether') + ' Ether' + priceStatus = true + } catch (e) { + txFeeText = ' Please fix this issue before sending any transaction. ' + e.message + priceStatus = false + } + cb(txFeeText, priceStatus) + }, + (cb) => { + executionContext.web3().eth.getGasPrice((error, gasPrice) => { + var warnMessage = ' Please fix this issue before sending any transaction. ' + if (error) { + return cb('Unable to retrieve the current network gas price.' + warnMessage + error) + } try { - var fee = executionContext.web3().toBigNumber(tx.gas).mul(executionContext.web3().toBigNumber(executionContext.web3().toWei(gasPrice.toString(10), 'gwei'))) - txFeeText = ' ' + executionContext.web3().fromWei(fee.toString(10), 'ether') + ' Ether' - priceStatus = true + var gasPriceValue = executionContext.web3().fromWei(gasPrice.toString(10), 'gwei') + cb(null, gasPriceValue) } catch (e) { - txFeeText = ' Please fix this issue before sending any transaction. ' + e.message - priceStatus = false + cb(warnMessage + e.message, null, false) } - cb(txFeeText, priceStatus) - }, - (cb) => { - executionContext.web3().eth.getGasPrice((error, gasPrice) => { - var warnMessage = ' Please fix this issue before sending any transaction. ' - if (error) { - return cb('Unable to retrieve the current network gas price.' + warnMessage + error) - } - try { - var gasPriceValue = executionContext.web3().fromWei(gasPrice.toString(10), 'gwei') - cb(null, gasPriceValue) - } catch (e) { - cb(warnMessage + e.message, null, false) - } - }) - } - ) - modalDialog('Confirm transaction', content, - { label: 'Confirm', + }) + } + ) + modalDialog('Confirm transaction', content, + { label: 'Confirm', + fn: () => { + self.udapp._deps.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked) + // TODO: check if this is check is still valid given the refactor + if (!content.gasPriceStatus) { + cancelCb('Given gas price is not correct') + } else { + var gasPrice = executionContext.web3().toWei(content.querySelector('#gasprice').value, 'gwei') + continueTxExecution(gasPrice) + } + }}, { + label: 'Cancel', fn: () => { - self._deps.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked) - // TODO: check if this is check is still valid given the refactor - if (!content.gasPriceStatus) { - cancelCb('Given gas price is not correct') - } else { - var gasPrice = executionContext.web3().toWei(content.querySelector('#gasprice').value, 'gwei') - continueTxExecution(gasPrice) - } - }}, { - label: 'Cancel', - fn: () => { - return cancelCb('Transaction canceled by user.') - } - }) - }, + return cancelCb('Transaction canceled by user.') + } + }) + } - (decoded) => { - outputOverride.innerHTML = '' - outputOverride.appendChild(decoded) + var outputCb = (decoded) => { + outputOverride.innerHTML = '' + outputOverride.appendChild(decoded) + } + + // contractsDetails is used to resolve libraries + txFormat.buildData(args.contractName, args.contractAbi, self.udapp.data.contractsDetails, false, args.funABI, args.funABI.type !== 'fallback' ? value : '', (error, data) => { + if (!error) { + if (!args.funABI.constant) { + self.udapp._deps.logCallback(`${logMsg} pending ... `) + } else { + self.udapp._deps.logCallback(`${logMsg}`) + } + if (args.funABI.type === 'fallback') data.dataHex = value + self.udapp.callFunction(args.address, data, args.funABI, confirmationCb, (error, txResult) => { + if (!error) { + var isVM = executionContext.isVM() + if (isVM) { + var vmError = txExecution.checkVMError(txResult) + if (vmError.error) { + self.udapp._deps.logCallback(`${logMsg} errored: ${vmError.message} `) + return + } + } + if (lookupOnly) { + var decoded = decodeResponseToTreeView(executionContext.isVM() ? txResult.result.vm.return : ethJSUtil.toBuffer(txResult.result), args.funABI) + outputCb(decoded) + } + } else { + self.udapp._deps.logCallback(`${logMsg} errored: ${error} `) + } + }) + } else { + self.udapp._deps.logCallback(`${logMsg} errored: ${error} `) } - ) + }, (msg) => { + self.udapp._deps.logCallback(msg) + }, (data, runTxCallback) => { + // called for libraries deployment + self.udapp.runTx(data, confirmationCb, runTxCallback) + }) } var multiParamManager = new MultiParamManager(lookupOnly, args.funABI, (valArray, inputsValues, domEl) => { diff --git a/src/universal-dapp.js b/src/universal-dapp.js index 21a915dca7..e01624c6e3 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -9,31 +9,12 @@ var remixLib = require('remix-lib') var EventManager = require('./lib/events') var crypto = require('crypto') var TxRunner = remixLib.execution.txRunner -var txExecution = remixLib.execution.txExecution -var txFormat = remixLib.execution.txFormat var txHelper = remixLib.execution.txHelper var executionContext = require('./execution-context') var globalRegistry = require('./global/registry') var modalCustom = require('./app/ui/modal-dialog-custom') var modalDialog = require('./app/ui/modaldialog') -var TreeView = require('./app/ui/TreeView') - -function decodeResponseToTreeView (response, fnabi) { - var treeView = new TreeView({ - extractData: (item, parent, key) => { - var ret = {} - if (BN.isBN(item)) { - ret.self = item.toString(10) - ret.children = [] - } else { - ret = treeView.extractDataDefault(item, parent, key) - } - return ret - } - }) - return treeView.render(txFormat.decodeResponse(response, fnabi)) -} function UniversalDApp () { this.event = new EventManager() @@ -202,55 +183,6 @@ UniversalDApp.prototype.pendingTransactionsCount = function () { return Object.keys(this.txRunner.pendingTxs).length } -UniversalDApp.prototype.call = function (isUserAction, args, value, lookupOnly, confirmationCb, outputCb) { - const self = this - var logMsg - if (isUserAction) { - if (!args.funABI.constant) { - logMsg = `transact to ${args.contractName}.${(args.funABI.name) ? args.funABI.name : '(fallback)'}` - } else { - logMsg = `call to ${args.contractName}.${(args.funABI.name) ? args.funABI.name : '(fallback)'}` - } - } - txFormat.buildData(args.contractName, args.contractAbi, self._deps.compilersartefacts['__last'].getData().contracts, false, args.funABI, args.funABI.type !== 'fallback' ? value : '', (error, data) => { - if (!error) { - if (isUserAction) { - if (!args.funABI.constant) { - self._deps.logCallback(`${logMsg} pending ... `) - } else { - self._deps.logCallback(`${logMsg}`) - } - } - if (args.funABI.type === 'fallback') data.dataHex = value - self.callFunction(args.address, data, args.funABI, confirmationCb, (error, txResult) => { - if (!error) { - var isVM = executionContext.isVM() - if (isVM) { - var vmError = txExecution.checkVMError(txResult) - if (vmError.error) { - self._deps.logCallback(`${logMsg} errored: ${vmError.message} `) - return - } - } - if (lookupOnly) { - var decoded = decodeResponseToTreeView(executionContext.isVM() ? txResult.result.vm.return : ethJSUtil.toBuffer(txResult.result), args.funABI) - outputCb(decoded) - } - } else { - self._deps.logCallback(`${logMsg} errored: ${error} `) - } - }) - } else { - self._deps.logCallback(`${logMsg} errored: ${error} `) - } - }, (msg) => { - self._deps.logCallback(msg) - }, (data, runTxCallback) => { - // called for libraries deployment - self.runTx(data, confirmationCb, runTxCallback) - }) -} - /** * deploy the given contract * From d4042d3d934d4666ae711d2ab7848c0c17a121bc Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Mon, 24 Dec 2018 15:03:12 -0500 Subject: [PATCH 07/50] move dialog ui out of udapp logic --- src/app/tabs/run-tab.js | 22 +++++++++++++++++++++- src/recorder.js | 23 ++++++++++++++++++++++- src/universal-dapp-ui.js | 24 +++++++++++++++++++++++- src/universal-dapp.js | 34 ++++++---------------------------- 4 files changed, 72 insertions(+), 31 deletions(-) diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index 5d5cda5272..734378ca10 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -452,7 +452,27 @@ function contractDropdown (events, self) { } }) }, - + (error, continueTxExecution, cancelCb) => { + if (error) { + var msg = typeof error !== 'string' ? error.message : error + modalDialog('Gas estimation failed', yo`
Gas estimation errored with the following message (see below). + The transaction execution will likely fail. Do you want to force sending?
+ ${msg} +
`, + { + label: 'Send Transaction', + fn: () => { + continueTxExecution() + }}, { + label: 'Cancel Transaction', + fn: () => { + cancelCb() + } + }) + } else { + continueTxExecution() + } + }, (error, txResult) => { if (!error) { var isVM = executionContext.isVM() diff --git a/src/recorder.js b/src/recorder.js index 2231d92d00..28b5ca2ad7 100644 --- a/src/recorder.js +++ b/src/recorder.js @@ -1,3 +1,4 @@ +var yo = require('yo-yo') var remixLib = require('remix-lib') var EventManager = require('./lib/events') var ethutil = require('ethereumjs-util') @@ -297,7 +298,27 @@ class Recorder { } }) }, - + (error, continueTxExecution, cancelCb) => { + if (error) { + var msg = typeof error !== 'string' ? error.message : error + modalDialog('Gas estimation failed', yo`
Gas estimation errored with the following message (see below). + The transaction execution will likely fail. Do you want to force sending?
+ ${msg} +
`, + { + label: 'Send Transaction', + fn: () => { + continueTxExecution() + }}, { + label: 'Cancel Transaction', + fn: () => { + cancelCb() + } + }) + } else { + continueTxExecution() + } + }, function (err, txResult) { if (err) { console.error(err) diff --git a/src/universal-dapp-ui.js b/src/universal-dapp-ui.js index 54179197b2..711b1b32bb 100644 --- a/src/universal-dapp-ui.js +++ b/src/universal-dapp-ui.js @@ -181,6 +181,28 @@ UniversalDAppUI.prototype.getCallButton = function (args) { }) } + var continueCb = (error, continueTxExecution, cancelCb) => { + if (error) { + var msg = typeof error !== 'string' ? error.message : error + modalDialog('Gas estimation failed', yo`
Gas estimation errored with the following message (see below). + The transaction execution will likely fail. Do you want to force sending?
+ ${msg} +
`, + { + label: 'Send Transaction', + fn: () => { + continueTxExecution() + }}, { + label: 'Cancel Transaction', + fn: () => { + cancelCb() + } + }) + } else { + continueTxExecution() + } + } + var outputCb = (decoded) => { outputOverride.innerHTML = '' outputOverride.appendChild(decoded) @@ -195,7 +217,7 @@ UniversalDAppUI.prototype.getCallButton = function (args) { self.udapp._deps.logCallback(`${logMsg}`) } if (args.funABI.type === 'fallback') data.dataHex = value - self.udapp.callFunction(args.address, data, args.funABI, confirmationCb, (error, txResult) => { + self.udapp.callFunction(args.address, data, args.funABI, confirmationCb, continueCb, (error, txResult) => { if (!error) { var isVM = executionContext.isVM() if (isVM) { diff --git a/src/universal-dapp.js b/src/universal-dapp.js index e01624c6e3..bc6053482c 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -14,7 +14,6 @@ var executionContext = require('./execution-context') var globalRegistry = require('./global/registry') var modalCustom = require('./app/ui/modal-dialog-custom') -var modalDialog = require('./app/ui/modaldialog') function UniversalDApp () { this.event = new EventManager() @@ -189,8 +188,8 @@ UniversalDApp.prototype.pendingTransactionsCount = function () { * @param {String} data - data to send with the transaction ( return of txFormat.buildData(...) ). * @param {Function} callback - callback. */ -UniversalDApp.prototype.createContract = function (data, confirmationCb, callback) { - this.runTx({data: data, useCall: false}, confirmationCb, (error, txResult) => { +UniversalDApp.prototype.createContract = function (data, confirmationCb, continueCb, callback) { + this.runTx({data: data, useCall: false}, confirmationCb, continueCb, (error, txResult) => { // see universaldapp.js line 660 => 700 to check possible values of txResult (error case) callback(error, txResult) }) @@ -204,8 +203,8 @@ UniversalDApp.prototype.createContract = function (data, confirmationCb, callbac * @param {Object} funAbi - abi definition of the function to call. * @param {Function} callback - callback. */ -UniversalDApp.prototype.callFunction = function (to, data, funAbi, confirmationCb, callback) { - this.runTx({to: to, data: data, useCall: funAbi.constant}, confirmationCb, (error, txResult) => { +UniversalDApp.prototype.callFunction = function (to, data, funAbi, confirmationCb, continueCb, callback) { + this.runTx({to: to, data: data, useCall: funAbi.constant}, confirmationCb, continueCb, (error, txResult) => { // see universaldapp.js line 660 => 700 to check possible values of txResult (error case) callback(error, txResult) }) @@ -247,7 +246,7 @@ UniversalDApp.prototype.silentRunTx = function (tx, cb) { cb) } -UniversalDApp.prototype.runTx = function (args, confirmationCb, cb) { +UniversalDApp.prototype.runTx = function (args, confirmationCb, continueCb, cb) { const self = this async.waterfall([ function getGasLimit (next) { @@ -293,28 +292,7 @@ UniversalDApp.prototype.runTx = function (args, confirmationCb, cb) { var timestamp = Date.now() self.event.trigger('initiatingTransaction', [timestamp, tx, payLoad]) - self.txRunner.rawRun(tx, confirmationCb, - (error, continueTxExecution, cancelCb) => { - if (error) { - var msg = typeof error !== 'string' ? error.message : error - modalDialog('Gas estimation failed', yo`
Gas estimation errored with the following message (see below). - The transaction execution will likely fail. Do you want to force sending?
- ${msg} -
`, - { - label: 'Send Transaction', - fn: () => { - continueTxExecution() - }}, { - label: 'Cancel Transaction', - fn: () => { - cancelCb() - } - }) - } else { - continueTxExecution() - } - }, + self.txRunner.rawRun(tx, confirmationCb, continueCb, function (okCb, cancelCb) { modalCustom.promptPassphrase(null, 'Personal mode is enabled. Please provide passphrase of account ' + tx.from, '', okCb, cancelCb) }, From 17fcf930944eed80b21c648df76bab3dcf3bde27 Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Mon, 24 Dec 2018 15:11:15 -0500 Subject: [PATCH 08/50] move passsword modal out of udapp logic --- src/app/tabs/run-tab.js | 23 +++++++++++++++++------ src/universal-dapp.js | 12 ++++-------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index 734378ca10..d865c785d8 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -26,6 +26,7 @@ var CompilerAbstract = require('../compiler/compiler-abstract') var tootip = require('../ui/tooltip') var confirmDialog = require('../execution/confirmDialog') +var modalCustom = require('../ui/modal-dialog-custom') function runTab (opts, localRegistry) { /* ------------------------- @@ -814,13 +815,23 @@ function settings (container, self) { }, 10000) function newAccount () { - self._deps.udapp.newAccount('', (error, address) => { - if (!error) { - addTooltip(`account ${address} created`) - } else { - addTooltip('Cannot create an account: ' + error) + self._deps.udapp.newAccount('', + (cb) => { + modalCustom.promptPassphraseCreation((error, passphrase) => { + if (error) { + return modalCustom.alert(error) + } + cb(passphrase) + }, () => {}) + }, + (error, address) => { + if (!error) { + addTooltip(`account ${address} created`) + } else { + addTooltip('Cannot create an account: ' + error) + } } - }) + ) } function signMessage (event) { self._deps.udapp.getAccounts((err, accounts) => { diff --git a/src/universal-dapp.js b/src/universal-dapp.js index bc6053482c..e917525514 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -75,18 +75,14 @@ UniversalDApp.prototype.createVMAccount = function (privateKey, balance, cb) { cb(null, '0x' + ethJSUtil.privateToAddress(privateKey).toString('hex')) } -UniversalDApp.prototype.newAccount = function (password, cb) { +UniversalDApp.prototype.newAccount = function (password, passwordPromptCb, cb) { if (!executionContext.isVM()) { if (!this._deps.config.get('settings/personal-mode')) { return cb('Not running in personal mode') } - modalCustom.promptPassphraseCreation((error, passphrase) => { - if (error) { - modalCustom.alert(error) - } else { - executionContext.web3().personal.newAccount(passphrase, cb) - } - }, () => {}) + passwordPromptCb((passphrase) => { + executionContext.web3().personal.newAccount(passphrase, cb) + }) } else { var privateKey do { From 8a4dc66dde710ed82615e7246a4e55b47507bc69 Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Wed, 26 Dec 2018 10:25:29 -0500 Subject: [PATCH 09/50] move second passsword modal out of udapp logic --- src/app/tabs/run-tab.js | 8 ++++++-- src/recorder.js | 6 +++++- src/universal-dapp-ui.js | 9 +++++++-- src/universal-dapp.js | 17 ++++++----------- 4 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index d865c785d8..ae6129d33d 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -474,6 +474,9 @@ function contractDropdown (events, self) { continueTxExecution() } }, + function (okCb, cancelCb) { + modalCustom.promptPassphrase(null, 'Personal mode is enabled. Please provide passphrase of account', '', okCb, cancelCb) + }, (error, txResult) => { if (!error) { var isVM = executionContext.isVM() @@ -521,7 +524,6 @@ function contractDropdown (events, self) { }, (data, runTxCallback) => { // called for libraries deployment self._deps.udapp.runTx(data, - (network, tx, gasEstimation, continueTxExecution, cancelCb) => { if (network.name !== 'Main') { return continueTxExecution(null) @@ -575,7 +577,9 @@ function contractDropdown (events, self) { } }) }, - + function (okCb, cancelCb) { + modalCustom.promptPassphrase(null, 'Personal mode is enabled. Please provide passphrase of account', '', okCb, cancelCb) + }, runTxCallback) }) } else { diff --git a/src/recorder.js b/src/recorder.js index 28b5ca2ad7..a12bc9fa22 100644 --- a/src/recorder.js +++ b/src/recorder.js @@ -9,8 +9,9 @@ var typeConversion = remixLib.execution.typeConversion var async = require('async') var modal = require('./app/ui/modal-dialog-custom') -var modalDialog = require('./app/ui/modaldialog') var confirmDialog = require('./app/execution/confirmDialog') +var modalCustom = require('./app/ui/modal-dialog-custom') +var modalDialog = require('./app/ui/modaldialog') /** * Record transaction as long as the user create them. @@ -319,6 +320,9 @@ class Recorder { continueTxExecution() } }, + function (okCb, cancelCb) { + modalCustom.promptPassphrase(null, 'Personal mode is enabled. Please provide passphrase of account', '', okCb, cancelCb) + }, function (err, txResult) { if (err) { console.error(err) diff --git a/src/universal-dapp-ui.js b/src/universal-dapp-ui.js index 711b1b32bb..a6162057a0 100644 --- a/src/universal-dapp-ui.js +++ b/src/universal-dapp-ui.js @@ -16,8 +16,9 @@ var txFormat = remixLib.execution.txFormat var executionContext = require('./execution-context') -var modalDialog = require('./app/ui/modaldialog') var confirmDialog = require('./app/execution/confirmDialog') +var modalCustom = require('./app/ui/modal-dialog-custom') +var modalDialog = require('./app/ui/modaldialog') var TreeView = require('./app/ui/TreeView') function UniversalDAppUI (udapp, opts = {}) { @@ -208,6 +209,10 @@ UniversalDAppUI.prototype.getCallButton = function (args) { outputOverride.appendChild(decoded) } + var promptCb = (okCb, cancelCb) => { + modalCustom.promptPassphrase(null, 'Personal mode is enabled. Please provide passphrase of account', '', okCb, cancelCb) + } + // contractsDetails is used to resolve libraries txFormat.buildData(args.contractName, args.contractAbi, self.udapp.data.contractsDetails, false, args.funABI, args.funABI.type !== 'fallback' ? value : '', (error, data) => { if (!error) { @@ -217,7 +222,7 @@ UniversalDAppUI.prototype.getCallButton = function (args) { self.udapp._deps.logCallback(`${logMsg}`) } if (args.funABI.type === 'fallback') data.dataHex = value - self.udapp.callFunction(args.address, data, args.funABI, confirmationCb, continueCb, (error, txResult) => { + self.udapp.callFunction(args.address, data, args.funABI, confirmationCb, continueCb, promptCb, (error, txResult) => { if (!error) { var isVM = executionContext.isVM() if (isVM) { diff --git a/src/universal-dapp.js b/src/universal-dapp.js index e917525514..4e1bdd38c2 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -13,8 +13,6 @@ var txHelper = remixLib.execution.txHelper var executionContext = require('./execution-context') var globalRegistry = require('./global/registry') -var modalCustom = require('./app/ui/modal-dialog-custom') - function UniversalDApp () { this.event = new EventManager() var self = this @@ -184,8 +182,8 @@ UniversalDApp.prototype.pendingTransactionsCount = function () { * @param {String} data - data to send with the transaction ( return of txFormat.buildData(...) ). * @param {Function} callback - callback. */ -UniversalDApp.prototype.createContract = function (data, confirmationCb, continueCb, callback) { - this.runTx({data: data, useCall: false}, confirmationCb, continueCb, (error, txResult) => { +UniversalDApp.prototype.createContract = function (data, confirmationCb, continueCb, promptCb, callback) { + this.runTx({data: data, useCall: false}, confirmationCb, continueCb, promptCb, (error, txResult) => { // see universaldapp.js line 660 => 700 to check possible values of txResult (error case) callback(error, txResult) }) @@ -199,8 +197,8 @@ UniversalDApp.prototype.createContract = function (data, confirmationCb, continu * @param {Object} funAbi - abi definition of the function to call. * @param {Function} callback - callback. */ -UniversalDApp.prototype.callFunction = function (to, data, funAbi, confirmationCb, continueCb, callback) { - this.runTx({to: to, data: data, useCall: funAbi.constant}, confirmationCb, continueCb, (error, txResult) => { +UniversalDApp.prototype.callFunction = function (to, data, funAbi, confirmationCb, continueCb, promptCb, callback) { + this.runTx({to: to, data: data, useCall: funAbi.constant}, confirmationCb, continueCb, promptCb, (error, txResult) => { // see universaldapp.js line 660 => 700 to check possible values of txResult (error case) callback(error, txResult) }) @@ -242,7 +240,7 @@ UniversalDApp.prototype.silentRunTx = function (tx, cb) { cb) } -UniversalDApp.prototype.runTx = function (args, confirmationCb, continueCb, cb) { +UniversalDApp.prototype.runTx = function (args, confirmationCb, continueCb, promptCb, cb) { const self = this async.waterfall([ function getGasLimit (next) { @@ -288,10 +286,7 @@ UniversalDApp.prototype.runTx = function (args, confirmationCb, continueCb, cb) var timestamp = Date.now() self.event.trigger('initiatingTransaction', [timestamp, tx, payLoad]) - self.txRunner.rawRun(tx, confirmationCb, continueCb, - function (okCb, cancelCb) { - modalCustom.promptPassphrase(null, 'Personal mode is enabled. Please provide passphrase of account ' + tx.from, '', okCb, cancelCb) - }, + self.txRunner.rawRun(tx, confirmationCb, continueCb, promptCb, function (error, result) { let eventName = (tx.useCall ? 'callExecuted' : 'transactionExecuted') self.event.trigger(eventName, [error, tx.from, tx.to, tx.data, tx.useCall, result, timestamp, payLoad]) From e615553b0cd8cafe607f63e6f83de0367a752238 Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Wed, 26 Dec 2018 10:36:57 -0500 Subject: [PATCH 10/50] call directly remix-lib execution-context and event-manager --- src/universal-dapp.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/universal-dapp.js b/src/universal-dapp.js index 4e1bdd38c2..f757cf6cee 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -6,11 +6,12 @@ var async = require('async') var ethJSUtil = require('ethereumjs-util') var BN = ethJSUtil.BN var remixLib = require('remix-lib') -var EventManager = require('./lib/events') var crypto = require('crypto') var TxRunner = remixLib.execution.txRunner var txHelper = remixLib.execution.txHelper -var executionContext = require('./execution-context') +var EventManager = remixLib.EventManager +var executionContext = remixLib.execution.executionContext + var globalRegistry = require('./global/registry') function UniversalDApp () { From 1d815f2019be63c3db99f98c0f6655ab0e4d7518 Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Wed, 26 Dec 2018 11:04:50 -0500 Subject: [PATCH 11/50] pass global registry to udapp as an argument --- src/app.js | 2 +- src/universal-dapp.js | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/app.js b/src/app.js index 03cc182d51..6ab4d8f2e4 100644 --- a/src/app.js +++ b/src/app.js @@ -313,7 +313,7 @@ Please make a backup of your contracts and start using http://remix.ethereum.org registry.put({api: self._components.compilersArtefacts, name: 'compilersartefacts'}) // ----------------- UniversalDApp ----------------- - var udapp = new UniversalDApp() + var udapp = new UniversalDApp(registry) registry.put({api: udapp, name: 'udapp'}) var udappUI = new UniversalDAppUI(udapp) diff --git a/src/universal-dapp.js b/src/universal-dapp.js index f757cf6cee..7faeec2a5f 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -12,9 +12,7 @@ var txHelper = remixLib.execution.txHelper var EventManager = remixLib.EventManager var executionContext = remixLib.execution.executionContext -var globalRegistry = require('./global/registry') - -function UniversalDApp () { +function UniversalDApp (globalRegistry) { this.event = new EventManager() var self = this self.data = {} From 18cd5a88539bd9c6c08e8345fdcee38a26d85b23 Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Wed, 26 Dec 2018 11:17:12 -0500 Subject: [PATCH 12/50] remove unneded _components var --- src/universal-dapp.js | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/universal-dapp.js b/src/universal-dapp.js index 7faeec2a5f..88bc0c77c5 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -14,14 +14,11 @@ var executionContext = remixLib.execution.executionContext function UniversalDApp (globalRegistry) { this.event = new EventManager() - var self = this - self.data = {} - self._components = {} - self._components.registry = globalRegistry - self._deps = { - config: self._components.registry.get('config').api, - compilersartefacts: self._components.registry.get('compilersartefacts').api, - logCallback: self._components.registry.get('logCallback').api + this.data = {} + this._deps = { + config: globalRegistry.get('config').api, + compiler: globalRegistry.get('compiler').api, + logCallback: globalRegistry.get('logCallback').api } executionContext.event.register('contextChanged', this, function (context) { self.resetEnvironment() From be75e37a0e75cd6567270adfb26a8c77135667b1 Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Wed, 26 Dec 2018 11:26:20 -0500 Subject: [PATCH 13/50] move yo-yo dependency out of udapp; remove need for logCallback inside udapp --- src/app.js | 4 ++++ src/universal-dapp.js | 13 +++---------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/app.js b/src/app.js index 6ab4d8f2e4..d14003c9a5 100644 --- a/src/app.js +++ b/src/app.js @@ -315,6 +315,10 @@ Please make a backup of your contracts and start using http://remix.ethereum.org // ----------------- UniversalDApp ----------------- var udapp = new UniversalDApp(registry) registry.put({api: udapp, name: 'udapp'}) + udapp.event.register('transactionBroadcasted', (txhash, networkName) => { + var txLink = executionContext.txDetailsLink(networkName, txhash) + if (txLink) registry.get('logCallback').api.logCallback(yo`${txLink}`) + }) var udappUI = new UniversalDAppUI(udapp) registry.put({api: udappUI, name: 'udappUI'}) diff --git a/src/universal-dapp.js b/src/universal-dapp.js index 88bc0c77c5..4fff562743 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -1,7 +1,3 @@ -/* global */ -'use strict' - -var yo = require('yo-yo') var async = require('async') var ethJSUtil = require('ethereumjs-util') var BN = ethJSUtil.BN @@ -17,8 +13,7 @@ function UniversalDApp (globalRegistry) { this.data = {} this._deps = { config: globalRegistry.get('config').api, - compiler: globalRegistry.get('compiler').api, - logCallback: globalRegistry.get('logCallback').api + compiler: globalRegistry.get('compiler').api } executionContext.event.register('contextChanged', this, function (context) { self.resetEnvironment() @@ -50,10 +45,8 @@ UniversalDApp.prototype.resetEnvironment = function () { this.txRunner = new TxRunner(this.accounts, this._txRunnerAPI) this.txRunner.event.register('transactionBroadcasted', (txhash) => { executionContext.detectNetwork((error, network) => { - if (!error && network) { - var txLink = executionContext.txDetailsLink(network.name, txhash) - if (txLink) this._deps.logCallback(yo`${txLink}`) - } + if (error || !network) return + this.event.trigger('transactionBroadcasted', [txhash, network.name]) }) }) } From ed17c946877debbf5011ca42350267c257c3a7a8 Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Wed, 26 Dec 2018 11:41:17 -0500 Subject: [PATCH 14/50] move txrunner config options to instance initialization --- src/universal-dapp.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/universal-dapp.js b/src/universal-dapp.js index 4fff562743..6728ec7a23 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -42,7 +42,15 @@ UniversalDApp.prototype.resetEnvironment = function () { this._addAccount('71975fbf7fe448e004ac7ae54cad0a383c3906055a65468714156a07385e96ce', '0x56BC75E2D63100000') executionContext.vm().stateManager.cache.flush(function () {}) } - this.txRunner = new TxRunner(this.accounts, this._txRunnerAPI) + this.txRunner = new TxRunner(this.accounts, { + config: this._deps.config, + detectNetwork: (cb) => { + executionContext.detectNetwork(cb) + }, + personalMode: () => { + return this._deps.config.get('settings/personal-mode') + } + }) this.txRunner.event.register('transactionBroadcasted', (txhash) => { executionContext.detectNetwork((error, network) => { if (error || !network) return From e78afe69b41c53adf01decbdb0ba4001cb4afdf0 Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Wed, 26 Dec 2018 11:54:03 -0500 Subject: [PATCH 15/50] remove unused method pendingTransactions --- src/universal-dapp.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/universal-dapp.js b/src/universal-dapp.js index 6728ec7a23..a463f04aee 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -165,10 +165,6 @@ UniversalDApp.prototype.getBalanceInEther = function (address, callback) { }) } -UniversalDApp.prototype.pendingTransactions = function () { - return this.txRunner.pendingTxs -} - UniversalDApp.prototype.pendingTransactionsCount = function () { return Object.keys(this.txRunner.pendingTxs).length } From e2fa3aebc60277c0b05e20755b0b459c069095a6 Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Wed, 26 Dec 2018 12:06:48 -0500 Subject: [PATCH 16/50] fix call to logCallback --- src/app.js | 2 +- src/universal-dapp-ui.js | 15 ++++++++------- src/universal-dapp.js | 1 - 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/app.js b/src/app.js index d14003c9a5..598223283f 100644 --- a/src/app.js +++ b/src/app.js @@ -320,7 +320,7 @@ Please make a backup of your contracts and start using http://remix.ethereum.org if (txLink) registry.get('logCallback').api.logCallback(yo`${txLink}`) }) - var udappUI = new UniversalDAppUI(udapp) + var udappUI = new UniversalDAppUI(udapp, registry) registry.put({api: udappUI, name: 'udappUI'}) // ----------------- Tx listener ----------------- diff --git a/src/universal-dapp-ui.js b/src/universal-dapp-ui.js index a6162057a0..26fcc393a9 100644 --- a/src/universal-dapp-ui.js +++ b/src/universal-dapp-ui.js @@ -21,8 +21,9 @@ var modalCustom = require('./app/ui/modal-dialog-custom') var modalDialog = require('./app/ui/modaldialog') var TreeView = require('./app/ui/TreeView') -function UniversalDAppUI (udapp, opts = {}) { +function UniversalDAppUI (udapp, registry) { this.udapp = udapp + this.registry = registry } function decodeResponseToTreeView (response, fnabi) { @@ -217,9 +218,9 @@ UniversalDAppUI.prototype.getCallButton = function (args) { txFormat.buildData(args.contractName, args.contractAbi, self.udapp.data.contractsDetails, false, args.funABI, args.funABI.type !== 'fallback' ? value : '', (error, data) => { if (!error) { if (!args.funABI.constant) { - self.udapp._deps.logCallback(`${logMsg} pending ... `) + self.registry.logCallback(`${logMsg} pending ... `) } else { - self.udapp._deps.logCallback(`${logMsg}`) + self.registry.logCallback(`${logMsg}`) } if (args.funABI.type === 'fallback') data.dataHex = value self.udapp.callFunction(args.address, data, args.funABI, confirmationCb, continueCb, promptCb, (error, txResult) => { @@ -228,7 +229,7 @@ UniversalDAppUI.prototype.getCallButton = function (args) { if (isVM) { var vmError = txExecution.checkVMError(txResult) if (vmError.error) { - self.udapp._deps.logCallback(`${logMsg} errored: ${vmError.message} `) + self.registry.logCallback(`${logMsg} errored: ${vmError.message} `) return } } @@ -237,14 +238,14 @@ UniversalDAppUI.prototype.getCallButton = function (args) { outputCb(decoded) } } else { - self.udapp._deps.logCallback(`${logMsg} errored: ${error} `) + self.registry.logCallback(`${logMsg} errored: ${error} `) } }) } else { - self.udapp._deps.logCallback(`${logMsg} errored: ${error} `) + self.registry.logCallback(`${logMsg} errored: ${error} `) } }, (msg) => { - self.udapp._deps.logCallback(msg) + self.registry.logCallback(msg) }, (data, runTxCallback) => { // called for libraries deployment self.udapp.runTx(data, confirmationCb, runTxCallback) diff --git a/src/universal-dapp.js b/src/universal-dapp.js index a463f04aee..a9c82b0ef5 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -10,7 +10,6 @@ var executionContext = remixLib.execution.executionContext function UniversalDApp (globalRegistry) { this.event = new EventManager() - this.data = {} this._deps = { config: globalRegistry.get('config').api, compiler: globalRegistry.get('compiler').api From 16a5f489300d543a104c3dc8523f29d19b6cb85f Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Wed, 26 Dec 2018 12:30:33 -0500 Subject: [PATCH 17/50] pass config object to udapp --- src/app.js | 2 +- src/config.js | 2 ++ src/universal-dapp.js | 10 +++++----- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/app.js b/src/app.js index 598223283f..5ccb6a3676 100644 --- a/src/app.js +++ b/src/app.js @@ -313,7 +313,7 @@ Please make a backup of your contracts and start using http://remix.ethereum.org registry.put({api: self._components.compilersArtefacts, name: 'compilersartefacts'}) // ----------------- UniversalDApp ----------------- - var udapp = new UniversalDApp(registry) + var udapp = new UniversalDApp(registry, registry.get('config').api) registry.put({api: udapp, name: 'udapp'}) udapp.event.register('transactionBroadcasted', (txhash, networkName) => { var txLink = executionContext.txDetailsLink(networkName, txhash) diff --git a/src/config.js b/src/config.js index 6a41ebd133..d5095b919b 100644 --- a/src/config.js +++ b/src/config.js @@ -53,6 +53,8 @@ function Config (storage) { return this.unpersistedItems[key] } + // TODO: this only used for *one* property "doNotShowTransactionConfirmationAgain" + // and can be removed once it's refactored away in txRunner this.setUnpersistedProperty = function (key, value) { this.unpersistedItems[key] = value } diff --git a/src/universal-dapp.js b/src/universal-dapp.js index a9c82b0ef5..56073b0072 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -8,7 +8,7 @@ var txHelper = remixLib.execution.txHelper var EventManager = remixLib.EventManager var executionContext = remixLib.execution.executionContext -function UniversalDApp (globalRegistry) { +function UniversalDApp (globalRegistry, config) { this.event = new EventManager() this._deps = { config: globalRegistry.get('config').api, @@ -42,12 +42,12 @@ UniversalDApp.prototype.resetEnvironment = function () { executionContext.vm().stateManager.cache.flush(function () {}) } this.txRunner = new TxRunner(this.accounts, { - config: this._deps.config, + config: this.config, detectNetwork: (cb) => { executionContext.detectNetwork(cb) }, personalMode: () => { - return this._deps.config.get('settings/personal-mode') + return this.config.get('settings/personal-mode') } }) this.txRunner.event.register('transactionBroadcasted', (txhash) => { @@ -71,7 +71,7 @@ UniversalDApp.prototype.createVMAccount = function (privateKey, balance, cb) { UniversalDApp.prototype.newAccount = function (password, passwordPromptCb, cb) { if (!executionContext.isVM()) { - if (!this._deps.config.get('settings/personal-mode')) { + if (!this.config.get('settings/personal-mode')) { return cb('Not running in personal mode') } passwordPromptCb((passphrase) => { @@ -112,7 +112,7 @@ UniversalDApp.prototype.getAccounts = function (cb) { // Weirdness of web3: listAccounts() is sync, `getListAccounts()` is async // See: https://github.com/ethereum/web3.js/issues/442 if (this._deps.config.get('settings/personal-mode')) { - executionContext.web3().personal.getListAccounts(cb) + return executionContext.web3().personal.getListAccounts(cb) } else { executionContext.web3().eth.getAccounts(cb) } From 2171bd84c4ea4533065281a76427f13eda397693 Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Wed, 26 Dec 2018 12:39:32 -0500 Subject: [PATCH 18/50] remove compiler dependency from udapp --- src/universal-dapp-ui.js | 7 ++++++- src/universal-dapp.js | 3 ++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/universal-dapp-ui.js b/src/universal-dapp-ui.js index 26fcc393a9..130ab33731 100644 --- a/src/universal-dapp-ui.js +++ b/src/universal-dapp-ui.js @@ -24,6 +24,11 @@ var TreeView = require('./app/ui/TreeView') function UniversalDAppUI (udapp, registry) { this.udapp = udapp this.registry = registry + + this.compilerData = {contractsDetails: {}} + registry.get('compiler').api.event.register('compilationFinished', (success, data, source) => { + this.compilerData.contractsDetails = success && data ? data.contracts : {} + }) } function decodeResponseToTreeView (response, fnabi) { @@ -215,7 +220,7 @@ UniversalDAppUI.prototype.getCallButton = function (args) { } // contractsDetails is used to resolve libraries - txFormat.buildData(args.contractName, args.contractAbi, self.udapp.data.contractsDetails, false, args.funABI, args.funABI.type !== 'fallback' ? value : '', (error, data) => { + txFormat.buildData(args.contractName, args.contractAbi, self.compilerData.contractsDetails, false, args.funABI, args.funABI.type !== 'fallback' ? value : '', (error, data) => { if (!error) { if (!args.funABI.constant) { self.registry.logCallback(`${logMsg} pending ... `) diff --git a/src/universal-dapp.js b/src/universal-dapp.js index 56073b0072..bdf89119fe 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -10,7 +10,8 @@ var executionContext = remixLib.execution.executionContext function UniversalDApp (globalRegistry, config) { this.event = new EventManager() - this._deps = { + var self = this + self._deps = { config: globalRegistry.get('config').api, compiler: globalRegistry.get('compiler').api } From 5dd4e1bd2d950edb43a8143cad66abf4dc495365 Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Wed, 26 Dec 2018 12:41:32 -0500 Subject: [PATCH 19/50] remove no longer necessary registry paramter from udapp constructor --- src/app.js | 2 +- src/universal-dapp.js | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/app.js b/src/app.js index 5ccb6a3676..5b6c5ecfa4 100644 --- a/src/app.js +++ b/src/app.js @@ -313,7 +313,7 @@ Please make a backup of your contracts and start using http://remix.ethereum.org registry.put({api: self._components.compilersArtefacts, name: 'compilersartefacts'}) // ----------------- UniversalDApp ----------------- - var udapp = new UniversalDApp(registry, registry.get('config').api) + var udapp = new UniversalDApp(registry.get('config').api) registry.put({api: udapp, name: 'udapp'}) udapp.event.register('transactionBroadcasted', (txhash, networkName) => { var txLink = executionContext.txDetailsLink(networkName, txhash) diff --git a/src/universal-dapp.js b/src/universal-dapp.js index bdf89119fe..b53a6641e0 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -8,16 +8,13 @@ var txHelper = remixLib.execution.txHelper var EventManager = remixLib.EventManager var executionContext = remixLib.execution.executionContext -function UniversalDApp (globalRegistry, config) { +function UniversalDApp (config) { this.event = new EventManager() var self = this self._deps = { config: globalRegistry.get('config').api, compiler: globalRegistry.get('compiler').api } - executionContext.event.register('contextChanged', this, function (context) { - self.resetEnvironment() - }) self._txRunnerAPI = { config: self._deps.config, detectNetwork: (cb) => { @@ -30,6 +27,7 @@ function UniversalDApp (globalRegistry, config) { self.txRunner = new TxRunner({}, self._txRunnerAPI) self.accounts = {} self.resetEnvironment() + executionContext.event.register('contextChanged', this.resetEnvironment.bind(this)) } UniversalDApp.prototype.resetEnvironment = function () { From d5a8746d51761629142710715bc279a622710b97 Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Wed, 26 Dec 2018 13:02:42 -0500 Subject: [PATCH 20/50] add todos for later --- src/app.js | 2 ++ src/universal-dapp.js | 3 +++ 2 files changed, 5 insertions(+) diff --git a/src/app.js b/src/app.js index 5b6c5ecfa4..e72736fb8c 100644 --- a/src/app.js +++ b/src/app.js @@ -314,6 +314,7 @@ Please make a backup of your contracts and start using http://remix.ethereum.org // ----------------- UniversalDApp ----------------- var udapp = new UniversalDApp(registry.get('config').api) + // TODO: to remove when possible registry.put({api: udapp, name: 'udapp'}) udapp.event.register('transactionBroadcasted', (txhash, networkName) => { var txLink = executionContext.txDetailsLink(networkName, txhash) @@ -321,6 +322,7 @@ Please make a backup of your contracts and start using http://remix.ethereum.org }) var udappUI = new UniversalDAppUI(udapp, registry) + // TODO: to remove when possible registry.put({api: udappUI, name: 'udappUI'}) // ----------------- Tx listener ----------------- diff --git a/src/universal-dapp.js b/src/universal-dapp.js index b53a6641e0..e3753a2d30 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -40,8 +40,11 @@ UniversalDApp.prototype.resetEnvironment = function () { this._addAccount('71975fbf7fe448e004ac7ae54cad0a383c3906055a65468714156a07385e96ce', '0x56BC75E2D63100000') executionContext.vm().stateManager.cache.flush(function () {}) } + // TODO: most params here can be refactored away in txRunner this.txRunner = new TxRunner(this.accounts, { + // TODO: only used to check value of doNotShowTransactionConfirmationAgain property config: this.config, + // TODO: to refactor, TxRunner already has access to executionContext detectNetwork: (cb) => { executionContext.detectNetwork(cb) }, From 57c73094d896d8f40702f50a66f23c71eddc9e7c Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Wed, 26 Dec 2018 16:19:30 -0500 Subject: [PATCH 21/50] fix log callback --- src/universal-dapp-ui.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/universal-dapp-ui.js b/src/universal-dapp-ui.js index 130ab33731..78e8682fcc 100644 --- a/src/universal-dapp-ui.js +++ b/src/universal-dapp-ui.js @@ -223,9 +223,9 @@ UniversalDAppUI.prototype.getCallButton = function (args) { txFormat.buildData(args.contractName, args.contractAbi, self.compilerData.contractsDetails, false, args.funABI, args.funABI.type !== 'fallback' ? value : '', (error, data) => { if (!error) { if (!args.funABI.constant) { - self.registry.logCallback(`${logMsg} pending ... `) + self.registry.get('logCallback').api(`${logMsg} pending ... `) } else { - self.registry.logCallback(`${logMsg}`) + self.registry.get('logCallback').api(`${logMsg}`) } if (args.funABI.type === 'fallback') data.dataHex = value self.udapp.callFunction(args.address, data, args.funABI, confirmationCb, continueCb, promptCb, (error, txResult) => { @@ -234,7 +234,7 @@ UniversalDAppUI.prototype.getCallButton = function (args) { if (isVM) { var vmError = txExecution.checkVMError(txResult) if (vmError.error) { - self.registry.logCallback(`${logMsg} errored: ${vmError.message} `) + self.registry.get('logCallback').api(`${logMsg} errored: ${vmError.message} `) return } } @@ -243,14 +243,14 @@ UniversalDAppUI.prototype.getCallButton = function (args) { outputCb(decoded) } } else { - self.registry.logCallback(`${logMsg} errored: ${error} `) + self.registry.get('logCallback').api(`${logMsg} errored: ${error} `) } }) } else { - self.registry.logCallback(`${logMsg} errored: ${error} `) + self.registry.get('logCallback').api(`${logMsg} errored: ${error} `) } }, (msg) => { - self.registry.logCallback(msg) + self.registry.get('logCallback').api(msg) }, (data, runTxCallback) => { // called for libraries deployment self.udapp.runTx(data, confirmationCb, runTxCallback) From 9dc1ad76db3dc26d0911b3856e31d5699a673e8f Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Wed, 26 Dec 2018 13:32:18 -0500 Subject: [PATCH 22/50] move settings and contractsDropdown UI to their own modules --- src/app/panels/righthand-panel.js | 74 +-- src/app/tabs/run-tab.js | 675 +----------------------- src/app/tabs/runTab/contractDropdown.js | 391 ++++++++++++++ src/app/tabs/runTab/settings.js | 299 +++++++++++ 4 files changed, 732 insertions(+), 707 deletions(-) create mode 100644 src/app/tabs/runTab/contractDropdown.js create mode 100644 src/app/tabs/runTab/settings.js diff --git a/src/app/panels/righthand-panel.js b/src/app/panels/righthand-panel.js index b995c77d73..723339d585 100644 --- a/src/app/panels/righthand-panel.js +++ b/src/app/panels/righthand-panel.js @@ -11,8 +11,43 @@ const DraggableContent = require('../ui/draggableContent') const styles = styleguide.chooser() -module.exports = class RighthandPanel { - constructor ({pluginManager, tabs}, localRegistry) { +const css = csjs` + .righthandpanel { + display : flex; + flex-direction : column; + top : 0; + right : 0; + bottom : 0; + box-sizing : border-box; + overflow : hidden; + height : 100%; + } + .header { + height : 100%; + } + .dragbar { + position : absolute; + width : 0.5em; + top : 3em; + bottom : 0; + cursor : col-resize; + z-index : 999; + border-left : 2px solid ${styles.rightPanel.bar_Dragging}; + } + .ghostbar { + width : 3px; + background-color : ${styles.rightPanel.bar_Ghost}; + opacity : 0.5; + position : absolute; + cursor : col-resize; + z-index : 9999; + top : 0; + bottom : 0; + } +` + +class RighthandPanel { + constructor (localRegistry) { const self = this self._components = {} self._components.registry = localRegistry || globalRegistry @@ -125,37 +160,4 @@ module.exports = class RighthandPanel { } } -const css = csjs` - .righthandpanel { - display : flex; - flex-direction : column; - top : 0; - right : 0; - bottom : 0; - box-sizing : border-box; - overflow : hidden; - height : 100%; - } - .header { - height : 100%; - } - .dragbar { - position : absolute; - width : 0.5em; - top : 3em; - bottom : 0; - cursor : col-resize; - z-index : 999; - border-left : 2px solid ${styles.rightPanel.bar_Dragging}; - } - .ghostbar { - width : 3px; - background-color : ${styles.rightPanel.bar_Ghost}; - opacity : 0.5; - position : absolute; - cursor : col-resize; - z-index : 9999; - top : 0; - bottom : 0; - } -` +module.exports = RighthandPanel diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index ae6129d33d..a7c1c5a9d1 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -1,32 +1,18 @@ 'use strict' var $ = require('jquery') var yo = require('yo-yo') -var remixLib = require('remix-lib') -var ethJSUtil = require('ethereumjs-util') var csjs = require('csjs-inject') -var txExecution = remixLib.execution.txExecution -var txFormat = remixLib.execution.txFormat -var txHelper = remixLib.execution.txHelper -var typeConversion = remixLib.execution.typeConversion var EventManager = require('../../lib/events') var globlalRegistry = require('../../global/registry') var helper = require('../../lib/helper.js') var executionContext = require('../../execution-context') var modalDialogCustom = require('../ui/modal-dialog-custom') -var copyToClipboard = require('../ui/copy-to-clipboard') -const Buffer = require('safe-buffer').Buffer -var Personal = require('web3-eth-personal') var Card = require('../ui/card') var Recorder = require('../../recorder') -var addTooltip = require('../ui/tooltip') var css = require('./styles/run-tab-styles') -var MultiParamManager = require('../../multiParamManager') -var modalDialog = require('../ui/modaldialog') -var CompilerAbstract = require('../compiler/compiler-abstract') -var tootip = require('../ui/tooltip') -var confirmDialog = require('../execution/confirmDialog') -var modalCustom = require('../ui/modal-dialog-custom') +var settingsUI = require('./runTab/settings.js') +var contractDropdownUI = require('./runTab/contractDropdown.js') function runTab (opts, localRegistry) { /* ------------------------- @@ -144,8 +130,8 @@ function runTab (opts, localRegistry) { --------------------------- */ var el = yo`
- ${settings(container, self)} - ${contractDropdown(self.event, self)} + ${settingsUI(container, self)} + ${contractDropdownUI(self.event, self)} ${recorderCard.render()} ${self._view.instanceContainer}
@@ -155,47 +141,6 @@ function runTab (opts, localRegistry) { return { render () { return container } } } -var accountListCallId = 0 -var loadedAccounts = {} -function fillAccountsList (container, self) { - accountListCallId++ - (function (callid) { - var txOrigin = container.querySelector('#txorigin') - self._deps.udapp.getAccounts((err, accounts) => { - if (accountListCallId > callid) return - accountListCallId++ - if (err) { addTooltip(`Cannot get account list: ${err}`) } - for (var loadedaddress in loadedAccounts) { - if (accounts.indexOf(loadedaddress) === -1) { - txOrigin.removeChild(txOrigin.querySelector('option[value="' + loadedaddress + '"]')) - delete loadedAccounts[loadedaddress] - } - } - for (var i in accounts) { - var address = accounts[i] - if (!loadedAccounts[address]) { - txOrigin.appendChild(yo``) - loadedAccounts[address] = 1 - } - } - txOrigin.setAttribute('value', accounts[0]) - }) - })(accountListCallId) -} - -function updateAccountBalances (container, self) { - var accounts = $(container.querySelector('#txorigin')).children('option') - accounts.each(function (index, value) { - (function (acc) { - self._deps.udapp.getBalanceInEther(accounts[acc].value, function (err, res) { - if (!err) { - accounts[acc].innerText = helper.shortenAddress(accounts[acc].value, res) - } - }) - })(index) - }) -} - /* ------------------------------------------------ RECORDER ------------------------------------------------ */ @@ -289,617 +234,5 @@ function makeRecorder (registry, runTabEvent, self) { return { recordButton, runButton } } -/* ------------------------------------------------ - CONTRACT (deploy or access deployed) ------------------------------------------------- */ - -function contractDropdown (events, self) { - var instanceContainer = self._view.instanceContainer - var instanceContainerTitle = self._view.instanceContainerTitle - instanceContainer.appendChild(instanceContainerTitle) - instanceContainer.appendChild(self._view.noInstancesText) - var compFails = yo`` - var info = yo`` - - var newlyCompiled = (success, data, source, compiler, compilerFullName) => { - getContractNames(success, data, compiler, compilerFullName) - if (success) { - compFails.style.display = 'none' - document.querySelector(`.${css.contractNames}`).classList.remove(css.contractNamesError) - } else { - compFails.style.display = 'block' - document.querySelector(`.${css.contractNames}`).classList.add(css.contractNamesError) - } - } - - self._deps.pluginManager.event.register('sendCompilationResult', (file, source, languageVersion, data) => { - let compiler = new CompilerAbstract(languageVersion, data, source) - newlyCompiled(true, data, source, compiler, languageVersion) - }) - - var deployAction = (value) => { - self._view.createPanel.style.display = value - self._view.orLabel.style.display = value - } - - self._deps.fileManager.event.register('currentFileChanged', (currentFile) => { - document.querySelector(`.${css.contractNames}`).classList.remove(css.contractNamesError) - var contractNames = document.querySelector(`.${css.contractNames.classNames[0]}`) - contractNames.innerHTML = '' - if (/.(.abi)$/.exec(currentFile)) { - deployAction('none') - compFails.style.display = 'none' - contractNames.appendChild(yo``) - selectContractNames.setAttribute('disabled', true) - } else if (/.(.sol)$/.exec(currentFile)) { - deployAction('block') - } - }) - - var atAddressButtonInput = yo`` - var selectContractNames = yo`` - - function getSelectedContract () { - var contract = selectContractNames.children[selectContractNames.selectedIndex] - var contractName = contract.innerHTML - var compiler = self._deps.compilersArtefacts['__last'] - if (!compiler) return null - - if (contractName) { - return { - name: contractName, - contract: compiler.getContract(contractName), - compiler - } - } - return null - } - - self._view.createPanel = yo`
` - self._view.orLabel = yo`
or
` - var el = yo` -
-
- ${selectContractNames} ${compFails} ${info} -
-
- ${self._view.createPanel} - ${self._view.orLabel} -
-
At Address
- ${atAddressButtonInput} -
-
-
- ` - - function setInputParamsPlaceHolder () { - self._view.createPanel.innerHTML = '' - if (selectContractNames.selectedIndex >= 0 && selectContractNames.children.length > 0) { - var selectedContract = getSelectedContract() - var ctrabi = txHelper.getConstructorInterface(selectedContract.contract.object.abi) - var ctrEVMbc = selectedContract.contract.object.evm.bytecode.object - var createConstructorInstance = new MultiParamManager(0, ctrabi, (valArray, inputsValues) => { - createInstance(inputsValues, selectedContract.compiler) - }, txHelper.inputParametersDeclarationToString(ctrabi.inputs), 'Deploy', ctrEVMbc) - self._view.createPanel.appendChild(createConstructorInstance.render()) - return - } else { - self._view.createPanel.innerHTML = 'No compiled contracts' - } - } - - selectContractNames.addEventListener('change', setInputParamsPlaceHolder) - - function createInstanceCallback (selectedContract, data) { - self._deps.logCallback(`creation of ${selectedContract.name} pending...`) - if (data) { - data.contractName = selectedContract.name - data.linkReferences = selectedContract.contract.object.evm.bytecode.linkReferences - data.contractABI = selectedContract.contract.object.abi - } - self._deps.udapp.createContract(data, - - (network, tx, gasEstimation, continueTxExecution, cancelCb) => { - if (network.name !== 'Main') { - return continueTxExecution(null) - } - var amount = executionContext.web3().fromWei(typeConversion.toInt(tx.value), 'ether') - var content = confirmDialog(tx, amount, gasEstimation, self, - (gasPrice, cb) => { - let txFeeText, priceStatus - // TODO: this try catch feels like an anti pattern, can/should be - // removed, but for now keeping the original logic - try { - var fee = executionContext.web3().toBigNumber(tx.gas).mul(executionContext.web3().toBigNumber(executionContext.web3().toWei(gasPrice.toString(10), 'gwei'))) - txFeeText = ' ' + executionContext.web3().fromWei(fee.toString(10), 'ether') + ' Ether' - priceStatus = true - } catch (e) { - txFeeText = ' Please fix this issue before sending any transaction. ' + e.message - priceStatus = false - } - cb(txFeeText, priceStatus) - }, - (cb) => { - executionContext.web3().eth.getGasPrice((error, gasPrice) => { - var warnMessage = ' Please fix this issue before sending any transaction. ' - if (error) { - return cb('Unable to retrieve the current network gas price.' + warnMessage + error) - } - try { - var gasPriceValue = executionContext.web3().fromWei(gasPrice.toString(10), 'gwei') - cb(null, gasPriceValue) - } catch (e) { - cb(warnMessage + e.message, null, false) - } - }) - } - ) - modalDialog('Confirm transaction', content, - { label: 'Confirm', - fn: () => { - self._deps.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked) - // TODO: check if this is check is still valid given the refactor - if (!content.gasPriceStatus) { - cancelCb('Given gas price is not correct') - } else { - var gasPrice = executionContext.web3().toWei(content.querySelector('#gasprice').value, 'gwei') - continueTxExecution(gasPrice) - } - }}, { - label: 'Cancel', - fn: () => { - return cancelCb('Transaction canceled by user.') - } - }) - }, - (error, continueTxExecution, cancelCb) => { - if (error) { - var msg = typeof error !== 'string' ? error.message : error - modalDialog('Gas estimation failed', yo`
Gas estimation errored with the following message (see below). - The transaction execution will likely fail. Do you want to force sending?
- ${msg} -
`, - { - label: 'Send Transaction', - fn: () => { - continueTxExecution() - }}, { - label: 'Cancel Transaction', - fn: () => { - cancelCb() - } - }) - } else { - continueTxExecution() - } - }, - function (okCb, cancelCb) { - modalCustom.promptPassphrase(null, 'Personal mode is enabled. Please provide passphrase of account', '', okCb, cancelCb) - }, - (error, txResult) => { - if (!error) { - var isVM = executionContext.isVM() - if (isVM) { - var vmError = txExecution.checkVMError(txResult) - if (vmError.error) { - self._deps.logCallback(vmError.message) - return - } - } - if (txResult.result.status && txResult.result.status === '0x0') { - self._deps.logCallback(`creation of ${selectedContract.name} errored: transaction execution failed`) - return - } - var noInstancesText = self._view.noInstancesText - if (noInstancesText.parentNode) { noInstancesText.parentNode.removeChild(noInstancesText) } - var address = isVM ? txResult.result.createdAddress : txResult.result.contractAddress - instanceContainer.appendChild(self._deps.udappUI.renderInstance(selectedContract.contract.object, address, selectContractNames.value)) - } else { - self._deps.logCallback(`creation of ${selectedContract.name} errored: ${error}`) - } - } - ) - } - - // DEPLOY INSTANCE - function createInstance (args, compiler) { - var selectedContract = getSelectedContract() - - if (selectedContract.contract.object.evm.bytecode.object.length === 0) { - modalDialogCustom.alert('This contract may be abstract, not implement an abstract parent\'s methods completely or not invoke an inherited contract\'s constructor correctly.') - return - } - - var forceSend = () => { - var constructor = txHelper.getConstructorInterface(selectedContract.contract.object.abi) - self._deps.filePanel.compilerMetadata().deployMetadataOf(selectedContract.name, (error, contractMetadata) => { - if (error) return self._deps.logCallback(`creation of ${selectedContract.name} errored: ` + error) - if (!contractMetadata || (contractMetadata && contractMetadata.autoDeployLib)) { - txFormat.buildData(selectedContract.name, selectedContract.contract.object, compiler.getContracts(), true, constructor, args, (error, data) => { - if (error) return self._deps.logCallback(`creation of ${selectedContract.name} errored: ` + error) - createInstanceCallback(selectedContract, data) - }, (msg) => { - self._deps.logCallback(msg) - }, (data, runTxCallback) => { - // called for libraries deployment - self._deps.udapp.runTx(data, - (network, tx, gasEstimation, continueTxExecution, cancelCb) => { - if (network.name !== 'Main') { - return continueTxExecution(null) - } - var amount = executionContext.web3().fromWei(typeConversion.toInt(tx.value), 'ether') - var content = confirmDialog(tx, amount, gasEstimation, self, - (gasPrice, cb) => { - let txFeeText, priceStatus - // TODO: this try catch feels like an anti pattern, can/should be - // removed, but for now keeping the original logic - try { - var fee = executionContext.web3().toBigNumber(tx.gas).mul(executionContext.web3().toBigNumber(executionContext.web3().toWei(gasPrice.toString(10), 'gwei'))) - txFeeText = ' ' + executionContext.web3().fromWei(fee.toString(10), 'ether') + ' Ether' - priceStatus = true - } catch (e) { - txFeeText = ' Please fix this issue before sending any transaction. ' + e.message - priceStatus = false - } - cb(txFeeText, priceStatus) - }, - (cb) => { - executionContext.web3().eth.getGasPrice((error, gasPrice) => { - var warnMessage = ' Please fix this issue before sending any transaction. ' - if (error) { - return cb('Unable to retrieve the current network gas price.' + warnMessage + error) - } - try { - var gasPriceValue = executionContext.web3().fromWei(gasPrice.toString(10), 'gwei') - cb(null, gasPriceValue) - } catch (e) { - cb(warnMessage + e.message, null, false) - } - }) - } - ) - modalDialog('Confirm transaction', content, - { label: 'Confirm', - fn: () => { - self._deps.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked) - // TODO: check if this is check is still valid given the refactor - if (!content.gasPriceStatus) { - cancelCb('Given gas price is not correct') - } else { - var gasPrice = executionContext.web3().toWei(content.querySelector('#gasprice').value, 'gwei') - continueTxExecution(gasPrice) - } - }}, { - label: 'Cancel', - fn: () => { - return cancelCb('Transaction canceled by user.') - } - }) - }, - function (okCb, cancelCb) { - modalCustom.promptPassphrase(null, 'Personal mode is enabled. Please provide passphrase of account', '', okCb, cancelCb) - }, - runTxCallback) - }) - } else { - if (Object.keys(selectedContract.contract.object.evm.bytecode.linkReferences).length) self._deps.logCallback(`linking ${JSON.stringify(selectedContract.contract.object.evm.bytecode.linkReferences, null, '\t')} using ${JSON.stringify(contractMetadata.linkReferences, null, '\t')}`) - txFormat.encodeConstructorCallAndLinkLibraries(selectedContract.contract.object, args, constructor, contractMetadata.linkReferences, selectedContract.contract.object.evm.bytecode.linkReferences, (error, data) => { - if (error) return self._deps.logCallback(`creation of ${selectedContract.name} errored: ` + error) - createInstanceCallback(selectedContract, data) - }) - } - }) - } - - if (selectedContract.contract.object.evm.deployedBytecode && selectedContract.contract.object.evm.deployedBytecode.object.length / 2 > 24576) { - modalDialog('Contract code size over limit', yo`
Contract creation initialization returns data with length of more than 24576 bytes. The deployment will likely fails.
- More info: eip-170 -
`, - { - label: 'Force Send', - fn: () => { - forceSend() - }}, { - label: 'Cancel', - fn: () => { - self._deps.logCallback(`creation of ${selectedContract.name} canceled by user.`) - } - }) - } else { - forceSend() - } - } - - // ACCESS DEPLOYED INSTANCE - function loadFromAddress () { - var noInstancesText = self._view.noInstancesText - if (noInstancesText.parentNode) { noInstancesText.parentNode.removeChild(noInstancesText) } - var address = atAddressButtonInput.value - if (!ethJSUtil.isValidAddress(address)) { - return modalDialogCustom.alert('Invalid address.') - } - if (/[a-f]/.test(address) && /[A-F]/.test(address) && !ethJSUtil.isValidChecksumAddress(address)) { - return modalDialogCustom.alert('Invalid checksum address.') - } - if (/.(.abi)$/.exec(self._deps.config.get('currentFile'))) { - modalDialogCustom.confirm(null, 'Do you really want to interact with ' + address + ' using the current ABI definition ?', () => { - var abi - try { - abi = JSON.parse(self._deps.editor.currentContent()) - } catch (e) { - return modalDialogCustom.alert('Failed to parse the current file as JSON ABI.') - } - instanceContainer.appendChild(self._deps.udappUI.renderInstanceFromABI(abi, address, address)) - }) - } else { - var selectedContract = getSelectedContract() - instanceContainer.appendChild(self._deps.udappUI.renderInstance(selectedContract.contract.object, address, selectContractNames.value)) - } - } - - // GET NAMES OF ALL THE CONTRACTS - function getContractNames (success, data, compiler, compilerFullName) { - var contractNames = document.querySelector(`.${css.contractNames.classNames[0]}`) - contractNames.innerHTML = '' - if (success) { - selectContractNames.removeAttribute('disabled') - compiler.visitContracts((contract) => { - contractNames.appendChild(yo``) - }) - } else { - selectContractNames.setAttribute('disabled', true) - } - setInputParamsPlaceHolder() - } - - return el -} -/* ------------------------------------------------ - section SETTINGS: Environment, Account, Gas, Value ------------------------------------------------- */ -function settings (container, self) { - // VARIABLES - var net = yo`` - var networkcallid = 0 - const updateNetwork = (cb) => { - networkcallid++ - (function (callid) { - executionContext.detectNetwork((err, { id, name } = {}) => { - if (networkcallid > callid) return - networkcallid++ - if (err) { - console.error(err) - net.innerHTML = 'can\'t detect network ' - } else { - net.innerHTML = ` ${name} (${id || '-'})` - } - if (cb) cb(err, {id, name}) - }) - })(networkcallid) - } - var environmentEl = yo` -
-
- Environment -
-
- ${net} - - -
-
- ` - var accountEl = yo` -
-
- Account - -
-
- - ${copyToClipboard(() => document.querySelector('#runTabView #txorigin').value)} - -
-
- ` - var gasPriceEl = yo` -
-
Gas limit
- -
- ` - var valueEl = yo` -
-
Value
- - -
- ` - // DOM ELEMENT - var el = yo` -
- ${environmentEl} - ${accountEl} - ${gasPriceEl} - ${valueEl} -
- ` - // HELPER FUNCTIONS AND EVENTS - self._deps.udapp.event.register('transactionExecuted', (error, from, to, data, lookupOnly, txResult) => { - if (error) return - if (!lookupOnly) el.querySelector('#value').value = '0' - updateAccountBalances(container, self) - }) - - // DROPDOWN - var selectExEnv = environmentEl.querySelector('#selectExEnvOptions') - - function setFinalContext () { - // set the final context. Cause it is possible that this is not the one we've originaly selected - selectExEnv.value = executionContext.getProvider() - self.event.trigger('clearInstance', []) - updateNetwork() - fillAccountsList(el, self) - } - - self.event.register('clearInstance', () => { - var instanceContainer = self._view.instanceContainer - var instanceContainerTitle = self._view.instanceContainerTitle - instanceContainer.innerHTML = '' // clear the instances list - instanceContainer.appendChild(instanceContainerTitle) - instanceContainer.appendChild(self._view.noInstancesText) - }) - - executionContext.event.register('addProvider', (network) => { - selectExEnv.appendChild(yo``) - tootip(`${network.name} [${network.url}] added`) - }) - - executionContext.event.register('removeProvider', (name) => { - var env = selectExEnv.querySelector(`option[value="${name}"]`) - if (env) { - selectExEnv.removeChild(env) - tootip(`${name} removed`) - } - }) - - selectExEnv.addEventListener('change', function (event) { - let context = selectExEnv.options[selectExEnv.selectedIndex].value - executionContext.executionContextChange(context, null, () => { - modalDialogCustom.confirm(null, 'Are you sure you want to connect to an ethereum node?', () => { - modalDialogCustom.prompt(null, 'Web3 Provider Endpoint', 'http://localhost:8545', (target) => { - executionContext.setProviderFromEndpoint(target, context, (alertMsg) => { - if (alertMsg) { - modalDialogCustom.alert(alertMsg) - } - setFinalContext() - }) - }, setFinalContext) - }, setFinalContext) - }, (alertMsg) => { - modalDialogCustom.alert(alertMsg) - }, setFinalContext) - }) - - selectExEnv.value = executionContext.getProvider() - executionContext.event.register('contextChanged', (context, silent) => { - setFinalContext() - }) - - setInterval(() => { - updateNetwork() - fillAccountsList(el, self) - }, 5000) - - setInterval(() => { - updateAccountBalances(container, self) - }, 10000) - - function newAccount () { - self._deps.udapp.newAccount('', - (cb) => { - modalCustom.promptPassphraseCreation((error, passphrase) => { - if (error) { - return modalCustom.alert(error) - } - cb(passphrase) - }, () => {}) - }, - (error, address) => { - if (!error) { - addTooltip(`account ${address} created`) - } else { - addTooltip('Cannot create an account: ' + error) - } - } - ) - } - function signMessage (event) { - self._deps.udapp.getAccounts((err, accounts) => { - if (err) { addTooltip(`Cannot get account list: ${err}`) } - var signMessageDialog = { 'title': 'Sign a message', 'text': 'Enter a message to sign', 'inputvalue': 'Message to sign' } - var $txOrigin = container.querySelector('#txorigin') - var account = $txOrigin.selectedOptions[0].value - var isVM = executionContext.isVM() - var isInjected = executionContext.getProvider() === 'injected' - function alertSignedData (error, hash, signedData) { - if (error && error.message !== '') { - console.log(error) - addTooltip(error.message) - } else { - modalDialogCustom.alert(yo`
hash:${hash}
signature:${signedData}
`) - } - } - if (isVM) { - modalDialogCustom.promptMulti(signMessageDialog, (message) => { - const personalMsg = ethJSUtil.hashPersonalMessage(Buffer.from(message)) - var privKey = self._deps.udapp.accounts[account].privateKey - try { - var rsv = ethJSUtil.ecsign(personalMsg, privKey) - var signedData = ethJSUtil.toRpcSig(rsv.v, rsv.r, rsv.s) - alertSignedData(null, '0x' + personalMsg.toString('hex'), signedData) - } catch (e) { - addTooltip(e.message) - return - } - }, false) - } else if (isInjected) { - modalDialogCustom.promptMulti(signMessageDialog, (message) => { - const hashedMsg = executionContext.web3().sha3(message) - try { - executionContext.web3().eth.sign(account, hashedMsg, (error, signedData) => { - alertSignedData(error, hashedMsg, signedData) - }) - } catch (e) { - addTooltip(e.message) - console.log(e) - return - } - }) - } else { - modalDialogCustom.promptPassphrase('Passphrase to sign a message', 'Enter your passphrase for this account to sign the message', '', (passphrase) => { - modalDialogCustom.promptMulti(signMessageDialog, (message) => { - const hashedMsg = executionContext.web3().sha3(message) - try { - var personal = new Personal(executionContext.web3().currentProvider) - personal.sign(hashedMsg, account, passphrase, (error, signedData) => { - alertSignedData(error, hashedMsg, signedData) - }) - } catch (e) { - addTooltip(e.message) - console.log(e) - return - } - }) - }, false) - } - }) - } - - return el -} module.exports = runTab diff --git a/src/app/tabs/runTab/contractDropdown.js b/src/app/tabs/runTab/contractDropdown.js new file mode 100644 index 0000000000..e3c23cad13 --- /dev/null +++ b/src/app/tabs/runTab/contractDropdown.js @@ -0,0 +1,391 @@ +var yo = require('yo-yo') +var ethJSUtil = require('ethereumjs-util') +var css = require('../styles/run-tab-styles') +var executionContext = require('../../../execution-context') +var modalDialogCustom = require('../../ui/modal-dialog-custom') +var modalCustom = require('../../ui/modal-dialog-custom') +var CompilerAbstract = require('../../compiler/compiler-abstract') +var remixLib = require('remix-lib') +var txExecution = remixLib.execution.txExecution +var txFormat = remixLib.execution.txFormat +var txHelper = remixLib.execution.txHelper +var typeConversion = remixLib.execution.typeConversion +var confirmDialog = require('../../execution/confirmDialog') +var modalDialog = require('../../ui/modaldialog') +var MultiParamManager = require('../../../multiParamManager') + +function contractDropdown (events, self) { + var instanceContainer = self._view.instanceContainer + var instanceContainerTitle = self._view.instanceContainerTitle + instanceContainer.appendChild(instanceContainerTitle) + instanceContainer.appendChild(self._view.noInstancesText) + var compFails = yo`` + var info = yo`` + + var newlyCompiled = (success, data, source, compiler, compilerFullName) => { + getContractNames(success, data, compiler, compilerFullName) + if (success) { + compFails.style.display = 'none' + document.querySelector(`.${css.contractNames}`).classList.remove(css.contractNamesError) + } else { + compFails.style.display = 'block' + document.querySelector(`.${css.contractNames}`).classList.add(css.contractNamesError) + } + } + + self._deps.pluginManager.event.register('sendCompilationResult', (file, source, languageVersion, data) => { + // TODO check whether the tab is configured + let compiler = new CompilerAbstract(languageVersion, data) + self._deps.compilersArtefacts[languageVersion] = compiler + self._deps.compilersArtefacts['__last'] = compiler + newlyCompiled(true, data, source, compiler, languageVersion) + }) + + self._deps.compiler.event.register('compilationFinished', (success, data, source) => { + var name = 'solidity' + let compiler = new CompilerAbstract(name, data) + self._deps.compilersArtefacts[name] = compiler + self._deps.compilersArtefacts['__last'] = compiler + newlyCompiled(success, data, source, self._deps.compiler, name) + }) + + var deployAction = (value) => { + self._view.createPanel.style.display = value + self._view.orLabel.style.display = value + } + + self._deps.fileManager.event.register('currentFileChanged', (currentFile) => { + document.querySelector(`.${css.contractNames}`).classList.remove(css.contractNamesError) + var contractNames = document.querySelector(`.${css.contractNames.classNames[0]}`) + contractNames.innerHTML = '' + if (/.(.abi)$/.exec(currentFile)) { + deployAction('none') + compFails.style.display = 'none' + contractNames.appendChild(yo``) + selectContractNames.setAttribute('disabled', true) + } else if (/.(.sol)$/.exec(currentFile)) { + deployAction('block') + } + }) + + var atAddressButtonInput = yo`` + var selectContractNames = yo`` + + function getSelectedContract () { + var contract = selectContractNames.children[selectContractNames.selectedIndex] + var contractName = contract.innerHTML + var compiler = self._deps.compilersArtefacts[contract.getAttribute('compiler')] + if (!compiler) return null + + if (contractName) { + return { + name: contractName, + contract: compiler.getContract(contractName), + compiler + } + } + return null + } + + self._view.createPanel = yo`
` + self._view.orLabel = yo`
or
` + var el = yo` +
+
+ ${selectContractNames} ${compFails} ${info} +
+
+ ${self._view.createPanel} + ${self._view.orLabel} +
+
At Address
+ ${atAddressButtonInput} +
+
+
+ ` + + function setInputParamsPlaceHolder () { + self._view.createPanel.innerHTML = '' + if (selectContractNames.selectedIndex >= 0 && selectContractNames.children.length > 0) { + var selectedContract = getSelectedContract() + var ctrabi = txHelper.getConstructorInterface(selectedContract.contract.object.abi) + var ctrEVMbc = selectedContract.contract.object.evm.bytecode.object + var createConstructorInstance = new MultiParamManager(0, ctrabi, (valArray, inputsValues) => { + createInstance(inputsValues, selectedContract.compiler) + }, txHelper.inputParametersDeclarationToString(ctrabi.inputs), 'Deploy', ctrEVMbc) + self._view.createPanel.appendChild(createConstructorInstance.render()) + return + } else { + self._view.createPanel.innerHTML = 'No compiled contracts' + } + } + + selectContractNames.addEventListener('change', setInputParamsPlaceHolder) + + function createInstanceCallback (selectedContract, data) { + self._deps.logCallback(`creation of ${selectedContract.name} pending...`) + if (data) { + data.contractName = selectedContract.name + data.linkReferences = selectedContract.contract.object.evm.bytecode.linkReferences + data.contractABI = selectedContract.contract.object.abi + } + self._deps.udapp.createContract(data, + + (network, tx, gasEstimation, continueTxExecution, cancelCb) => { + if (network.name !== 'Main') { + return continueTxExecution(null) + } + var amount = executionContext.web3().fromWei(typeConversion.toInt(tx.value), 'ether') + var content = confirmDialog(tx, amount, gasEstimation, self, + (gasPrice, cb) => { + let txFeeText, priceStatus + // TODO: this try catch feels like an anti pattern, can/should be + // removed, but for now keeping the original logic + try { + var fee = executionContext.web3().toBigNumber(tx.gas).mul(executionContext.web3().toBigNumber(executionContext.web3().toWei(gasPrice.toString(10), 'gwei'))) + txFeeText = ' ' + executionContext.web3().fromWei(fee.toString(10), 'ether') + ' Ether' + priceStatus = true + } catch (e) { + txFeeText = ' Please fix this issue before sending any transaction. ' + e.message + priceStatus = false + } + cb(txFeeText, priceStatus) + }, + (cb) => { + executionContext.web3().eth.getGasPrice((error, gasPrice) => { + var warnMessage = ' Please fix this issue before sending any transaction. ' + if (error) { + return cb('Unable to retrieve the current network gas price.' + warnMessage + error) + } + try { + var gasPriceValue = executionContext.web3().fromWei(gasPrice.toString(10), 'gwei') + cb(null, gasPriceValue) + } catch (e) { + cb(warnMessage + e.message, null, false) + } + }) + } + ) + modalDialog('Confirm transaction', content, + { label: 'Confirm', + fn: () => { + self._deps.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked) + // TODO: check if this is check is still valid given the refactor + if (!content.gasPriceStatus) { + cancelCb('Given gas price is not correct') + } else { + var gasPrice = executionContext.web3().toWei(content.querySelector('#gasprice').value, 'gwei') + continueTxExecution(gasPrice) + } + }}, { + label: 'Cancel', + fn: () => { + return cancelCb('Transaction canceled by user.') + } + }) + }, + (error, continueTxExecution, cancelCb) => { + if (error) { + var msg = typeof error !== 'string' ? error.message : error + modalDialog('Gas estimation failed', yo`
Gas estimation errored with the following message (see below). + The transaction execution will likely fail. Do you want to force sending?
+ ${msg} +
`, + { + label: 'Send Transaction', + fn: () => { + continueTxExecution() + }}, { + label: 'Cancel Transaction', + fn: () => { + cancelCb() + } + }) + } else { + continueTxExecution() + } + }, + function (okCb, cancelCb) { + modalCustom.promptPassphrase(null, 'Personal mode is enabled. Please provide passphrase of account', '', okCb, cancelCb) + }, + (error, txResult) => { + if (!error) { + var isVM = executionContext.isVM() + if (isVM) { + var vmError = txExecution.checkVMError(txResult) + if (vmError.error) { + self._deps.logCallback(vmError.message) + return + } + } + if (txResult.result.status && txResult.result.status === '0x0') { + self._deps.logCallback(`creation of ${selectedContract.name} errored: transaction execution failed`) + return + } + var noInstancesText = self._view.noInstancesText + if (noInstancesText.parentNode) { noInstancesText.parentNode.removeChild(noInstancesText) } + var address = isVM ? txResult.result.createdAddress : txResult.result.contractAddress + instanceContainer.appendChild(self._deps.udappUI.renderInstance(selectedContract.contract.object, address, selectContractNames.value)) + } else { + self._deps.logCallback(`creation of ${selectedContract.name} errored: ${error}`) + } + } + ) + } + + // DEPLOY INSTANCE + function createInstance (args, compiler) { + var selectedContract = getSelectedContract() + + if (selectedContract.contract.object.evm.bytecode.object.length === 0) { + modalDialogCustom.alert('This contract may be abstract, not implement an abstract parent\'s methods completely or not invoke an inherited contract\'s constructor correctly.') + return + } + + var forceSend = () => { + var constructor = txHelper.getConstructorInterface(selectedContract.contract.object.abi) + self._deps.filePanel.compilerMetadata().deployMetadataOf(selectedContract.name, (error, contractMetadata) => { + if (error) return self._deps.logCallback(`creation of ${selectedContract.name} errored: ` + error) + if (!contractMetadata || (contractMetadata && contractMetadata.autoDeployLib)) { + txFormat.buildData(selectedContract.name, selectedContract.contract.object, compiler.getContracts(), true, constructor, args, (error, data) => { + if (error) return self._deps.logCallback(`creation of ${selectedContract.name} errored: ` + error) + createInstanceCallback(selectedContract, data) + }, (msg) => { + self._deps.logCallback(msg) + }, (data, runTxCallback) => { + // called for libraries deployment + self._deps.udapp.runTx(data, + (network, tx, gasEstimation, continueTxExecution, cancelCb) => { + if (network.name !== 'Main') { + return continueTxExecution(null) + } + var amount = executionContext.web3().fromWei(typeConversion.toInt(tx.value), 'ether') + var content = confirmDialog(tx, amount, gasEstimation, self, + (gasPrice, cb) => { + let txFeeText, priceStatus + // TODO: this try catch feels like an anti pattern, can/should be + // removed, but for now keeping the original logic + try { + var fee = executionContext.web3().toBigNumber(tx.gas).mul(executionContext.web3().toBigNumber(executionContext.web3().toWei(gasPrice.toString(10), 'gwei'))) + txFeeText = ' ' + executionContext.web3().fromWei(fee.toString(10), 'ether') + ' Ether' + priceStatus = true + } catch (e) { + txFeeText = ' Please fix this issue before sending any transaction. ' + e.message + priceStatus = false + } + cb(txFeeText, priceStatus) + }, + (cb) => { + executionContext.web3().eth.getGasPrice((error, gasPrice) => { + var warnMessage = ' Please fix this issue before sending any transaction. ' + if (error) { + return cb('Unable to retrieve the current network gas price.' + warnMessage + error) + } + try { + var gasPriceValue = executionContext.web3().fromWei(gasPrice.toString(10), 'gwei') + cb(null, gasPriceValue) + } catch (e) { + cb(warnMessage + e.message, null, false) + } + }) + } + ) + modalDialog('Confirm transaction', content, + { label: 'Confirm', + fn: () => { + self._deps.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked) + // TODO: check if this is check is still valid given the refactor + if (!content.gasPriceStatus) { + cancelCb('Given gas price is not correct') + } else { + var gasPrice = executionContext.web3().toWei(content.querySelector('#gasprice').value, 'gwei') + continueTxExecution(gasPrice) + } + }}, { + label: 'Cancel', + fn: () => { + return cancelCb('Transaction canceled by user.') + } + }) + }, + function (okCb, cancelCb) { + modalCustom.promptPassphrase(null, 'Personal mode is enabled. Please provide passphrase of account', '', okCb, cancelCb) + }, + runTxCallback) + }) + } else { + if (Object.keys(selectedContract.contract.object.evm.bytecode.linkReferences).length) self._deps.logCallback(`linking ${JSON.stringify(selectedContract.contract.object.evm.bytecode.linkReferences, null, '\t')} using ${JSON.stringify(contractMetadata.linkReferences, null, '\t')}`) + txFormat.encodeConstructorCallAndLinkLibraries(selectedContract.contract.object, args, constructor, contractMetadata.linkReferences, selectedContract.contract.object.evm.bytecode.linkReferences, (error, data) => { + if (error) return self._deps.logCallback(`creation of ${selectedContract.name} errored: ` + error) + createInstanceCallback(selectedContract, data) + }) + } + }) + } + + if (selectedContract.contract.object.evm.deployedBytecode && selectedContract.contract.object.evm.deployedBytecode.object.length / 2 > 24576) { + modalDialog('Contract code size over limit', yo`
Contract creation initialization returns data with length of more than 24576 bytes. The deployment will likely fails.
+ More info: eip-170 +
`, + { + label: 'Force Send', + fn: () => { + forceSend() + }}, { + label: 'Cancel', + fn: () => { + self._deps.logCallback(`creation of ${selectedContract.name} canceled by user.`) + } + }) + } else { + forceSend() + } + } + + // ACCESS DEPLOYED INSTANCE + function loadFromAddress () { + var noInstancesText = self._view.noInstancesText + if (noInstancesText.parentNode) { noInstancesText.parentNode.removeChild(noInstancesText) } + var address = atAddressButtonInput.value + if (!ethJSUtil.isValidAddress(address)) { + return modalDialogCustom.alert('Invalid address.') + } + if (/[a-f]/.test(address) && /[A-F]/.test(address) && !ethJSUtil.isValidChecksumAddress(address)) { + return modalDialogCustom.alert('Invalid checksum address.') + } + if (/.(.abi)$/.exec(self._deps.config.get('currentFile'))) { + modalDialogCustom.confirm(null, 'Do you really want to interact with ' + address + ' using the current ABI definition ?', () => { + var abi + try { + abi = JSON.parse(self._deps.editor.currentContent()) + } catch (e) { + return modalDialogCustom.alert('Failed to parse the current file as JSON ABI.') + } + instanceContainer.appendChild(self._deps.udappUI.renderInstanceFromABI(abi, address, address)) + }) + } else { + var selectedContract = getSelectedContract() + instanceContainer.appendChild(self._deps.udappUI.renderInstance(selectedContract.contract.object, address, selectContractNames.value)) + } + } + + // GET NAMES OF ALL THE CONTRACTS + function getContractNames (success, data, compiler, compilerFullName) { + var contractNames = document.querySelector(`.${css.contractNames.classNames[0]}`) + contractNames.innerHTML = '' + if (success) { + selectContractNames.removeAttribute('disabled') + compiler.visitContracts((contract) => { + contractNames.appendChild(yo``) + }) + } else { + selectContractNames.setAttribute('disabled', true) + } + setInputParamsPlaceHolder() + } + + return el +} + +module.exports = contractDropdown diff --git a/src/app/tabs/runTab/settings.js b/src/app/tabs/runTab/settings.js new file mode 100644 index 0000000000..6c7bb20029 --- /dev/null +++ b/src/app/tabs/runTab/settings.js @@ -0,0 +1,299 @@ +var $ = require('jquery') +var yo = require('yo-yo') +var ethJSUtil = require('ethereumjs-util') +var Personal = require('web3-eth-personal') +var css = require('../styles/run-tab-styles') +var executionContext = require('../../../execution-context') +var copyToClipboard = require('../../ui/copy-to-clipboard') +var modalDialogCustom = require('../../ui/modal-dialog-custom') +var addTooltip = require('../../ui/tooltip') +var modalCustom = require('../../ui/modal-dialog-custom') +var tootip = require('../../ui/tooltip') +var helper = require('../../../lib/helper.js') + +function settings (container, self) { + // VARIABLES + var net = yo`` + var networkcallid = 0 + const updateNetwork = (cb) => { + networkcallid++ + (function (callid) { + executionContext.detectNetwork((err, { id, name } = {}) => { + if (networkcallid > callid) return + networkcallid++ + if (err) { + console.error(err) + net.innerHTML = 'can\'t detect network ' + } else { + net.innerHTML = ` ${name} (${id || '-'})` + } + if (cb) cb(err, {id, name}) + }) + })(networkcallid) + } + var environmentEl = yo` +
+
+ Environment +
+
+ ${net} + + +
+
+ ` + var accountEl = yo` +
+
+ Account + +
+
+ + ${copyToClipboard(() => document.querySelector('#runTabView #txorigin').value)} + +
+
+ ` + var gasPriceEl = yo` +
+
Gas limit
+ +
+ ` + var valueEl = yo` +
+
Value
+ + +
+ ` + // DOM ELEMENT + var el = yo` +
+ ${environmentEl} + ${accountEl} + ${gasPriceEl} + ${valueEl} +
+ ` + // HELPER FUNCTIONS AND EVENTS + self._deps.udapp.event.register('transactionExecuted', (error, from, to, data, lookupOnly, txResult) => { + if (error) return + if (!lookupOnly) el.querySelector('#value').value = '0' + updateAccountBalances(container, self) + }) + + // DROPDOWN + var selectExEnv = environmentEl.querySelector('#selectExEnvOptions') + + function setFinalContext () { + // set the final context. Cause it is possible that this is not the one we've originaly selected + selectExEnv.value = executionContext.getProvider() + self.event.trigger('clearInstance', []) + updateNetwork() + fillAccountsList(el, self) + } + + self.event.register('clearInstance', () => { + var instanceContainer = self._view.instanceContainer + var instanceContainerTitle = self._view.instanceContainerTitle + instanceContainer.innerHTML = '' // clear the instances list + instanceContainer.appendChild(instanceContainerTitle) + instanceContainer.appendChild(self._view.noInstancesText) + }) + + executionContext.event.register('addProvider', (network) => { + selectExEnv.appendChild(yo``) + tootip(`${network.name} [${network.url}] added`) + }) + + executionContext.event.register('removeProvider', (name) => { + var env = selectExEnv.querySelector(`option[value="${name}"]`) + if (env) { + selectExEnv.removeChild(env) + tootip(`${name} removed`) + } + }) + + selectExEnv.addEventListener('change', function (event) { + let context = selectExEnv.options[selectExEnv.selectedIndex].value + executionContext.executionContextChange(context, null, () => { + modalDialogCustom.confirm(null, 'Are you sure you want to connect to an ethereum node?', () => { + modalDialogCustom.prompt(null, 'Web3 Provider Endpoint', 'http://localhost:8545', (target) => { + executionContext.setProviderFromEndpoint(target, context, (alertMsg) => { + if (alertMsg) { + modalDialogCustom.alert(alertMsg) + } + setFinalContext() + }) + }, setFinalContext) + }, setFinalContext) + }, (alertMsg) => { + modalDialogCustom.alert(alertMsg) + }, setFinalContext) + }) + + selectExEnv.value = executionContext.getProvider() + executionContext.event.register('contextChanged', (context, silent) => { + setFinalContext() + }) + + setInterval(() => { + updateNetwork() + fillAccountsList(el, self) + }, 5000) + + setInterval(() => { + updateAccountBalances(container, self) + }, 10000) + + function newAccount () { + self._deps.udapp.newAccount('', + (cb) => { + modalCustom.promptPassphraseCreation((error, passphrase) => { + if (error) { + return modalCustom.alert(error) + } + cb(passphrase) + }, () => {}) + }, + (error, address) => { + if (!error) { + addTooltip(`account ${address} created`) + } else { + addTooltip('Cannot create an account: ' + error) + } + } + ) + } + function signMessage (event) { + self._deps.udapp.getAccounts((err, accounts) => { + if (err) { addTooltip(`Cannot get account list: ${err}`) } + var signMessageDialog = { 'title': 'Sign a message', 'text': 'Enter a message to sign', 'inputvalue': 'Message to sign' } + var $txOrigin = container.querySelector('#txorigin') + var account = $txOrigin.selectedOptions[0].value + var isVM = executionContext.isVM() + var isInjected = executionContext.getProvider() === 'injected' + function alertSignedData (error, hash, signedData) { + if (error && error.message !== '') { + console.log(error) + addTooltip(error.message) + } else { + modalDialogCustom.alert(yo`
hash:${hash}
signature:${signedData}
`) + } + } + if (isVM) { + modalDialogCustom.promptMulti(signMessageDialog, (message) => { + const personalMsg = ethJSUtil.hashPersonalMessage(Buffer.from(message)) + var privKey = self._deps.udapp.accounts[account].privateKey + try { + var rsv = ethJSUtil.ecsign(personalMsg, privKey) + var signedData = ethJSUtil.toRpcSig(rsv.v, rsv.r, rsv.s) + alertSignedData(null, '0x' + personalMsg.toString('hex'), signedData) + } catch (e) { + addTooltip(e.message) + return + } + }, false) + } else if (isInjected) { + modalDialogCustom.promptMulti(signMessageDialog, (message) => { + const hashedMsg = executionContext.web3().sha3(message) + try { + executionContext.web3().eth.sign(account, hashedMsg, (error, signedData) => { + alertSignedData(error, hashedMsg, signedData) + }) + } catch (e) { + addTooltip(e.message) + console.log(e) + return + } + }) + } else { + modalDialogCustom.promptPassphrase('Passphrase to sign a message', 'Enter your passphrase for this account to sign the message', '', (passphrase) => { + modalDialogCustom.promptMulti(signMessageDialog, (message) => { + const hashedMsg = executionContext.web3().sha3(message) + try { + var personal = new Personal(executionContext.web3().currentProvider) + personal.sign(hashedMsg, account, passphrase, (error, signedData) => { + alertSignedData(error, hashedMsg, signedData) + }) + } catch (e) { + addTooltip(e.message) + console.log(e) + return + } + }) + }, false) + } + }) + } + + return el +} + +var accountListCallId = 0 +var loadedAccounts = {} +function fillAccountsList (container, self) { + accountListCallId++ + (function (callid) { + var txOrigin = container.querySelector('#txorigin') + self._deps.udapp.getAccounts((err, accounts) => { + if (accountListCallId > callid) return + accountListCallId++ + if (err) { addTooltip(`Cannot get account list: ${err}`) } + for (var loadedaddress in loadedAccounts) { + if (accounts.indexOf(loadedaddress) === -1) { + txOrigin.removeChild(txOrigin.querySelector('option[value="' + loadedaddress + '"]')) + delete loadedAccounts[loadedaddress] + } + } + for (var i in accounts) { + var address = accounts[i] + if (!loadedAccounts[address]) { + txOrigin.appendChild(yo``) + loadedAccounts[address] = 1 + } + } + txOrigin.setAttribute('value', accounts[0]) + }) + })(accountListCallId) +} + +function updateAccountBalances (container, self) { + var accounts = $(container.querySelector('#txorigin')).children('option') + accounts.each(function (index, value) { + (function (acc) { + self._deps.udapp.getBalanceInEther(accounts[acc].value, function (err, res) { + if (!err) { + accounts[acc].innerText = helper.shortenAddress(accounts[acc].value, res) + } + }) + })(index) + }) +} + +module.exports = settings From f3681345f96ed999915cd722db07a96d2175dbea Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Wed, 26 Dec 2018 16:32:55 -0500 Subject: [PATCH 23/50] refactor settings into a class; add render method --- src/app/tabs/run-tab.js | 5 +- src/app/tabs/runTab/settings.js | 326 +++++++++++++++++--------------- 2 files changed, 176 insertions(+), 155 deletions(-) diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index a7c1c5a9d1..c1329739fe 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -11,7 +11,7 @@ var Card = require('../ui/card') var Recorder = require('../../recorder') var css = require('./styles/run-tab-styles') -var settingsUI = require('./runTab/settings.js') +var SettingsUI = require('./runTab/settings.js') var contractDropdownUI = require('./runTab/contractDropdown.js') function runTab (opts, localRegistry) { @@ -128,9 +128,10 @@ function runTab (opts, localRegistry) { /* ------------------------- MAIN HTML ELEMENT --------------------------- */ + var settingsUI = new SettingsUI(container, self) var el = yo`
- ${settingsUI(container, self)} + ${settingsUI.render()} ${contractDropdownUI(self.event, self)} ${recorderCard.render()} ${self._view.instanceContainer} diff --git a/src/app/tabs/runTab/settings.js b/src/app/tabs/runTab/settings.js index 6c7bb20029..9ae219d513 100644 --- a/src/app/tabs/runTab/settings.js +++ b/src/app/tabs/runTab/settings.js @@ -11,168 +11,167 @@ var modalCustom = require('../../ui/modal-dialog-custom') var tootip = require('../../ui/tooltip') var helper = require('../../../lib/helper.js') -function settings (container, self) { - // VARIABLES - var net = yo`` - var networkcallid = 0 - const updateNetwork = (cb) => { - networkcallid++ - (function (callid) { - executionContext.detectNetwork((err, { id, name } = {}) => { - if (networkcallid > callid) return - networkcallid++ - if (err) { - console.error(err) - net.innerHTML = 'can\'t detect network ' - } else { - net.innerHTML = ` ${name} (${id || '-'})` - } - if (cb) cb(err, {id, name}) - }) - })(networkcallid) +class SettingsUI { + + constructor (container, parentSelf) { + this.container = container + this.parentSelf = parentSelf + // HELPER FUNCTIONS AND EVENTS + this.parentSelf._deps.udapp.event.register('transactionExecuted', (error, from, to, data, lookupOnly, txResult) => { + if (error) return + if (!lookupOnly) this.el.querySelector('#value').value = '0' + updateAccountBalances(this.container, this.parentSelf) + }) } - var environmentEl = yo` -
-
- Environment + + render () { + this.netUI = yo`` + + var environmentEl = yo` +
+
+ Environment +
+
+ ${this.netUI} + + +
-
- ${net} - - + ` + + var accountEl = yo` +
+
+ Account + +
+
+ + ${copyToClipboard(() => document.querySelector('#runTabView #txorigin').value)} + +
-
- ` - var accountEl = yo` -
-
- Account - + ` + + var gasPriceEl = yo` +
+
Gas limit
+
-
- - ${copyToClipboard(() => document.querySelector('#runTabView #txorigin').value)} - + ` + + var valueEl = yo` +
+
Value
+ +
-
- ` - var gasPriceEl = yo` -
-
Gas limit
- -
- ` - var valueEl = yo` -
-
Value
- - -
- ` - // DOM ELEMENT - var el = yo` -
- ${environmentEl} - ${accountEl} - ${gasPriceEl} - ${valueEl} -
- ` - // HELPER FUNCTIONS AND EVENTS - self._deps.udapp.event.register('transactionExecuted', (error, from, to, data, lookupOnly, txResult) => { - if (error) return - if (!lookupOnly) el.querySelector('#value').value = '0' - updateAccountBalances(container, self) - }) + ` - // DROPDOWN - var selectExEnv = environmentEl.querySelector('#selectExEnvOptions') + // DOM ELEMENT + var el = yo` +
+ ${environmentEl} + ${accountEl} + ${gasPriceEl} + ${valueEl} +
+ ` - function setFinalContext () { - // set the final context. Cause it is possible that this is not the one we've originaly selected - selectExEnv.value = executionContext.getProvider() - self.event.trigger('clearInstance', []) - updateNetwork() - fillAccountsList(el, self) - } + // DROPDOWN + var selectExEnv = environmentEl.querySelector('#selectExEnvOptions') + this.selectExEnv = selectExEnv - self.event.register('clearInstance', () => { - var instanceContainer = self._view.instanceContainer - var instanceContainerTitle = self._view.instanceContainerTitle - instanceContainer.innerHTML = '' // clear the instances list - instanceContainer.appendChild(instanceContainerTitle) - instanceContainer.appendChild(self._view.noInstancesText) - }) + this.parentSelf.event.register('clearInstance', () => { + var instanceContainer = this.parentSelf._view.instanceContainer + var instanceContainerTitle = this.parentSelf._view.instanceContainerTitle + instanceContainer.innerHTML = '' // clear the instances list + instanceContainer.appendChild(instanceContainerTitle) + instanceContainer.appendChild(this.parentSelf._view.noInstancesText) + }) - executionContext.event.register('addProvider', (network) => { - selectExEnv.appendChild(yo``) - tootip(`${network.name} [${network.url}] added`) - }) + executionContext.event.register('addProvider', (network) => { + selectExEnv.appendChild(yo``) + tootip(`${network.name} [${network.url}] added`) + }) - executionContext.event.register('removeProvider', (name) => { - var env = selectExEnv.querySelector(`option[value="${name}"]`) - if (env) { - selectExEnv.removeChild(env) - tootip(`${name} removed`) - } - }) + executionContext.event.register('removeProvider', (name) => { + var env = selectExEnv.querySelector(`option[value="${name}"]`) + if (env) { + selectExEnv.removeChild(env) + tootip(`${name} removed`) + } + }) - selectExEnv.addEventListener('change', function (event) { - let context = selectExEnv.options[selectExEnv.selectedIndex].value - executionContext.executionContextChange(context, null, () => { - modalDialogCustom.confirm(null, 'Are you sure you want to connect to an ethereum node?', () => { - modalDialogCustom.prompt(null, 'Web3 Provider Endpoint', 'http://localhost:8545', (target) => { - executionContext.setProviderFromEndpoint(target, context, (alertMsg) => { - if (alertMsg) { - modalDialogCustom.alert(alertMsg) - } - setFinalContext() - }) - }, setFinalContext) - }, setFinalContext) - }, (alertMsg) => { - modalDialogCustom.alert(alertMsg) - }, setFinalContext) - }) + selectExEnv.addEventListener('change', (event) => { + let context = selectExEnv.options[selectExEnv.selectedIndex].value + executionContext.executionContextChange(context, null, () => { + modalDialogCustom.confirm(null, 'Are you sure you want to connect to an ethereum node?', () => { + modalDialogCustom.prompt(null, 'Web3 Provider Endpoint', 'http://localhost:8545', (target) => { + executionContext.setProviderFromEndpoint(target, context, (alertMsg) => { + if (alertMsg) { + modalDialogCustom.alert(alertMsg) + } + this.setFinalContext() + }) + }, this.setFinalContext.bind(this)) + }, this.setFinalContext.bind(this)) + }, (alertMsg) => { + modalDialogCustom.alert(alertMsg) + }, this.setFinalContext.bind(this)) + }) - selectExEnv.value = executionContext.getProvider() - executionContext.event.register('contextChanged', (context, silent) => { - setFinalContext() - }) + selectExEnv.value = executionContext.getProvider() + executionContext.event.register('contextChanged', (context, silent) => { + this.setFinalContext() + }) + + setInterval(() => { + this.updateNetwork() + fillAccountsList(el, this.parentSelf) + }, 5000) + + setInterval(() => { + updateAccountBalances(this.container, this.parentSelf) + }, 10000) - setInterval(() => { - updateNetwork() - fillAccountsList(el, self) - }, 5000) + this.el = el + return el + } - setInterval(() => { - updateAccountBalances(container, self) - }, 10000) + setFinalContext () { + // set the final context. Cause it is possible that this is not the one we've originaly selected + this.selectExEnv.value = executionContext.getProvider() + this.parentSelf.event.trigger('clearInstance', []) + this.updateNetwork() + fillAccountsList(this.el, this.parentSelf) + } - function newAccount () { - self._deps.udapp.newAccount('', + newAccount () { + this.parentSelf._deps.udapp.newAccount('', (cb) => { modalCustom.promptPassphraseCreation((error, passphrase) => { if (error) { @@ -190,11 +189,12 @@ function settings (container, self) { } ) } - function signMessage (event) { - self._deps.udapp.getAccounts((err, accounts) => { + + signMessage (event) { + this.parentSelf._deps.udapp.getAccounts((err, accounts) => { if (err) { addTooltip(`Cannot get account list: ${err}`) } var signMessageDialog = { 'title': 'Sign a message', 'text': 'Enter a message to sign', 'inputvalue': 'Message to sign' } - var $txOrigin = container.querySelector('#txorigin') + var $txOrigin = this.container.querySelector('#txorigin') var account = $txOrigin.selectedOptions[0].value var isVM = executionContext.isVM() var isInjected = executionContext.getProvider() === 'injected' @@ -209,7 +209,7 @@ function settings (container, self) { if (isVM) { modalDialogCustom.promptMulti(signMessageDialog, (message) => { const personalMsg = ethJSUtil.hashPersonalMessage(Buffer.from(message)) - var privKey = self._deps.udapp.accounts[account].privateKey + var privKey = this.parentSelf._deps.udapp.accounts[account].privateKey try { var rsv = ethJSUtil.ecsign(personalMsg, privKey) var signedData = ethJSUtil.toRpcSig(rsv.v, rsv.r, rsv.s) @@ -252,7 +252,27 @@ function settings (container, self) { }) } - return el + // TODO: cb param doesn't seem to be used + updateNetwork (cb) { + let self = this + var networkcallid = 0 + networkcallid++ + (function (callid) { + executionContext.detectNetwork((err, { id, name } = {}) => { + if (networkcallid > callid) return + networkcallid++ + if (err) { + console.error(err) + self.netUI.innerHTML = 'can\'t detect network ' + } else { + self.netUI.innerHTML = ` ${name} (${id || '-'})` + } + // TODO: cb param doesn't seem to be used + if (cb) cb(err, {id, name}) + }) + })(networkcallid) + } + } var accountListCallId = 0 @@ -296,4 +316,4 @@ function updateAccountBalances (container, self) { }) } -module.exports = settings +module.exports = SettingsUI From 6a8c58cd8a9322036eed8f260e50b057dd6138d4 Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Wed, 26 Dec 2018 16:56:01 -0500 Subject: [PATCH 24/50] refactor contract dropdown into a class; add render method --- src/app/tabs/run-tab.js | 5 +- src/app/tabs/runTab/contractDropdown.js | 256 ++++++++++++------------ 2 files changed, 134 insertions(+), 127 deletions(-) diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index c1329739fe..7cb8b9f71d 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -12,7 +12,7 @@ var Recorder = require('../../recorder') var css = require('./styles/run-tab-styles') var SettingsUI = require('./runTab/settings.js') -var contractDropdownUI = require('./runTab/contractDropdown.js') +var ContractDropdownUI = require('./runTab/contractDropdown.js') function runTab (opts, localRegistry) { /* ------------------------- @@ -129,10 +129,11 @@ function runTab (opts, localRegistry) { MAIN HTML ELEMENT --------------------------- */ var settingsUI = new SettingsUI(container, self) + var contractDropdownUI = new ContractDropdownUI(self) var el = yo`
${settingsUI.render()} - ${contractDropdownUI(self.event, self)} + ${contractDropdownUI.render()} ${recorderCard.render()} ${self._view.instanceContainer}
diff --git a/src/app/tabs/runTab/contractDropdown.js b/src/app/tabs/runTab/contractDropdown.js index e3c23cad13..6e9fd461a4 100644 --- a/src/app/tabs/runTab/contractDropdown.js +++ b/src/app/tabs/runTab/contractDropdown.js @@ -14,130 +14,137 @@ var confirmDialog = require('../../execution/confirmDialog') var modalDialog = require('../../ui/modaldialog') var MultiParamManager = require('../../../multiParamManager') -function contractDropdown (events, self) { - var instanceContainer = self._view.instanceContainer - var instanceContainerTitle = self._view.instanceContainerTitle - instanceContainer.appendChild(instanceContainerTitle) - instanceContainer.appendChild(self._view.noInstancesText) - var compFails = yo`` - var info = yo`` - - var newlyCompiled = (success, data, source, compiler, compilerFullName) => { - getContractNames(success, data, compiler, compilerFullName) - if (success) { - compFails.style.display = 'none' - document.querySelector(`.${css.contractNames}`).classList.remove(css.contractNamesError) - } else { - compFails.style.display = 'block' - document.querySelector(`.${css.contractNames}`).classList.add(css.contractNamesError) - } +class ContractDropdownUI { + constructor (parentSelf) { + this.parentSelf = parentSelf } - self._deps.pluginManager.event.register('sendCompilationResult', (file, source, languageVersion, data) => { - // TODO check whether the tab is configured - let compiler = new CompilerAbstract(languageVersion, data) - self._deps.compilersArtefacts[languageVersion] = compiler - self._deps.compilersArtefacts['__last'] = compiler - newlyCompiled(true, data, source, compiler, languageVersion) - }) - - self._deps.compiler.event.register('compilationFinished', (success, data, source) => { - var name = 'solidity' - let compiler = new CompilerAbstract(name, data) - self._deps.compilersArtefacts[name] = compiler - self._deps.compilersArtefacts['__last'] = compiler - newlyCompiled(success, data, source, self._deps.compiler, name) - }) + render () { + this.instanceContainer = this.parentSelf._view.instanceContainer + var instanceContainerTitle = this.parentSelf._view.instanceContainerTitle + this.instanceContainer.appendChild(instanceContainerTitle) + this.instanceContainer.appendChild(this.parentSelf._view.noInstancesText) + var compFails = yo`` + var info = yo`` - var deployAction = (value) => { - self._view.createPanel.style.display = value - self._view.orLabel.style.display = value - } - - self._deps.fileManager.event.register('currentFileChanged', (currentFile) => { - document.querySelector(`.${css.contractNames}`).classList.remove(css.contractNamesError) - var contractNames = document.querySelector(`.${css.contractNames.classNames[0]}`) - contractNames.innerHTML = '' - if (/.(.abi)$/.exec(currentFile)) { - deployAction('none') - compFails.style.display = 'none' - contractNames.appendChild(yo``) - selectContractNames.setAttribute('disabled', true) - } else if (/.(.sol)$/.exec(currentFile)) { - deployAction('block') + var newlyCompiled = (success, data, source, compiler, compilerFullName) => { + this.getContractNames(success, data, compiler, compilerFullName) + if (success) { + compFails.style.display = 'none' + document.querySelector(`.${css.contractNames}`).classList.remove(css.contractNamesError) + } else { + compFails.style.display = 'block' + document.querySelector(`.${css.contractNames}`).classList.add(css.contractNamesError) + } } - }) - var atAddressButtonInput = yo`` - var selectContractNames = yo`` + this.parentSelf._deps.pluginManager.event.register('sendCompilationResult', (file, source, languageVersion, data) => { + // TODO check whether the tab is configured + let compiler = new CompilerAbstract(languageVersion, data) + this.parentSelf._deps.compilersArtefacts[languageVersion] = compiler + this.parentSelf._deps.compilersArtefacts['__last'] = compiler + newlyCompiled(true, data, source, compiler, languageVersion) + }) - function getSelectedContract () { - var contract = selectContractNames.children[selectContractNames.selectedIndex] - var contractName = contract.innerHTML - var compiler = self._deps.compilersArtefacts[contract.getAttribute('compiler')] - if (!compiler) return null + this.parentSelf._deps.compiler.event.register('compilationFinished', (success, data, source) => { + var name = 'solidity' + let compiler = new CompilerAbstract(name, data) + this.parentSelf._deps.compilersArtefacts[name] = compiler + this.parentSelf._deps.compilersArtefacts['__last'] = compiler + newlyCompiled(success, data, source, this.parentSelf._deps.compiler, name) + }) - if (contractName) { - return { - name: contractName, - contract: compiler.getContract(contractName), - compiler - } + var deployAction = (value) => { + this.parentSelf._view.createPanel.style.display = value + this.parentSelf._view.orLabel.style.display = value } - return null - } - self._view.createPanel = yo`
` - self._view.orLabel = yo`
or
` - var el = yo` -
-
- ${selectContractNames} ${compFails} ${info} -
-
- ${self._view.createPanel} - ${self._view.orLabel} -
-
At Address
- ${atAddressButtonInput} + this.parentSelf._deps.fileManager.event.register('currentFileChanged', (currentFile) => { + document.querySelector(`.${css.contractNames}`).classList.remove(css.contractNamesError) + var contractNames = document.querySelector(`.${css.contractNames.classNames[0]}`) + contractNames.innerHTML = '' + if (/.(.abi)$/.exec(currentFile)) { + deployAction('none') + compFails.style.display = 'none' + contractNames.appendChild(yo``) + this.selectContractNames.setAttribute('disabled', true) + } else if (/.(.sol)$/.exec(currentFile)) { + deployAction('block') + } + }) + + this.atAddressButtonInput = yo`` + this.selectContractNames = yo`` + + this.parentSelf._view.createPanel = yo`
` + this.parentSelf._view.orLabel = yo`
or
` + var el = yo` +
+
+ ${this.selectContractNames} ${compFails} ${info} +
+
+ ${this.parentSelf._view.createPanel} + ${this.parentSelf._view.orLabel} +
+
At Address
+ ${this.atAddressButtonInput} +
-
- ` + ` + this.selectContractNames.addEventListener('change', this.setInputParamsPlaceHolder.bind(this)) + + return el + } - function setInputParamsPlaceHolder () { - self._view.createPanel.innerHTML = '' - if (selectContractNames.selectedIndex >= 0 && selectContractNames.children.length > 0) { - var selectedContract = getSelectedContract() + setInputParamsPlaceHolder () { + this.parentSelf._view.createPanel.innerHTML = '' + if (this.selectContractNames.selectedIndex >= 0 && this.selectContractNames.children.length > 0) { + var selectedContract = this.getSelectedContract() var ctrabi = txHelper.getConstructorInterface(selectedContract.contract.object.abi) var ctrEVMbc = selectedContract.contract.object.evm.bytecode.object var createConstructorInstance = new MultiParamManager(0, ctrabi, (valArray, inputsValues) => { - createInstance(inputsValues, selectedContract.compiler) + this.createInstance(inputsValues, selectedContract.compiler) }, txHelper.inputParametersDeclarationToString(ctrabi.inputs), 'Deploy', ctrEVMbc) - self._view.createPanel.appendChild(createConstructorInstance.render()) + this.parentSelf._view.createPanel.appendChild(createConstructorInstance.render()) return } else { - self._view.createPanel.innerHTML = 'No compiled contracts' + this.parentSelf._view.createPanel.innerHTML = 'No compiled contracts' } } - selectContractNames.addEventListener('change', setInputParamsPlaceHolder) + getSelectedContract () { + var contract = this.selectContractNames.children[this.selectContractNames.selectedIndex] + var contractName = contract.innerHTML + var compiler = this.parentSelf._deps.compilersArtefacts[contract.getAttribute('compiler')] + if (!compiler) return null + + if (contractName) { + return { + name: contractName, + contract: compiler.getContract(contractName), + compiler + } + } + return null + } - function createInstanceCallback (selectedContract, data) { - self._deps.logCallback(`creation of ${selectedContract.name} pending...`) + createInstanceCallback (selectedContract, data) { + this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} pending...`) if (data) { data.contractName = selectedContract.name data.linkReferences = selectedContract.contract.object.evm.bytecode.linkReferences data.contractABI = selectedContract.contract.object.abi } - self._deps.udapp.createContract(data, + this.parentSelf._deps.udapp.createContract(data, (network, tx, gasEstimation, continueTxExecution, cancelCb) => { if (network.name !== 'Main') { return continueTxExecution(null) } var amount = executionContext.web3().fromWei(typeConversion.toInt(tx.value), 'ether') - var content = confirmDialog(tx, amount, gasEstimation, self, + var content = confirmDialog(tx, amount, gasEstimation, this.parentSelf, (gasPrice, cb) => { let txFeeText, priceStatus // TODO: this try catch feels like an anti pattern, can/should be @@ -170,7 +177,7 @@ function contractDropdown (events, self) { modalDialog('Confirm transaction', content, { label: 'Confirm', fn: () => { - self._deps.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked) + this.parentSelf._deps.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked) // TODO: check if this is check is still valid given the refactor if (!content.gasPriceStatus) { cancelCb('Given gas price is not correct') @@ -215,28 +222,28 @@ function contractDropdown (events, self) { if (isVM) { var vmError = txExecution.checkVMError(txResult) if (vmError.error) { - self._deps.logCallback(vmError.message) + this.parentSelf._deps.logCallback(vmError.message) return } } if (txResult.result.status && txResult.result.status === '0x0') { - self._deps.logCallback(`creation of ${selectedContract.name} errored: transaction execution failed`) + this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} errored: transaction execution failed`) return } - var noInstancesText = self._view.noInstancesText + var noInstancesText = this.parentSelf._view.noInstancesText if (noInstancesText.parentNode) { noInstancesText.parentNode.removeChild(noInstancesText) } var address = isVM ? txResult.result.createdAddress : txResult.result.contractAddress - instanceContainer.appendChild(self._deps.udappUI.renderInstance(selectedContract.contract.object, address, selectContractNames.value)) + this.instanceContainer.appendChild(this.parentSelf._deps.udappUI.renderInstance(selectedContract.contract.object, address, this.selectContractNames.value)) } else { - self._deps.logCallback(`creation of ${selectedContract.name} errored: ${error}`) + this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} errored: ${error}`) } } ) } // DEPLOY INSTANCE - function createInstance (args, compiler) { - var selectedContract = getSelectedContract() + createInstance (args, compiler) { + var selectedContract = this.getSelectedContract() if (selectedContract.contract.object.evm.bytecode.object.length === 0) { modalDialogCustom.alert('This contract may be abstract, not implement an abstract parent\'s methods completely or not invoke an inherited contract\'s constructor correctly.') @@ -245,23 +252,23 @@ function contractDropdown (events, self) { var forceSend = () => { var constructor = txHelper.getConstructorInterface(selectedContract.contract.object.abi) - self._deps.filePanel.compilerMetadata().deployMetadataOf(selectedContract.name, (error, contractMetadata) => { - if (error) return self._deps.logCallback(`creation of ${selectedContract.name} errored: ` + error) + this.parentSelf._deps.filePanel.compilerMetadata().deployMetadataOf(selectedContract.name, (error, contractMetadata) => { + if (error) return this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} errored: ` + error) if (!contractMetadata || (contractMetadata && contractMetadata.autoDeployLib)) { txFormat.buildData(selectedContract.name, selectedContract.contract.object, compiler.getContracts(), true, constructor, args, (error, data) => { - if (error) return self._deps.logCallback(`creation of ${selectedContract.name} errored: ` + error) - createInstanceCallback(selectedContract, data) + if (error) return this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} errored: ` + error) + this.createInstanceCallback(selectedContract, data) }, (msg) => { - self._deps.logCallback(msg) + this.parentSelf._deps.logCallback(msg) }, (data, runTxCallback) => { // called for libraries deployment - self._deps.udapp.runTx(data, + this.parentSelf._deps.udapp.runTx(data, (network, tx, gasEstimation, continueTxExecution, cancelCb) => { if (network.name !== 'Main') { return continueTxExecution(null) } var amount = executionContext.web3().fromWei(typeConversion.toInt(tx.value), 'ether') - var content = confirmDialog(tx, amount, gasEstimation, self, + var content = confirmDialog(tx, amount, gasEstimation, this.parentSelf, (gasPrice, cb) => { let txFeeText, priceStatus // TODO: this try catch feels like an anti pattern, can/should be @@ -294,7 +301,7 @@ function contractDropdown (events, self) { modalDialog('Confirm transaction', content, { label: 'Confirm', fn: () => { - self._deps.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked) + this.parentSelf._deps.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked) // TODO: check if this is check is still valid given the refactor if (!content.gasPriceStatus) { cancelCb('Given gas price is not correct') @@ -315,10 +322,10 @@ function contractDropdown (events, self) { runTxCallback) }) } else { - if (Object.keys(selectedContract.contract.object.evm.bytecode.linkReferences).length) self._deps.logCallback(`linking ${JSON.stringify(selectedContract.contract.object.evm.bytecode.linkReferences, null, '\t')} using ${JSON.stringify(contractMetadata.linkReferences, null, '\t')}`) + if (Object.keys(selectedContract.contract.object.evm.bytecode.linkReferences).length) this.parentSelf._deps.logCallback(`linking ${JSON.stringify(selectedContract.contract.object.evm.bytecode.linkReferences, null, '\t')} using ${JSON.stringify(contractMetadata.linkReferences, null, '\t')}`) txFormat.encodeConstructorCallAndLinkLibraries(selectedContract.contract.object, args, constructor, contractMetadata.linkReferences, selectedContract.contract.object.evm.bytecode.linkReferences, (error, data) => { - if (error) return self._deps.logCallback(`creation of ${selectedContract.name} errored: ` + error) - createInstanceCallback(selectedContract, data) + if (error) return this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} errored: ` + error) + this.createInstanceCallback(selectedContract, data) }) } }) @@ -335,7 +342,7 @@ function contractDropdown (events, self) { }}, { label: 'Cancel', fn: () => { - self._deps.logCallback(`creation of ${selectedContract.name} canceled by user.`) + this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} canceled by user.`) } }) } else { @@ -344,48 +351,47 @@ function contractDropdown (events, self) { } // ACCESS DEPLOYED INSTANCE - function loadFromAddress () { - var noInstancesText = self._view.noInstancesText + loadFromAddress () { + var noInstancesText = this.parentSelf._view.noInstancesText if (noInstancesText.parentNode) { noInstancesText.parentNode.removeChild(noInstancesText) } - var address = atAddressButtonInput.value + var address = this.atAddressButtonInput.value if (!ethJSUtil.isValidAddress(address)) { return modalDialogCustom.alert('Invalid address.') } if (/[a-f]/.test(address) && /[A-F]/.test(address) && !ethJSUtil.isValidChecksumAddress(address)) { return modalDialogCustom.alert('Invalid checksum address.') } - if (/.(.abi)$/.exec(self._deps.config.get('currentFile'))) { + if (/.(.abi)$/.exec(this.parentSelf._deps.config.get('currentFile'))) { modalDialogCustom.confirm(null, 'Do you really want to interact with ' + address + ' using the current ABI definition ?', () => { var abi try { - abi = JSON.parse(self._deps.editor.currentContent()) + abi = JSON.parse(this.parentSelf._deps.editor.currentContent()) } catch (e) { return modalDialogCustom.alert('Failed to parse the current file as JSON ABI.') } - instanceContainer.appendChild(self._deps.udappUI.renderInstanceFromABI(abi, address, address)) + this.instanceContainer.appendChild(this.parentSelf._deps.udappUI.renderInstanceFromABI(abi, address, address)) }) } else { - var selectedContract = getSelectedContract() - instanceContainer.appendChild(self._deps.udappUI.renderInstance(selectedContract.contract.object, address, selectContractNames.value)) + var selectedContract = this.getSelectedContract() + this.instanceContainer.appendChild(this.parentSelf._deps.udappUI.renderInstance(selectedContract.contract.object, address, this.selectContractNames.value)) } } // GET NAMES OF ALL THE CONTRACTS - function getContractNames (success, data, compiler, compilerFullName) { + getContractNames (success, data, compiler, compilerFullName) { var contractNames = document.querySelector(`.${css.contractNames.classNames[0]}`) contractNames.innerHTML = '' if (success) { - selectContractNames.removeAttribute('disabled') + this.selectContractNames.removeAttribute('disabled') compiler.visitContracts((contract) => { contractNames.appendChild(yo``) }) } else { - selectContractNames.setAttribute('disabled', true) + this.selectContractNames.setAttribute('disabled', true) } - setInputParamsPlaceHolder() + this.setInputParamsPlaceHolder() } - return el } -module.exports = contractDropdown +module.exports = ContractDropdownUI From 874eb2993f6440cd7a9403dd5c304532ebf518ff Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Wed, 26 Dec 2018 17:05:54 -0500 Subject: [PATCH 25/50] cleanup settings --- src/app/tabs/runTab/settings.js | 50 +++++++++++++++------------------ 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/src/app/tabs/runTab/settings.js b/src/app/tabs/runTab/settings.js index 9ae219d513..f51c992904 100644 --- a/src/app/tabs/runTab/settings.js +++ b/src/app/tabs/runTab/settings.js @@ -181,15 +181,23 @@ class SettingsUI { }, () => {}) }, (error, address) => { - if (!error) { - addTooltip(`account ${address} created`) - } else { - addTooltip('Cannot create an account: ' + error) + if (error) { + return addTooltip('Cannot create an account: ' + error) } + addTooltip(`account ${address} created`) } ) } + alertSignedData (error, hash, signedData) { + if (error && error.message !== '') { + console.log(error) + addTooltip(error.message) + } else { + modalDialogCustom.alert(yo`
hash:${hash}
signature:${signedData}
`) + } + } + signMessage (event) { this.parentSelf._deps.udapp.getAccounts((err, accounts) => { if (err) { addTooltip(`Cannot get account list: ${err}`) } @@ -198,14 +206,6 @@ class SettingsUI { var account = $txOrigin.selectedOptions[0].value var isVM = executionContext.isVM() var isInjected = executionContext.getProvider() === 'injected' - function alertSignedData (error, hash, signedData) { - if (error && error.message !== '') { - console.log(error) - addTooltip(error.message) - } else { - modalDialogCustom.alert(yo`
hash:${hash}
signature:${signedData}
`) - } - } if (isVM) { modalDialogCustom.promptMulti(signMessageDialog, (message) => { const personalMsg = ethJSUtil.hashPersonalMessage(Buffer.from(message)) @@ -213,7 +213,7 @@ class SettingsUI { try { var rsv = ethJSUtil.ecsign(personalMsg, privKey) var signedData = ethJSUtil.toRpcSig(rsv.v, rsv.r, rsv.s) - alertSignedData(null, '0x' + personalMsg.toString('hex'), signedData) + this.alertSignedData(null, '0x' + personalMsg.toString('hex'), signedData) } catch (e) { addTooltip(e.message) return @@ -224,7 +224,7 @@ class SettingsUI { const hashedMsg = executionContext.web3().sha3(message) try { executionContext.web3().eth.sign(account, hashedMsg, (error, signedData) => { - alertSignedData(error, hashedMsg, signedData) + this.alertSignedData(error, hashedMsg, signedData) }) } catch (e) { addTooltip(e.message) @@ -239,7 +239,7 @@ class SettingsUI { try { var personal = new Personal(executionContext.web3().currentProvider) personal.sign(hashedMsg, account, passphrase, (error, signedData) => { - alertSignedData(error, hashedMsg, signedData) + this.alertSignedData(error, hashedMsg, signedData) }) } catch (e) { addTooltip(e.message) @@ -252,12 +252,11 @@ class SettingsUI { }) } - // TODO: cb param doesn't seem to be used - updateNetwork (cb) { + updateNetwork () { let self = this var networkcallid = 0 networkcallid++ - (function (callid) { + ((callid) => { executionContext.detectNetwork((err, { id, name } = {}) => { if (networkcallid > callid) return networkcallid++ @@ -267,8 +266,6 @@ class SettingsUI { } else { self.netUI.innerHTML = ` ${name} (${id || '-'})` } - // TODO: cb param doesn't seem to be used - if (cb) cb(err, {id, name}) }) })(networkcallid) } @@ -279,7 +276,7 @@ var accountListCallId = 0 var loadedAccounts = {} function fillAccountsList (container, self) { accountListCallId++ - (function (callid) { + ((callid) => { var txOrigin = container.querySelector('#txorigin') self._deps.udapp.getAccounts((err, accounts) => { if (accountListCallId > callid) return @@ -305,12 +302,11 @@ function fillAccountsList (container, self) { function updateAccountBalances (container, self) { var accounts = $(container.querySelector('#txorigin')).children('option') - accounts.each(function (index, value) { - (function (acc) { - self._deps.udapp.getBalanceInEther(accounts[acc].value, function (err, res) { - if (!err) { - accounts[acc].innerText = helper.shortenAddress(accounts[acc].value, res) - } + accounts.each((index, value) => { + ((acc) => { + self._deps.udapp.getBalanceInEther(accounts[acc].value, (err, res) => { + if (err) return + accounts[acc].innerText = helper.shortenAddress(accounts[acc].value, res) }) })(index) }) From 354111860b16813690372c7a3e834ff47c503119 Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Wed, 26 Dec 2018 17:29:40 -0500 Subject: [PATCH 26/50] move recorder UI to its own module --- src/app/tabs/run-tab.js | 103 ++------------------------------ src/app/tabs/runTab/recorder.js | 102 +++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+), 99 deletions(-) create mode 100644 src/app/tabs/runTab/recorder.js diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index 7cb8b9f71d..be3078542d 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -1,18 +1,15 @@ 'use strict' var $ = require('jquery') var yo = require('yo-yo') -var csjs = require('csjs-inject') var EventManager = require('../../lib/events') var globlalRegistry = require('../../global/registry') -var helper = require('../../lib/helper.js') var executionContext = require('../../execution-context') -var modalDialogCustom = require('../ui/modal-dialog-custom') var Card = require('../ui/card') -var Recorder = require('../../recorder') var css = require('./styles/run-tab-styles') var SettingsUI = require('./runTab/settings.js') var ContractDropdownUI = require('./runTab/contractDropdown.js') +var RecorderUI = require('./runTab/recorder.js') function runTab (opts, localRegistry) { /* ------------------------- @@ -90,7 +87,9 @@ function runTab (opts, localRegistry) {
` var container = yo`
` - var recorderInterface = makeRecorder(localRegistry, self.event, self) + + var recorderInterface = new RecorderUI(self.event, self) + recorderInterface.render() self._view.collapsedView = yo`
@@ -143,98 +142,4 @@ function runTab (opts, localRegistry) { return { render () { return container } } } -/* ------------------------------------------------ - RECORDER ------------------------------------------------- */ -function makeRecorder (registry, runTabEvent, self) { - var recorder = new Recorder(self._deps.udapp, self._deps.logCallback) - - recorder.event.register('newTxRecorded', (count) => { - self.data.count = count - self._view.recorderCount.innerText = count - }) - recorder.event.register('cleared', () => { - self.data.count = 0 - self._view.recorderCount.innerText = 0 - }) - - executionContext.event.register('contextChanged', () => { - recorder.clearAll() - }) - - runTabEvent.register('clearInstance', () => { - recorder.clearAll() - }) - - var css2 = csjs` - .container {} - .runTxs {} - .recorder {} - ` - - var runButton = yo`` - var recordButton = yo` - ` - - function triggerRecordButton () { - var txJSON = JSON.stringify(recorder.getAll(), null, 2) - var fileManager = self._deps.fileManager - var path = fileManager.currentPath() - modalDialogCustom.prompt(null, 'Transactions will be saved in a file under ' + path, 'scenario.json', input => { - var fileProvider = fileManager.fileProviderOf(path) - if (fileProvider) { - var newFile = path + '/' + input - helper.createNonClashingName(newFile, fileProvider, (error, newFile) => { - if (error) return modalDialogCustom.alert('Failed to create file. ' + newFile + ' ' + error) - if (!fileProvider.set(newFile, txJSON)) { - modalDialogCustom.alert('Failed to create file ' + newFile) - } else { - fileManager.switchFile(newFile) - } - }) - } - }) - } - - runButton.onclick = () => { - /* - @TODO - update account address in scenario.json - popup if scenario.json not open - "Open a file with transactions you want to replay and click play again" - */ - var currentFile = self._deps.config.get('currentFile') - self._deps.fileManager.fileProviderOf(currentFile).get(currentFile, (error, json) => { - if (error) { - modalDialogCustom.alert('Invalid Scenario File ' + error) - } else { - if (currentFile.match('.json$')) { - try { - var obj = JSON.parse(json) - var txArray = obj.transactions || [] - var accounts = obj.accounts || [] - var options = obj.options || {} - var abis = obj.abis || {} - var linkReferences = obj.linkReferences || {} - } catch (e) { - return modalDialogCustom.alert('Invalid Scenario File, please try again') - } - if (txArray.length) { - var noInstancesText = self._view.noInstancesText - if (noInstancesText.parentNode) { noInstancesText.parentNode.removeChild(noInstancesText) } - recorder.run(txArray, accounts, options, abis, linkReferences, self._deps.udapp, (abi, address, contractName) => { - self._view.instanceContainer.appendChild(self._deps.udappUI.renderInstanceFromABI(abi, address, contractName)) - }) - } - } else { - modalDialogCustom.alert('A scenario file is required. Please make sure a scenario file is currently displayed in the editor. The file must be of type JSON. Use the "Save Transactions" Button to generate a new Scenario File.') - } - } - }) - } - - return { recordButton, runButton } -} - module.exports = runTab diff --git a/src/app/tabs/runTab/recorder.js b/src/app/tabs/runTab/recorder.js new file mode 100644 index 0000000000..ba440441e9 --- /dev/null +++ b/src/app/tabs/runTab/recorder.js @@ -0,0 +1,102 @@ +var yo = require('yo-yo') +var csjs = require('csjs-inject') +var css = require('../styles/run-tab-styles') +var helper = require('../../../lib/helper.js') +var executionContext = require('../../../execution-context') +var Recorder = require('../../../recorder') +var modalDialogCustom = require('../../ui/modal-dialog-custom') + +class RecorderUI { + + constructor (registry, runTabEvent, parentSelf) { + this.parentSelf = parentSelf + this.recorder = new Recorder(this.parentSelf._deps.udapp, this.parentSelf._deps.logCallback) + + this.recorder.event.register('newTxRecorded', (count) => { + this.parentSelf.data.count = count + this.parentSelf._view.recorderCount.innerText = count + }) + this.recorder.event.register('cleared', () => { + this.parentSelf.data.count = 0 + this.parentSelf._view.recorderCount.innerText = 0 + }) + + executionContext.event.register('contextChanged', () => { + this.recorder.clearAll() + }) + + runTabEvent.register('clearInstance', () => { + this.recorder.clearAll() + }) + } + + render () { + var css2 = csjs` + .container {} + .runTxs {} + .recorder {} + ` + + this.runButton = yo`` + this.recordButton = yo` + ` + + this.runButton.onclick = this.runScenario.bind(this) + } + + runScenario () { + var currentFile = this.parentSelf._deps.config.get('currentFile') + this.parentSelf._deps.fileManager.fileProviderOf(currentFile).get(currentFile, (error, json) => { + if (error) { + modalDialogCustom.alert('Invalid Scenario File ' + error) + } else { + if (currentFile.match('.json$')) { + try { + var obj = JSON.parse(json) + var txArray = obj.transactions || [] + var accounts = obj.accounts || [] + var options = obj.options || {} + var abis = obj.abis || {} + var linkReferences = obj.linkReferences || {} + } catch (e) { + return modalDialogCustom.alert('Invalid Scenario File, please try again') + } + if (txArray.length) { + var noInstancesText = this.parentSelf._view.noInstancesText + if (noInstancesText.parentNode) { noInstancesText.parentNode.removeChild(noInstancesText) } + this.recorder.run(txArray, accounts, options, abis, linkReferences, this.parentSelf._deps.udapp, (abi, address, contractName) => { + this.parentSelf._view.instanceContainer.appendChild(this.parentSelf._deps.udappUI.renderInstanceFromABI(abi, address, contractName)) + }) + } + } else { + modalDialogCustom.alert('A scenario file is required. Please make sure a scenario file is currently displayed in the editor. The file must be of type JSON. Use the "Save Transactions" Button to generate a new Scenario File.') + } + } + }) + } + + triggerRecordButton () { + var txJSON = JSON.stringify(this.recorder.getAll(), null, 2) + var fileManager = this.parentSelf._deps.fileManager + var path = fileManager.currentPath() + modalDialogCustom.prompt(null, 'Transactions will be saved in a file under ' + path, 'scenario.json', input => { + var fileProvider = fileManager.fileProviderOf(path) + if (fileProvider) { + var newFile = path + '/' + input + helper.createNonClashingName(newFile, fileProvider, (error, newFile) => { + if (error) return modalDialogCustom.alert('Failed to create file. ' + newFile + ' ' + error) + if (!fileProvider.set(newFile, txJSON)) { + modalDialogCustom.alert('Failed to create file ' + newFile) + } else { + fileManager.switchFile(newFile) + } + }) + } + }) + } + +} + +module.exports = RecorderUI From 749ad2038abaee66347dfa4b164e98a6aeb61c87 Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Wed, 26 Dec 2018 17:31:31 -0500 Subject: [PATCH 27/50] cleanup; remove extra parameter no longer needed in recorder constructor --- src/app/tabs/run-tab.js | 13 ++----------- src/app/tabs/runTab/recorder.js | 2 +- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index be3078542d..203d1895ea 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -1,4 +1,3 @@ -'use strict' var $ = require('jquery') var yo = require('yo-yo') var EventManager = require('../../lib/events') @@ -12,18 +11,13 @@ var ContractDropdownUI = require('./runTab/contractDropdown.js') var RecorderUI = require('./runTab/recorder.js') function runTab (opts, localRegistry) { - /* ------------------------- - VARIABLES - --------------------------- */ var self = this self.event = new EventManager() self._view = {} self.data = { count: 0, - text: `All transactions (deployed contracts and function executions) - in this environment can be saved and replayed in - another environment. e.g Transactions created in - Javascript VM can be replayed in the Injected Web3.` + text: `All transactions (deployed contracts and function executions) in this environment can be saved and replayed in + another environment. e.g Transactions created in Javascript VM can be replayed in the Injected Web3.` } self._components = {} self._components.registry = localRegistry || globlalRegistry @@ -124,9 +118,6 @@ function runTab (opts, localRegistry) { status.appendChild(self._view.collapsedView) } }) - /* ------------------------- - MAIN HTML ELEMENT - --------------------------- */ var settingsUI = new SettingsUI(container, self) var contractDropdownUI = new ContractDropdownUI(self) var el = yo` diff --git a/src/app/tabs/runTab/recorder.js b/src/app/tabs/runTab/recorder.js index ba440441e9..5b1a753ad4 100644 --- a/src/app/tabs/runTab/recorder.js +++ b/src/app/tabs/runTab/recorder.js @@ -8,7 +8,7 @@ var modalDialogCustom = require('../../ui/modal-dialog-custom') class RecorderUI { - constructor (registry, runTabEvent, parentSelf) { + constructor (runTabEvent, parentSelf) { this.parentSelf = parentSelf this.recorder = new Recorder(this.parentSelf._deps.udapp, this.parentSelf._deps.logCallback) From 77d366c1b95d11ee2f5593aa75edce98292a7eb6 Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Wed, 26 Dec 2018 17:34:02 -0500 Subject: [PATCH 28/50] refactor multiple if else into a single condition --- src/app/tabs/run-tab.js | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index 203d1895ea..ad9930b210 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -32,14 +32,8 @@ function runTab (opts, localRegistry) { var index = select.selectedIndex var selectedUnit = select.querySelectorAll('option')[index].dataset.unit var unit = 'ether' // default - if (selectedUnit === 'ether') { - unit = 'ether' - } else if (selectedUnit === 'finney') { - unit = 'finney' - } else if (selectedUnit === 'gwei') { - unit = 'gwei' - } else if (selectedUnit === 'wei') { - unit = 'wei' + if (['ether', 'finney', 'gwei', 'wei'].indexOf(selectedUnit) >= 0) { + unit = selectedUnit } cb(null, executionContext.web3().toWei(number, unit)) } catch (e) { From d7721363dc10d19e68640d5080c0af3cf858e214 Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Wed, 26 Dec 2018 17:40:30 -0500 Subject: [PATCH 29/50] simplify recorder ui --- src/app/tabs/runTab/recorder.js | 71 ++++++++++++++------------------- 1 file changed, 30 insertions(+), 41 deletions(-) diff --git a/src/app/tabs/runTab/recorder.js b/src/app/tabs/runTab/recorder.js index 5b1a753ad4..6ac5be0e73 100644 --- a/src/app/tabs/runTab/recorder.js +++ b/src/app/tabs/runTab/recorder.js @@ -21,13 +21,8 @@ class RecorderUI { this.parentSelf._view.recorderCount.innerText = 0 }) - executionContext.event.register('contextChanged', () => { - this.recorder.clearAll() - }) - - runTabEvent.register('clearInstance', () => { - this.recorder.clearAll() - }) + executionContext.event.register('contextChanged', this.recorder.clearAll.bind(this.recorder)) + runTabEvent.register('clearInstance', this.recorder.clearAll.bind(this.recorder)) } render () { @@ -50,29 +45,27 @@ class RecorderUI { var currentFile = this.parentSelf._deps.config.get('currentFile') this.parentSelf._deps.fileManager.fileProviderOf(currentFile).get(currentFile, (error, json) => { if (error) { - modalDialogCustom.alert('Invalid Scenario File ' + error) - } else { - if (currentFile.match('.json$')) { - try { - var obj = JSON.parse(json) - var txArray = obj.transactions || [] - var accounts = obj.accounts || [] - var options = obj.options || {} - var abis = obj.abis || {} - var linkReferences = obj.linkReferences || {} - } catch (e) { - return modalDialogCustom.alert('Invalid Scenario File, please try again') - } - if (txArray.length) { - var noInstancesText = this.parentSelf._view.noInstancesText - if (noInstancesText.parentNode) { noInstancesText.parentNode.removeChild(noInstancesText) } - this.recorder.run(txArray, accounts, options, abis, linkReferences, this.parentSelf._deps.udapp, (abi, address, contractName) => { - this.parentSelf._view.instanceContainer.appendChild(this.parentSelf._deps.udappUI.renderInstanceFromABI(abi, address, contractName)) - }) - } - } else { - modalDialogCustom.alert('A scenario file is required. Please make sure a scenario file is currently displayed in the editor. The file must be of type JSON. Use the "Save Transactions" Button to generate a new Scenario File.') - } + return modalDialogCustom.alert('Invalid Scenario File ' + error) + } + if (!currentFile.match('.json$')) { + modalDialogCustom.alert('A scenario file is required. Please make sure a scenario file is currently displayed in the editor. The file must be of type JSON. Use the "Save Transactions" Button to generate a new Scenario File.') + } + try { + var obj = JSON.parse(json) + var txArray = obj.transactions || [] + var accounts = obj.accounts || [] + var options = obj.options || {} + var abis = obj.abis || {} + var linkReferences = obj.linkReferences || {} + } catch (e) { + return modalDialogCustom.alert('Invalid Scenario File, please try again') + } + if (txArray.length) { + var noInstancesText = this.parentSelf._view.noInstancesText + if (noInstancesText.parentNode) { noInstancesText.parentNode.removeChild(noInstancesText) } + this.recorder.run(txArray, accounts, options, abis, linkReferences, this.parentSelf._deps.udapp, (abi, address, contractName) => { + this.parentSelf._view.instanceContainer.appendChild(this.parentSelf._deps.udappUI.renderInstanceFromABI(abi, address, contractName)) + }) } }) } @@ -83,17 +76,13 @@ class RecorderUI { var path = fileManager.currentPath() modalDialogCustom.prompt(null, 'Transactions will be saved in a file under ' + path, 'scenario.json', input => { var fileProvider = fileManager.fileProviderOf(path) - if (fileProvider) { - var newFile = path + '/' + input - helper.createNonClashingName(newFile, fileProvider, (error, newFile) => { - if (error) return modalDialogCustom.alert('Failed to create file. ' + newFile + ' ' + error) - if (!fileProvider.set(newFile, txJSON)) { - modalDialogCustom.alert('Failed to create file ' + newFile) - } else { - fileManager.switchFile(newFile) - } - }) - } + if (!fileProvider) return + var newFile = path + '/' + input + helper.createNonClashingName(newFile, fileProvider, (error, newFile) => { + if (error) return modalDialogCustom.alert('Failed to create file. ' + newFile + ' ' + error) + if (!fileProvider.set(newFile, txJSON)) return modalDialogCustom.alert('Failed to create file ' + newFile) + fileManager.switchFile(newFile) + }) }) } From 9a63d0977da263aeb1c1df2534f2401f781d0bcd Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Thu, 27 Dec 2018 07:22:59 -0500 Subject: [PATCH 30/50] refactor recorder; separate logic from view code --- src/app/tabs/run-tab.js | 16 +- src/{ => app/tabs/runTab/model}/recorder.js | 247 +++++++++++--------- src/app/tabs/runTab/recorder.js | 103 ++++---- 3 files changed, 200 insertions(+), 166 deletions(-) rename src/{ => app/tabs/runTab/model}/recorder.js (56%) diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index ad9930b210..8e4fa9d51e 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -8,6 +8,8 @@ var css = require('./styles/run-tab-styles') var SettingsUI = require('./runTab/settings.js') var ContractDropdownUI = require('./runTab/contractDropdown.js') + +var Recorder = require('./runTab/model/recorder.js') var RecorderUI = require('./runTab/recorder.js') function runTab (opts, localRegistry) { @@ -76,7 +78,19 @@ function runTab (opts, localRegistry) { var container = yo`
` - var recorderInterface = new RecorderUI(self.event, self) + var recorder = new Recorder(self._deps.udapp, self._deps.fileManager, self._deps.udapp.config) + recorder.event.register('newTxRecorded', (count) => { + this.data.count = count + this._view.recorderCount.innerText = count + }) + recorder.event.register('cleared', () => { + this.data.count = 0 + this._view.recorderCount.innerText = 0 + }) + executionContext.event.register('contextChanged', recorder.clearAll.bind(recorder)) + self.event.register('clearInstance', recorder.clearAll.bind(recorder)) + + var recorderInterface = new RecorderUI(recorder, self) recorderInterface.render() self._view.collapsedView = yo` diff --git a/src/recorder.js b/src/app/tabs/runTab/model/recorder.js similarity index 56% rename from src/recorder.js rename to src/app/tabs/runTab/model/recorder.js index a12bc9fa22..747aab6e5a 100644 --- a/src/recorder.js +++ b/src/app/tabs/runTab/model/recorder.js @@ -1,17 +1,12 @@ -var yo = require('yo-yo') -var remixLib = require('remix-lib') -var EventManager = require('./lib/events') +var async = require('async') var ethutil = require('ethereumjs-util') -var executionContext = require('./execution-context') +var remixLib = require('remix-lib') +var EventManager = remixLib.EventManager +var executionContext = remixLib.execution.executionContext var format = remixLib.execution.txFormat var txHelper = remixLib.execution.txHelper var typeConversion = remixLib.execution.typeConversion -var async = require('async') -var modal = require('./app/ui/modal-dialog-custom') - -var confirmDialog = require('./app/execution/confirmDialog') -var modalCustom = require('./app/ui/modal-dialog-custom') -var modalDialog = require('./app/ui/modaldialog') +var helper = require('../../../../lib/helper.js') /** * Record transaction as long as the user create them. @@ -19,13 +14,15 @@ var modalDialog = require('./app/ui/modaldialog') * */ class Recorder { - constructor (udapp, logCallBack) { + constructor (udapp, fileManager, config) { var self = this - self.logCallBack = logCallBack self.event = new EventManager() self.data = { _listen: true, _replay: false, journal: [], _createdContracts: {}, _createdContractsReverse: {}, _usedAccounts: {}, _abis: {}, _contractABIReferences: {}, _linkReferences: {} } + this.udapp = udapp + this.fileManager = fileManager + this.config = config - udapp.event.register('initiatingTransaction', (timestamp, tx, payLoad) => { + this.udapp.event.register('initiatingTransaction', (timestamp, tx, payLoad) => { if (tx.useCall) return var { from, to, value } = tx @@ -58,7 +55,7 @@ class Recorder { record.inputs = txHelper.serializeInputs(payLoad.funAbi) record.type = payLoad.funAbi.type - udapp.getAccounts((error, accounts) => { + this.udapp.getAccounts((error, accounts) => { if (error) return console.log(error) record.from = `account{${accounts.indexOf(from)}}` self.data._usedAccounts[record.from] = from @@ -67,13 +64,13 @@ class Recorder { } }) - udapp.event.register('transactionExecuted', (error, from, to, data, call, txResult, timestamp) => { + this.udapp.event.register('transactionExecuted', (error, from, to, data, call, txResult, timestamp) => { if (error) return console.log(error) if (call) return var address = executionContext.isVM() ? txResult.result.createdAddress : txResult.result.contractAddress if (!address) return // not a contract creation - address = addressToString(address) + address = this.addressToString(address) // save back created addresses for the convertion from tokens to real adresses this.data._createdContracts[address] = timestamp this.data._createdContractsReverse[timestamp] = address @@ -178,15 +175,15 @@ class Recorder { * @param {Function} newContractFn * */ - run (records, accounts, options, abis, linkReferences, udapp, newContractFn) { + run (records, accounts, options, abis, linkReferences, confirmationCb, continueCb, promptCb, alertCb, logCallBack, newContractFn) { var self = this self.setListen(false) - self.logCallBack(`Running ${records.length} transaction(s) ...`) + logCallBack(`Running ${records.length} transaction(s) ...`) async.eachOfSeries(records, function (tx, index, cb) { var record = self.resolveAddress(tx.record, accounts, options) var abi = abis[tx.record.abi] if (!abi) { - modal.alert('cannot find ABI for ' + tx.record.abi + '. Execution stopped at ' + index) + alertCb('cannot find ABI for ' + tx.record.abi + '. Execution stopped at ' + index) return } /* Resolve Library */ @@ -210,7 +207,7 @@ class Recorder { fnABI = txHelper.getFunction(abi, record.name + record.inputs) } if (!fnABI) { - modal.alert('cannot resolve abi of ' + JSON.stringify(record, null, '\t') + '. Execution stopped at ' + index) + alertCb('cannot resolve abi of ' + JSON.stringify(record, null, '\t') + '. Execution stopped at ' + index) cb('cannot resolve abi') return } @@ -230,107 +227,29 @@ class Recorder { tx.record.parameters[index] = value }) } catch (e) { - modal.alert('cannot resolve input parameters ' + JSON.stringify(tx.record.parameters) + '. Execution stopped at ' + index) + alertCb('cannot resolve input parameters ' + JSON.stringify(tx.record.parameters) + '. Execution stopped at ' + index) return } } var data = format.encodeData(fnABI, tx.record.parameters, tx.record.bytecode) if (data.error) { - modal.alert(data.error + '. Record:' + JSON.stringify(record, null, '\t') + '. Execution stopped at ' + index) + alertCb(data.error + '. Record:' + JSON.stringify(record, null, '\t') + '. Execution stopped at ' + index) cb(data.error) return } else { - self.logCallBack(`(${index}) ${JSON.stringify(record, null, '\t')}`) - self.logCallBack(`(${index}) data: ${data.data}`) + logCallBack(`(${index}) ${JSON.stringify(record, null, '\t')}`) + logCallBack(`(${index}) data: ${data.data}`) record.data = { dataHex: data.data, funArgs: tx.record.parameters, funAbi: fnABI, contractBytecode: tx.record.bytecode, contractName: tx.record.contractName } } - udapp.runTx(record, - - (network, tx, gasEstimation, continueTxExecution, cancelCb) => { - if (network.name !== 'Main') { - return continueTxExecution(null) - } - var amount = executionContext.web3().fromWei(typeConversion.toInt(tx.value), 'ether') - var content = confirmDialog(tx, amount, gasEstimation, self, - (gasPrice, cb) => { - let txFeeText, priceStatus - // TODO: this try catch feels like an anti pattern, can/should be - // removed, but for now keeping the original logic - try { - var fee = executionContext.web3().toBigNumber(tx.gas).mul(executionContext.web3().toBigNumber(executionContext.web3().toWei(gasPrice.toString(10), 'gwei'))) - txFeeText = ' ' + executionContext.web3().fromWei(fee.toString(10), 'ether') + ' Ether' - priceStatus = true - } catch (e) { - txFeeText = ' Please fix this issue before sending any transaction. ' + e.message - priceStatus = false - } - cb(txFeeText, priceStatus) - }, - (cb) => { - executionContext.web3().eth.getGasPrice((error, gasPrice) => { - var warnMessage = ' Please fix this issue before sending any transaction. ' - if (error) { - return cb('Unable to retrieve the current network gas price.' + warnMessage + error) - } - try { - var gasPriceValue = executionContext.web3().fromWei(gasPrice.toString(10), 'gwei') - cb(null, gasPriceValue) - } catch (e) { - cb(warnMessage + e.message, null, false) - } - }) - } - ) - modalDialog('Confirm transaction', content, - { label: 'Confirm', - fn: () => { - udapp._deps.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked) - // TODO: check if this is check is still valid given the refactor - if (!content.gasPriceStatus) { - cancelCb('Given gas price is not correct') - } else { - var gasPrice = executionContext.web3().toWei(content.querySelector('#gasprice').value, 'gwei') - continueTxExecution(gasPrice) - } - }}, { - label: 'Cancel', - fn: () => { - return cancelCb('Transaction canceled by user.') - } - }) - }, - (error, continueTxExecution, cancelCb) => { - if (error) { - var msg = typeof error !== 'string' ? error.message : error - modalDialog('Gas estimation failed', yo`
Gas estimation errored with the following message (see below). - The transaction execution will likely fail. Do you want to force sending?
- ${msg} -
`, - { - label: 'Send Transaction', - fn: () => { - continueTxExecution() - }}, { - label: 'Cancel Transaction', - fn: () => { - cancelCb() - } - }) - } else { - continueTxExecution() - } - }, - function (okCb, cancelCb) { - modalCustom.promptPassphrase(null, 'Personal mode is enabled. Please provide passphrase of account', '', okCb, cancelCb) - }, + self.udapp.runTx(record, confirmationCb, continueCb, promptCb, function (err, txResult) { if (err) { console.error(err) - self.logCallBack(err + '. Execution failed at ' + index) + logCallBack(err + '. Execution failed at ' + index) } else { var address = executionContext.isVM() ? txResult.result.createdAddress : txResult.result.contractAddress if (address) { - address = addressToString(address) + address = self.addressToString(address) // save back created addresses for the convertion from tokens to real adresses self.data._createdContracts[address] = tx.timestamp self.data._createdContractsReverse[tx.timestamp] = address @@ -342,17 +261,119 @@ class Recorder { ) }, () => { self.setListen(true); self.clearAll() }) } -} -function addressToString (address) { - if (!address) return null - if (typeof address !== 'string') { - address = address.toString('hex') + addressToString (address) { + if (!address) return null + if (typeof address !== 'string') { + address = address.toString('hex') + } + if (address.indexOf('0x') === -1) { + address = '0x' + address + } + return address } - if (address.indexOf('0x') === -1) { - address = '0x' + address + + runScenario (continueCb, promptCb, alertCb, confirmDialog, modalDialog, logCallBack, cb) { + var currentFile = this.config.get('currentFile') + this.fileManager.fileProviderOf(currentFile).get(currentFile, (error, json) => { + if (error) { + return cb('Invalid Scenario File ' + error) + } + if (!currentFile.match('.json$')) { + return cb('A scenario file is required. Please make sure a scenario file is currently displayed in the editor. The file must be of type JSON. Use the "Save Transactions" Button to generate a new Scenario File.') + } + try { + var obj = JSON.parse(json) + var txArray = obj.transactions || [] + var accounts = obj.accounts || [] + var options = obj.options || {} + var abis = obj.abis || {} + var linkReferences = obj.linkReferences || {} + } catch (e) { + return cb('Invalid Scenario File, please try again') + } + + if (!txArray.length) { + return + } + + var confirmationCb = (network, tx, gasEstimation, continueTxExecution, cancelCb) => { + if (network.name !== 'Main') { + return continueTxExecution(null) + } + var amount = executionContext.web3().fromWei(typeConversion.toInt(tx.value), 'ether') + + // TODO: there is still a UI dependency to remove here, it's still too coupled at this point to remove easily + var content = confirmDialog(tx, amount, gasEstimation, this.recorder, + (gasPrice, cb) => { + let txFeeText, priceStatus + // TODO: this try catch feels like an anti pattern, can/should be + // removed, but for now keeping the original logic + try { + var fee = executionContext.web3().toBigNumber(tx.gas).mul(executionContext.web3().toBigNumber(executionContext.web3().toWei(gasPrice.toString(10), 'gwei'))) + txFeeText = ' ' + executionContext.web3().fromWei(fee.toString(10), 'ether') + ' Ether' + priceStatus = true + } catch (e) { + txFeeText = ' Please fix this issue before sending any transaction. ' + e.message + priceStatus = false + } + cb(txFeeText, priceStatus) + }, + (cb) => { + executionContext.web3().eth.getGasPrice((error, gasPrice) => { + var warnMessage = ' Please fix this issue before sending any transaction. ' + if (error) { + return cb('Unable to retrieve the current network gas price.' + warnMessage + error) + } + try { + var gasPriceValue = executionContext.web3().fromWei(gasPrice.toString(10), 'gwei') + cb(null, gasPriceValue) + } catch (e) { + cb(warnMessage + e.message, null, false) + } + }) + } + ) + modalDialog('Confirm transaction', content, + { label: 'Confirm', + fn: () => { + this.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked) + // TODO: check if this is check is still valid given the refactor + if (!content.gasPriceStatus) { + cancelCb('Given gas price is not correct') + } else { + var gasPrice = executionContext.web3().toWei(content.querySelector('#gasprice').value, 'gwei') + continueTxExecution(gasPrice) + } + }}, { + label: 'Cancel', + fn: () => { + return cancelCb('Transaction canceled by user.') + } + }) + } + + this.run(txArray, accounts, options, abis, linkReferences, confirmationCb, continueCb, promptCb, alertCb, logCallBack, (abi, address, contractName) => { + cb(null, abi, address, contractName) + }) + }) } - return address + + saveScenario (promptCb, cb) { + var txJSON = JSON.stringify(this.getAll(), null, 2) + var path = this.fileManager.currentPath() + promptCb(path, input => { + var fileProvider = this.fileManager.fileProviderOf(path) + if (!fileProvider) return + var newFile = path + '/' + input + helper.createNonClashingName(newFile, fileProvider, (error, newFile) => { + if (error) return cb('Failed to create file. ' + newFile + ' ' + error) + if (!fileProvider.set(newFile, txJSON)) return cb('Failed to create file ' + newFile) + this.fileManager.switchFile(newFile) + }) + }) + } + } module.exports = Recorder diff --git a/src/app/tabs/runTab/recorder.js b/src/app/tabs/runTab/recorder.js index 6ac5be0e73..c14f221efc 100644 --- a/src/app/tabs/runTab/recorder.js +++ b/src/app/tabs/runTab/recorder.js @@ -1,28 +1,17 @@ var yo = require('yo-yo') var csjs = require('csjs-inject') var css = require('../styles/run-tab-styles') -var helper = require('../../../lib/helper.js') -var executionContext = require('../../../execution-context') -var Recorder = require('../../../recorder') + var modalDialogCustom = require('../../ui/modal-dialog-custom') +var modalDialog = require('../../ui/modaldialog') +var confirmDialog = require('../../execution/confirmDialog') class RecorderUI { - constructor (runTabEvent, parentSelf) { + constructor (recorder, parentSelf) { + this.recorder = recorder this.parentSelf = parentSelf - this.recorder = new Recorder(this.parentSelf._deps.udapp, this.parentSelf._deps.logCallback) - - this.recorder.event.register('newTxRecorded', (count) => { - this.parentSelf.data.count = count - this.parentSelf._view.recorderCount.innerText = count - }) - this.recorder.event.register('cleared', () => { - this.parentSelf.data.count = 0 - this.parentSelf._view.recorderCount.innerText = 0 - }) - - executionContext.event.register('contextChanged', this.recorder.clearAll.bind(this.recorder)) - runTabEvent.register('clearInstance', this.recorder.clearAll.bind(this.recorder)) + this.logCallBack = this.parentSelf._deps.logCallback } render () { @@ -42,48 +31,58 @@ class RecorderUI { } runScenario () { - var currentFile = this.parentSelf._deps.config.get('currentFile') - this.parentSelf._deps.fileManager.fileProviderOf(currentFile).get(currentFile, (error, json) => { + var continueCb = (error, continueTxExecution, cancelCb) => { if (error) { - return modalDialogCustom.alert('Invalid Scenario File ' + error) - } - if (!currentFile.match('.json$')) { - modalDialogCustom.alert('A scenario file is required. Please make sure a scenario file is currently displayed in the editor. The file must be of type JSON. Use the "Save Transactions" Button to generate a new Scenario File.') + var msg = typeof error !== 'string' ? error.message : error + modalDialog('Gas estimation failed', yo`
Gas estimation errored with the following message (see below). + The transaction execution will likely fail. Do you want to force sending?
+ ${msg} +
`, + { + label: 'Send Transaction', + fn: () => { + continueTxExecution() + }}, { + label: 'Cancel Transaction', + fn: () => { + cancelCb() + } + }) + } else { + continueTxExecution() } - try { - var obj = JSON.parse(json) - var txArray = obj.transactions || [] - var accounts = obj.accounts || [] - var options = obj.options || {} - var abis = obj.abis || {} - var linkReferences = obj.linkReferences || {} - } catch (e) { - return modalDialogCustom.alert('Invalid Scenario File, please try again') - } - if (txArray.length) { - var noInstancesText = this.parentSelf._view.noInstancesText - if (noInstancesText.parentNode) { noInstancesText.parentNode.removeChild(noInstancesText) } - this.recorder.run(txArray, accounts, options, abis, linkReferences, this.parentSelf._deps.udapp, (abi, address, contractName) => { - this.parentSelf._view.instanceContainer.appendChild(this.parentSelf._deps.udappUI.renderInstanceFromABI(abi, address, contractName)) - }) + } + + var promptCb = (okCb, cancelCb) => { + modalDialogCustom.promptPassphrase(null, 'Personal mode is enabled. Please provide passphrase of account', '', okCb, cancelCb) + } + + var alertCb = (msg) => { + modalDialogCustom.alert(msg) + } + + // TODO: there is still a UI dependency to remove here, it's still too coupled at this point to remove easily + this.recorder.runScenario(continueCb, promptCb, alertCb, confirmDialog, modalDialog, this.logCallBack, (error, abi, address, contractName) => { + if (error) { + return modalDialogCustom.alert(error) } + + var noInstancesText = this.parentSelf._view.noInstancesText + if (noInstancesText.parentNode) { noInstancesText.parentNode.removeChild(noInstancesText) } + + this.parentSelf._view.instanceContainer.appendChild(this.parentSelf._deps.udappUI.renderInstanceFromABI(abi, address, contractName)) }) } triggerRecordButton () { - var txJSON = JSON.stringify(this.recorder.getAll(), null, 2) - var fileManager = this.parentSelf._deps.fileManager - var path = fileManager.currentPath() - modalDialogCustom.prompt(null, 'Transactions will be saved in a file under ' + path, 'scenario.json', input => { - var fileProvider = fileManager.fileProviderOf(path) - if (!fileProvider) return - var newFile = path + '/' + input - helper.createNonClashingName(newFile, fileProvider, (error, newFile) => { - if (error) return modalDialogCustom.alert('Failed to create file. ' + newFile + ' ' + error) - if (!fileProvider.set(newFile, txJSON)) return modalDialogCustom.alert('Failed to create file ' + newFile) - fileManager.switchFile(newFile) - }) - }) + this.recorder.saveScenario( + (path, cb) => { + modalDialogCustom.prompt(null, 'Transactions will be saved in a file under ' + path, 'scenario.json', cb) + }, + (error) => { + if (error) return modalDialogCustom.alert(error) + } + ) } } From 7ff7b8e5bf20318b623970be206345e25c1adf38 Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Thu, 27 Dec 2018 13:51:50 -0500 Subject: [PATCH 31/50] refactor settings; separate logic from view code; refactor code --- src/app/tabs/run-tab.js | 18 +- src/app/tabs/runTab/model/settings.js | 117 +++++++++++++ src/app/tabs/runTab/settings.js | 233 ++++++++++---------------- 3 files changed, 225 insertions(+), 143 deletions(-) create mode 100644 src/app/tabs/runTab/model/settings.js diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index 8e4fa9d51e..48999f914e 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -6,7 +6,9 @@ var executionContext = require('../../execution-context') var Card = require('../ui/card') var css = require('./styles/run-tab-styles') +var Settings = require('./runTab/model/settings.js') var SettingsUI = require('./runTab/settings.js') + var ContractDropdownUI = require('./runTab/contractDropdown.js') var Recorder = require('./runTab/model/recorder.js') @@ -126,7 +128,21 @@ function runTab (opts, localRegistry) { status.appendChild(self._view.collapsedView) } }) - var settingsUI = new SettingsUI(container, self) + + var settings = new Settings(self._deps.udapp) + var settingsUI = new SettingsUI(settings) + + self.event.register('clearInstance', () => { + var instanceContainer = self._view.instanceContainer + var instanceContainerTitle = self._view.instanceContainerTitle + instanceContainer.innerHTML = '' // clear the instances list + instanceContainer.appendChild(instanceContainerTitle) + instanceContainer.appendChild(self._view.noInstancesText) + }) + settingsUI.event.register('clearInstance', () => { + this.event.trigger('clearInstance', []) + }) + var contractDropdownUI = new ContractDropdownUI(self) var el = yo`
diff --git a/src/app/tabs/runTab/model/settings.js b/src/app/tabs/runTab/model/settings.js new file mode 100644 index 0000000000..5d961c077c --- /dev/null +++ b/src/app/tabs/runTab/model/settings.js @@ -0,0 +1,117 @@ +var ethJSUtil = require('ethereumjs-util') +var Personal = require('web3-eth-personal') +var remixLib = require('remix-lib') +var EventManager = remixLib.EventManager +var executionContext = remixLib.execution.executionContext + +class Settings { + + constructor (udapp) { + this.udapp = udapp + this.event = new EventManager() + + this.udapp.event.register('transactionExecuted', (error, from, to, data, lookupOnly, txResult) => { + this.event.trigger('transactionExecuted', [error, from, to, data, lookupOnly, txResult]) + }) + + executionContext.event.register('contextChanged', (context, silent) => { + this.event.trigger('contextChanged', [context, silent]) + }) + + executionContext.event.register('addProvider', (network) => { + this.event.trigger('addProvider', [network]) + }) + + executionContext.event.register('removeProvider', (name) => { + this.event.trigger('removeProvider', [name]) + }) + + this.networkcallid = 0 + } + + changeExecutionContext (context, cb) { + return executionContext.executionContextChange(context, null, cb) + } + + setProviderFromEndpoint (target, context, cb) { + return executionContext.setProviderFromEndpoint(target, context, cb) + } + + getProvider () { + return executionContext.getProvider() + } + + getAccountBalanceForAddress (address, cb) { + return this.udapp.getBalanceInEther(address, cb) + } + + updateNetwork (cb) { + this.networkcallid++ + ((callid) => { + executionContext.detectNetwork((err, { id, name } = {}) => { + if (this.networkcallid > callid) return + this.networkcallid++ + if (err) { + return cb(err) + } + cb(null, {id, name}) + }) + })(this.networkcallid) + } + + newAccount (passphraseCb, cb) { + return this.udapp.newAccount('', passphraseCb, cb) + } + + getAccounts (cb) { + return this.udapp.getAccounts(cb) + } + + isWeb3Provider () { + var isVM = executionContext.isVM() + var isInjected = executionContext.getProvider() === 'injected' + return (!isVM && !isInjected) + } + + signMessage (message, account, passphrase, cb) { + var isVM = executionContext.isVM() + var isInjected = executionContext.getProvider() === 'injected' + + if (isVM) { + const personalMsg = ethJSUtil.hashPersonalMessage(Buffer.from(message)) + var privKey = this.udapp.accounts[account].privateKey + try { + var rsv = ethJSUtil.ecsign(personalMsg, privKey) + var signedData = ethJSUtil.toRpcSig(rsv.v, rsv.r, rsv.s) + cb(null, '0x' + personalMsg.toString('hex'), signedData) + } catch (e) { + cb(e.message) + } + return + } + if (isInjected) { + const hashedMsg = executionContext.web3().sha3(message) + try { + executionContext.web3().eth.sign(account, hashedMsg, (error, signedData) => { + cb(error, hashedMsg, signedData) + }) + } catch (e) { + cb(e.message) + } + return + } + + const hashedMsg = executionContext.web3().sha3(message) + try { + var personal = new Personal(executionContext.web3().currentProvider) + personal.sign(hashedMsg, account, passphrase, (error, signedData) => { + cb(error, hashedMsg, signedData) + }) + } catch (e) { + cb(e.message) + } + } + +} + +module.exports = Settings diff --git a/src/app/tabs/runTab/settings.js b/src/app/tabs/runTab/settings.js index f51c992904..2a60b0f91e 100644 --- a/src/app/tabs/runTab/settings.js +++ b/src/app/tabs/runTab/settings.js @@ -1,26 +1,41 @@ var $ = require('jquery') var yo = require('yo-yo') -var ethJSUtil = require('ethereumjs-util') -var Personal = require('web3-eth-personal') +var remixLib = require('remix-lib') +var EventManager = remixLib.EventManager var css = require('../styles/run-tab-styles') -var executionContext = require('../../../execution-context') var copyToClipboard = require('../../ui/copy-to-clipboard') var modalDialogCustom = require('../../ui/modal-dialog-custom') var addTooltip = require('../../ui/tooltip') -var modalCustom = require('../../ui/modal-dialog-custom') -var tootip = require('../../ui/tooltip') var helper = require('../../../lib/helper.js') class SettingsUI { - constructor (container, parentSelf) { - this.container = container - this.parentSelf = parentSelf - // HELPER FUNCTIONS AND EVENTS - this.parentSelf._deps.udapp.event.register('transactionExecuted', (error, from, to, data, lookupOnly, txResult) => { + constructor (settings) { + this.settings = settings + this.event = new EventManager() + + this.settings.event.register('transactionExecuted', (error, from, to, data, lookupOnly, txResult) => { if (error) return if (!lookupOnly) this.el.querySelector('#value').value = '0' - updateAccountBalances(this.container, this.parentSelf) + this.updateAccountBalances() + }) + + setInterval(() => { + this.updateAccountBalances() + }, 10 * 1000) + + this.accountListCallId = 0 + this.loadedAccounts = {} + } + + updateAccountBalances () { + if (!this.el) return + var accounts = $(this.el.querySelector('#txorigin')).children('option') + accounts.each((index, account) => { + this.settings.getAccountBalanceForAddress(account.value, (err, balance) => { + if (err) return + account.innerText = helper.shortenAddress(account.value, balance) + }) }) } @@ -88,7 +103,6 @@ class SettingsUI {
` - // DOM ELEMENT var el = yo`
${environmentEl} @@ -98,40 +112,47 @@ class SettingsUI {
` - // DROPDOWN var selectExEnv = environmentEl.querySelector('#selectExEnvOptions') - this.selectExEnv = selectExEnv + this.setDropdown(selectExEnv) - this.parentSelf.event.register('clearInstance', () => { - var instanceContainer = this.parentSelf._view.instanceContainer - var instanceContainerTitle = this.parentSelf._view.instanceContainerTitle - instanceContainer.innerHTML = '' // clear the instances list - instanceContainer.appendChild(instanceContainerTitle) - instanceContainer.appendChild(this.parentSelf._view.noInstancesText) + this.settings.event.register('contextChanged', (context, silent) => { + this.setFinalContext() }) - executionContext.event.register('addProvider', (network) => { + setInterval(() => { + this.updateNetwork() + this.fillAccountsList() + }, 5000) + + this.el = el + return el + } + + setDropdown (selectExEnv) { + this.selectExEnv = selectExEnv + + this.settings.event.register('addProvider', (network) => { selectExEnv.appendChild(yo``) - tootip(`${network.name} [${network.url}] added`) + addTooltip(`${network.name} [${network.url}] added`) }) - executionContext.event.register('removeProvider', (name) => { + this.settings.event.register('removeProvider', (name) => { var env = selectExEnv.querySelector(`option[value="${name}"]`) if (env) { selectExEnv.removeChild(env) - tootip(`${name} removed`) + addTooltip(`${name} removed`) } }) selectExEnv.addEventListener('change', (event) => { let context = selectExEnv.options[selectExEnv.selectedIndex].value - executionContext.executionContextChange(context, null, () => { + this.settings.changeExecutionContext(context, () => { modalDialogCustom.confirm(null, 'Are you sure you want to connect to an ethereum node?', () => { modalDialogCustom.prompt(null, 'Web3 Provider Endpoint', 'http://localhost:8545', (target) => { - executionContext.setProviderFromEndpoint(target, context, (alertMsg) => { + this.settings.setProviderFromEndpoint(target, context, (alertMsg) => { if (alertMsg) { modalDialogCustom.alert(alertMsg) } @@ -144,38 +165,23 @@ class SettingsUI { }, this.setFinalContext.bind(this)) }) - selectExEnv.value = executionContext.getProvider() - executionContext.event.register('contextChanged', (context, silent) => { - this.setFinalContext() - }) - - setInterval(() => { - this.updateNetwork() - fillAccountsList(el, this.parentSelf) - }, 5000) - - setInterval(() => { - updateAccountBalances(this.container, this.parentSelf) - }, 10000) - - this.el = el - return el + selectExEnv.value = this.settings.getProvider() } setFinalContext () { // set the final context. Cause it is possible that this is not the one we've originaly selected - this.selectExEnv.value = executionContext.getProvider() - this.parentSelf.event.trigger('clearInstance', []) + this.selectExEnv.value = this.settings.getProvider() + this.event.trigger('clearInstance', []) this.updateNetwork() - fillAccountsList(this.el, this.parentSelf) + this.fillAccountsList() } newAccount () { - this.parentSelf._deps.udapp.newAccount('', + this.settings.newAccount( (cb) => { - modalCustom.promptPassphraseCreation((error, passphrase) => { + modalDialogCustom.promptPassphraseCreation((error, passphrase) => { if (error) { - return modalCustom.alert(error) + return modalDialogCustom.alert(error) } cb(passphrase) }, () => {}) @@ -189,127 +195,70 @@ class SettingsUI { ) } - alertSignedData (error, hash, signedData) { - if (error && error.message !== '') { - console.log(error) - addTooltip(error.message) - } else { - modalDialogCustom.alert(yo`
hash:${hash}
signature:${signedData}
`) - } - } + signMessage () { + this.settings.getAccounts((err, accounts) => { + if (err) { + return addTooltip(`Cannot get account list: ${err}`) + } - signMessage (event) { - this.parentSelf._deps.udapp.getAccounts((err, accounts) => { - if (err) { addTooltip(`Cannot get account list: ${err}`) } var signMessageDialog = { 'title': 'Sign a message', 'text': 'Enter a message to sign', 'inputvalue': 'Message to sign' } - var $txOrigin = this.container.querySelector('#txorigin') + var $txOrigin = this.el.querySelector('#txorigin') var account = $txOrigin.selectedOptions[0].value - var isVM = executionContext.isVM() - var isInjected = executionContext.getProvider() === 'injected' - if (isVM) { - modalDialogCustom.promptMulti(signMessageDialog, (message) => { - const personalMsg = ethJSUtil.hashPersonalMessage(Buffer.from(message)) - var privKey = this.parentSelf._deps.udapp.accounts[account].privateKey - try { - var rsv = ethJSUtil.ecsign(personalMsg, privKey) - var signedData = ethJSUtil.toRpcSig(rsv.v, rsv.r, rsv.s) - this.alertSignedData(null, '0x' + personalMsg.toString('hex'), signedData) - } catch (e) { - addTooltip(e.message) - return - } - }, false) - } else if (isInjected) { + + var promptCb = (passphrase) => { modalDialogCustom.promptMulti(signMessageDialog, (message) => { - const hashedMsg = executionContext.web3().sha3(message) - try { - executionContext.web3().eth.sign(account, hashedMsg, (error, signedData) => { - this.alertSignedData(error, hashedMsg, signedData) - }) - } catch (e) { - addTooltip(e.message) - console.log(e) - return - } - }) - } else { - modalDialogCustom.promptPassphrase('Passphrase to sign a message', 'Enter your passphrase for this account to sign the message', '', (passphrase) => { - modalDialogCustom.promptMulti(signMessageDialog, (message) => { - const hashedMsg = executionContext.web3().sha3(message) - try { - var personal = new Personal(executionContext.web3().currentProvider) - personal.sign(hashedMsg, account, passphrase, (error, signedData) => { - this.alertSignedData(error, hashedMsg, signedData) - }) - } catch (e) { - addTooltip(e.message) - console.log(e) - return + this.settings.signMessage(message, account, passphrase, (err, msgHash, signedData) => { + if (err) { + return addTooltip(err) } + modalDialogCustom.alert(yo`
hash:${msgHash}
signature:${signedData}
`) }) }, false) } + + if (this.settings.isWeb3Provider()) { + return modalDialogCustom.promptPassphrase('Passphrase to sign a message', 'Enter your passphrase for this account to sign the message', '', promptCb, false) + } + promptCb() }) } updateNetwork () { - let self = this - var networkcallid = 0 - networkcallid++ - ((callid) => { - executionContext.detectNetwork((err, { id, name } = {}) => { - if (networkcallid > callid) return - networkcallid++ - if (err) { - console.error(err) - self.netUI.innerHTML = 'can\'t detect network ' - } else { - self.netUI.innerHTML = ` ${name} (${id || '-'})` - } - }) - })(networkcallid) + this.settings.updateNetwork((err, {id, name} = {}) => { + if (err) { + this.netUI.innerHTML = 'can\'t detect network ' + return + } + this.netUI.innerHTML = ` ${name} (${id || '-'})` + }) } -} - -var accountListCallId = 0 -var loadedAccounts = {} -function fillAccountsList (container, self) { - accountListCallId++ - ((callid) => { - var txOrigin = container.querySelector('#txorigin') - self._deps.udapp.getAccounts((err, accounts) => { - if (accountListCallId > callid) return - accountListCallId++ + // TODO: unclear what's the goal of accountListCallId, feels like it can be simplified + fillAccountsList () { + this.accountListCallId++ + var callid = this.accountListCallId + var txOrigin = this.el.querySelector('#txorigin') + this.settings.getAccounts((err, accounts) => { + if (this.accountListCallId > callid) return + this.accountListCallId++ if (err) { addTooltip(`Cannot get account list: ${err}`) } - for (var loadedaddress in loadedAccounts) { + for (var loadedaddress in this.loadedAccounts) { if (accounts.indexOf(loadedaddress) === -1) { txOrigin.removeChild(txOrigin.querySelector('option[value="' + loadedaddress + '"]')) - delete loadedAccounts[loadedaddress] + delete this.loadedAccounts[loadedaddress] } } for (var i in accounts) { var address = accounts[i] - if (!loadedAccounts[address]) { + if (!this.loadedAccounts[address]) { txOrigin.appendChild(yo``) - loadedAccounts[address] = 1 + this.loadedAccounts[address] = 1 } } txOrigin.setAttribute('value', accounts[0]) }) - })(accountListCallId) -} + } -function updateAccountBalances (container, self) { - var accounts = $(container.querySelector('#txorigin')).children('option') - accounts.each((index, value) => { - ((acc) => { - self._deps.udapp.getBalanceInEther(accounts[acc].value, (err, res) => { - if (err) return - accounts[acc].innerText = helper.shortenAddress(accounts[acc].value, res) - }) - })(index) - }) } module.exports = SettingsUI From d095b8353114cbed83f05b725adccc113e5125ac Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Fri, 28 Dec 2018 13:33:50 -0500 Subject: [PATCH 32/50] refactor contract dropdown; separate logic from view code; refactor code --- src/app/tabs/run-tab.js | 27 ++- src/app/tabs/runTab/contractDropdown.js | 227 +++++++++------------ src/app/tabs/runTab/model/dropdownlogic.js | 70 +++++++ 3 files changed, 189 insertions(+), 135 deletions(-) create mode 100644 src/app/tabs/runTab/model/dropdownlogic.js diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index 48999f914e..d3b90c34db 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -9,6 +9,7 @@ var css = require('./styles/run-tab-styles') var Settings = require('./runTab/model/settings.js') var SettingsUI = require('./runTab/settings.js') +var DropdownLogic = require('./runTab/model/dropdownLogic.js') var ContractDropdownUI = require('./runTab/contractDropdown.js') var Recorder = require('./runTab/model/recorder.js') @@ -133,17 +134,31 @@ function runTab (opts, localRegistry) { var settingsUI = new SettingsUI(settings) self.event.register('clearInstance', () => { - var instanceContainer = self._view.instanceContainer - var instanceContainerTitle = self._view.instanceContainerTitle - instanceContainer.innerHTML = '' // clear the instances list - instanceContainer.appendChild(instanceContainerTitle) - instanceContainer.appendChild(self._view.noInstancesText) + this._view.instanceContainer.innerHTML = '' // clear the instances list + this._view.instanceContainer.appendChild(self._view.instanceContainerTitle) + this._view.instanceContainer.appendChild(self._view.noInstancesText) }) settingsUI.event.register('clearInstance', () => { this.event.trigger('clearInstance', []) }) - var contractDropdownUI = new ContractDropdownUI(self) + var dropdownLogic = new DropdownLogic(self) + var contractDropdownUI = new ContractDropdownUI(dropdownLogic, self) + + contractDropdownUI.event.register('clearInstance', () => { + var noInstancesText = this._view.noInstancesText + if (noInstancesText.parentNode) { noInstancesText.parentNode.removeChild(noInstancesText) } + }) + contractDropdownUI.event.register('newContractABIAdded', (abi, address) => { + this._view.instanceContainer.appendChild(this._deps.udappUI.renderInstanceFromABI(abi, address, address)) + }) + contractDropdownUI.event.register('newContractInstanceAdded', (contractObject, address, value) => { + this._view.instanceContainer.appendChild(this._deps.udappUI.renderInstance(contractObject, address, value)) + }) + + this._view.instanceContainer.appendChild(this._view.instanceContainerTitle) + this._view.instanceContainer.appendChild(this._view.noInstancesText) + var el = yo`
${settingsUI.render()} diff --git a/src/app/tabs/runTab/contractDropdown.js b/src/app/tabs/runTab/contractDropdown.js index 6e9fd461a4..f1157aaa04 100644 --- a/src/app/tabs/runTab/contractDropdown.js +++ b/src/app/tabs/runTab/contractDropdown.js @@ -1,93 +1,71 @@ var yo = require('yo-yo') -var ethJSUtil = require('ethereumjs-util') var css = require('../styles/run-tab-styles') var executionContext = require('../../../execution-context') var modalDialogCustom = require('../../ui/modal-dialog-custom') -var modalCustom = require('../../ui/modal-dialog-custom') -var CompilerAbstract = require('../../compiler/compiler-abstract') var remixLib = require('remix-lib') var txExecution = remixLib.execution.txExecution var txFormat = remixLib.execution.txFormat var txHelper = remixLib.execution.txHelper +var EventManager = remixLib.EventManager var typeConversion = remixLib.execution.typeConversion var confirmDialog = require('../../execution/confirmDialog') var modalDialog = require('../../ui/modaldialog') var MultiParamManager = require('../../../multiParamManager') class ContractDropdownUI { - constructor (parentSelf) { + constructor (dropdownLogic, parentSelf) { this.parentSelf = parentSelf + this.dropdownLogic = dropdownLogic + this.event = new EventManager() + + this.listenToEvents() } - render () { - this.instanceContainer = this.parentSelf._view.instanceContainer - var instanceContainerTitle = this.parentSelf._view.instanceContainerTitle - this.instanceContainer.appendChild(instanceContainerTitle) - this.instanceContainer.appendChild(this.parentSelf._view.noInstancesText) - var compFails = yo`` - var info = yo`` + listenToEvents () { + this.dropdownLogic.event.register('newlyCompiled', (success, data, source, compiler, compilerFullName) => { + var contractNames = document.querySelector(`.${css.contractNames.classNames[0]}`) + contractNames.innerHTML = '' + if (success) { + this.selectContractNames.removeAttribute('disabled') + this.dropdownLogic.getCompiledContracts(compiler, compilerFullName).forEach((contract) => { + contractNames.appendChild(yo``) + }) + } else { + this.selectContractNames.setAttribute('disabled', true) + } + this.setInputParamsPlaceHolder() - var newlyCompiled = (success, data, source, compiler, compilerFullName) => { - this.getContractNames(success, data, compiler, compilerFullName) if (success) { - compFails.style.display = 'none' + this.compFails.style.display = 'none' document.querySelector(`.${css.contractNames}`).classList.remove(css.contractNamesError) } else { - compFails.style.display = 'block' + this.compFails.style.display = 'block' document.querySelector(`.${css.contractNames}`).classList.add(css.contractNamesError) } - } - - this.parentSelf._deps.pluginManager.event.register('sendCompilationResult', (file, source, languageVersion, data) => { - // TODO check whether the tab is configured - let compiler = new CompilerAbstract(languageVersion, data) - this.parentSelf._deps.compilersArtefacts[languageVersion] = compiler - this.parentSelf._deps.compilersArtefacts['__last'] = compiler - newlyCompiled(true, data, source, compiler, languageVersion) }) - this.parentSelf._deps.compiler.event.register('compilationFinished', (success, data, source) => { - var name = 'solidity' - let compiler = new CompilerAbstract(name, data) - this.parentSelf._deps.compilersArtefacts[name] = compiler - this.parentSelf._deps.compilersArtefacts['__last'] = compiler - newlyCompiled(success, data, source, this.parentSelf._deps.compiler, name) - }) - - var deployAction = (value) => { - this.parentSelf._view.createPanel.style.display = value - this.parentSelf._view.orLabel.style.display = value - } + this.dropdownLogic.event.register('currentFileChanged', this.changeCurrentFile.bind(this)) + } - this.parentSelf._deps.fileManager.event.register('currentFileChanged', (currentFile) => { - document.querySelector(`.${css.contractNames}`).classList.remove(css.contractNamesError) - var contractNames = document.querySelector(`.${css.contractNames.classNames[0]}`) - contractNames.innerHTML = '' - if (/.(.abi)$/.exec(currentFile)) { - deployAction('none') - compFails.style.display = 'none' - contractNames.appendChild(yo``) - this.selectContractNames.setAttribute('disabled', true) - } else if (/.(.sol)$/.exec(currentFile)) { - deployAction('block') - } - }) + render () { + this.compFails = yo`` + var info = yo`` this.atAddressButtonInput = yo`` this.selectContractNames = yo`` - this.parentSelf._view.createPanel = yo`
` - this.parentSelf._view.orLabel = yo`
or
` + this.createPanel = yo`
` + this.orLabel = yo`
or
` var el = yo`
- ${this.selectContractNames} ${compFails} ${info} + ${this.selectContractNames} ${this.compFails} ${info}
- ${this.parentSelf._view.createPanel} - ${this.parentSelf._view.orLabel} + ${this.createPanel} + ${this.orLabel}
-
At Address
+
At Address
${this.atAddressButtonInput}
@@ -98,36 +76,50 @@ class ContractDropdownUI { return el } + changeCurrentFile (currentFile) { + document.querySelector(`.${css.contractNames}`).classList.remove(css.contractNamesError) + var contractNames = document.querySelector(`.${css.contractNames.classNames[0]}`) + contractNames.innerHTML = '' + if (/.(.abi)$/.exec(currentFile)) { + this.createPanel.style.display = 'none' + this.orLabel.style.display = 'none' + this.compFails.style.display = 'none' + contractNames.appendChild(yo``) + this.selectContractNames.setAttribute('disabled', true) + } else if (/.(.sol)$/.exec(currentFile)) { + this.createPanel.style.display = 'block' + this.orLabel.style.display = 'block' + } + } + setInputParamsPlaceHolder () { - this.parentSelf._view.createPanel.innerHTML = '' - if (this.selectContractNames.selectedIndex >= 0 && this.selectContractNames.children.length > 0) { - var selectedContract = this.getSelectedContract() - var ctrabi = txHelper.getConstructorInterface(selectedContract.contract.object.abi) - var ctrEVMbc = selectedContract.contract.object.evm.bytecode.object - var createConstructorInstance = new MultiParamManager(0, ctrabi, (valArray, inputsValues) => { - this.createInstance(inputsValues, selectedContract.compiler) - }, txHelper.inputParametersDeclarationToString(ctrabi.inputs), 'Deploy', ctrEVMbc) - this.parentSelf._view.createPanel.appendChild(createConstructorInstance.render()) + this.createPanel.innerHTML = '' + if (this.selectContractNames.selectedIndex < 0 || this.selectContractNames.children.length <= 0) { + this.createPanel.innerHTML = 'No compiled contracts' return - } else { - this.parentSelf._view.createPanel.innerHTML = 'No compiled contracts' } + + var selectedContract = this.getSelectedContract() + var ctrabi = txHelper.getConstructorInterface(selectedContract.contract.object.abi) + var ctrEVMbc = selectedContract.contract.object.evm.bytecode.object + var createConstructorInstance = new MultiParamManager(0, ctrabi, (valArray, inputsValues) => { + this.createInstance(inputsValues, selectedContract.compiler) + }, txHelper.inputParametersDeclarationToString(ctrabi.inputs), 'Deploy', ctrEVMbc) + this.createPanel.appendChild(createConstructorInstance.render()) } getSelectedContract () { var contract = this.selectContractNames.children[this.selectContractNames.selectedIndex] var contractName = contract.innerHTML - var compiler = this.parentSelf._deps.compilersArtefacts[contract.getAttribute('compiler')] + var compiler = this.dropdownLogic.getContractCompiler(contract.getAttribute('compiler')) if (!compiler) return null - if (contractName) { - return { - name: contractName, - contract: compiler.getContract(contractName), - compiler - } + if (!contractName) return null + return { + name: contractName, + contract: compiler.getContract(contractName), + compiler } - return null } createInstanceCallback (selectedContract, data) { @@ -214,29 +206,27 @@ class ContractDropdownUI { } }, function (okCb, cancelCb) { - modalCustom.promptPassphrase(null, 'Personal mode is enabled. Please provide passphrase of account', '', okCb, cancelCb) + modalDialogCustom.promptPassphrase(null, 'Personal mode is enabled. Please provide passphrase of account', '', okCb, cancelCb) }, (error, txResult) => { - if (!error) { - var isVM = executionContext.isVM() - if (isVM) { - var vmError = txExecution.checkVMError(txResult) - if (vmError.error) { - this.parentSelf._deps.logCallback(vmError.message) - return - } - } - if (txResult.result.status && txResult.result.status === '0x0') { - this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} errored: transaction execution failed`) + if (error) { + return this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} errored: ${error}`) + } + var isVM = executionContext.isVM() + if (isVM) { + var vmError = txExecution.checkVMError(txResult) + if (vmError.error) { + this.parentSelf._deps.logCallback(vmError.message) return } - var noInstancesText = this.parentSelf._view.noInstancesText - if (noInstancesText.parentNode) { noInstancesText.parentNode.removeChild(noInstancesText) } - var address = isVM ? txResult.result.createdAddress : txResult.result.contractAddress - this.instanceContainer.appendChild(this.parentSelf._deps.udappUI.renderInstance(selectedContract.contract.object, address, this.selectContractNames.value)) - } else { - this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} errored: ${error}`) } + if (txResult.result.status && txResult.result.status === '0x0') { + this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} errored: transaction execution failed`) + return + } + this.event.trigger('clearInstance') + var address = isVM ? txResult.result.createdAddress : txResult.result.contractAddress + this.event.trigger('newContractInstanceAdded', [selectedContract.contract.object, address, this.selectContractNames.value]) } ) } @@ -317,7 +307,7 @@ class ContractDropdownUI { }) }, function (okCb, cancelCb) { - modalCustom.promptPassphrase(null, 'Personal mode is enabled. Please provide passphrase of account', '', okCb, cancelCb) + modalDialogCustom.promptPassphrase(null, 'Personal mode is enabled. Please provide passphrase of account', '', okCb, cancelCb) }, runTxCallback) }) @@ -350,46 +340,25 @@ class ContractDropdownUI { } } - // ACCESS DEPLOYED INSTANCE loadFromAddress () { - var noInstancesText = this.parentSelf._view.noInstancesText - if (noInstancesText.parentNode) { noInstancesText.parentNode.removeChild(noInstancesText) } + this.event.trigger('clearInstance') + var address = this.atAddressButtonInput.value - if (!ethJSUtil.isValidAddress(address)) { - return modalDialogCustom.alert('Invalid address.') - } - if (/[a-f]/.test(address) && /[A-F]/.test(address) && !ethJSUtil.isValidChecksumAddress(address)) { - return modalDialogCustom.alert('Invalid checksum address.') - } - if (/.(.abi)$/.exec(this.parentSelf._deps.config.get('currentFile'))) { - modalDialogCustom.confirm(null, 'Do you really want to interact with ' + address + ' using the current ABI definition ?', () => { - var abi - try { - abi = JSON.parse(this.parentSelf._deps.editor.currentContent()) - } catch (e) { - return modalDialogCustom.alert('Failed to parse the current file as JSON ABI.') + this.dropdownLogic.loadContractFromAddress(address, + (cb) => { + modalDialogCustom.confirm(null, 'Do you really want to interact with ' + address + ' using the current ABI definition ?', cb) + }, + (error, loadType, abi) => { + if (error) { + return modalDialogCustom.alert(error) } - this.instanceContainer.appendChild(this.parentSelf._deps.udappUI.renderInstanceFromABI(abi, address, address)) - }) - } else { - var selectedContract = this.getSelectedContract() - this.instanceContainer.appendChild(this.parentSelf._deps.udappUI.renderInstance(selectedContract.contract.object, address, this.selectContractNames.value)) - } - } - - // GET NAMES OF ALL THE CONTRACTS - getContractNames (success, data, compiler, compilerFullName) { - var contractNames = document.querySelector(`.${css.contractNames.classNames[0]}`) - contractNames.innerHTML = '' - if (success) { - this.selectContractNames.removeAttribute('disabled') - compiler.visitContracts((contract) => { - contractNames.appendChild(yo``) - }) - } else { - this.selectContractNames.setAttribute('disabled', true) - } - this.setInputParamsPlaceHolder() + if (loadType === 'abi') { + return this.event.trigger('newContractABIAdded', [abi, address]) + } + var selectedContract = this.getSelectedContract() + this.event.trigger('newContractInstanceAdded', [selectedContract.contract.object, address, this.selectContractNames.value]) + } + ) } } diff --git a/src/app/tabs/runTab/model/dropdownlogic.js b/src/app/tabs/runTab/model/dropdownlogic.js new file mode 100644 index 0000000000..fce17512d7 --- /dev/null +++ b/src/app/tabs/runTab/model/dropdownlogic.js @@ -0,0 +1,70 @@ +var ethJSUtil = require('ethereumjs-util') +var remixLib = require('remix-lib') +var CompilerAbstract = require('../../../compiler/compiler-abstract') +var EventManager = remixLib.EventManager + +class DropdownLogic { + constructor (parentSelf) { + this.parentSelf = parentSelf + this.event = new EventManager() + + this.listenToCompilationEvents() + + this.parentSelf._deps.fileManager.event.register('currentFileChanged', (currentFile) => { + this.event.trigger('currentFileChanged', [currentFile]) + }) + } + + listenToCompilationEvents () { + this.parentSelf._deps.pluginManager.event.register('sendCompilationResult', (file, source, languageVersion, data) => { + // TODO check whether the tab is configured + let compiler = new CompilerAbstract(languageVersion, data) + this.parentSelf._deps.compilersArtefacts[languageVersion] = compiler + this.parentSelf._deps.compilersArtefacts['__last'] = compiler + this.event.trigger('newlyCompiled', [true, data, source, compiler, languageVersion]) + }) + + this.parentSelf._deps.compiler.event.register('compilationFinished', (success, data, source) => { + var name = 'solidity' + let compiler = new CompilerAbstract(name, data) + this.parentSelf._deps.compilersArtefacts[name] = compiler + this.parentSelf._deps.compilersArtefacts['__last'] = compiler + this.event.trigger('newlyCompiled', [success, data, source, this.parentSelf._deps.compiler, name]) + }) + } + + loadContractFromAddress (address, confirmCb, cb) { + if (!ethJSUtil.isValidAddress(address)) { + return cb('Invalid address.') + } + if (/[a-f]/.test(address) && /[A-F]/.test(address) && !ethJSUtil.isValidChecksumAddress(address)) { + return cb('Invalid checksum address.') + } + if (/.(.abi)$/.exec(this.parentSelf._deps.config.get('currentFile'))) { + confirmCb(() => { + var abi + try { + abi = JSON.parse(this.parentSelf._deps.editor.currentContent()) + } catch (e) { + return cb('Failed to parse the current file as JSON ABI.') + } + cb(null, 'abi', abi) + }) + } + cb(null, 'instance') + } + + getCompiledContracts (compiler, compilerFullName) { + var contracts = [] + compiler.visitContracts((contract) => { + contracts.push(contract) + }) + return contracts + } + + getContractCompiler (name) { + return this.parentSelf._deps.compilersArtefacts[name] + } +} + +module.exports = DropdownLogic From 3d90140b25e96e43fe51a3b5392177c506f71db9 Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Fri, 28 Dec 2018 14:08:03 -0500 Subject: [PATCH 33/50] remove excessive coupling with structure of contract object --- src/app/tabs/runTab/contractDropdown.js | 37 +++++++++------------- src/app/tabs/runTab/model/dropdownlogic.js | 20 ++++++++++-- 2 files changed, 33 insertions(+), 24 deletions(-) diff --git a/src/app/tabs/runTab/contractDropdown.js b/src/app/tabs/runTab/contractDropdown.js index f1157aaa04..d602f86333 100644 --- a/src/app/tabs/runTab/contractDropdown.js +++ b/src/app/tabs/runTab/contractDropdown.js @@ -100,8 +100,8 @@ class ContractDropdownUI { } var selectedContract = this.getSelectedContract() - var ctrabi = txHelper.getConstructorInterface(selectedContract.contract.object.abi) - var ctrEVMbc = selectedContract.contract.object.evm.bytecode.object + var ctrabi = txHelper.getConstructorInterface(selectedContract.abi) + var ctrEVMbc = selectedContract.bytecodeObject var createConstructorInstance = new MultiParamManager(0, ctrabi, (valArray, inputsValues) => { this.createInstance(inputsValues, selectedContract.compiler) }, txHelper.inputParametersDeclarationToString(ctrabi.inputs), 'Deploy', ctrEVMbc) @@ -111,23 +111,17 @@ class ContractDropdownUI { getSelectedContract () { var contract = this.selectContractNames.children[this.selectContractNames.selectedIndex] var contractName = contract.innerHTML - var compiler = this.dropdownLogic.getContractCompiler(contract.getAttribute('compiler')) - if (!compiler) return null + var compilerAtributeName = contract.getAttribute('compiler') - if (!contractName) return null - return { - name: contractName, - contract: compiler.getContract(contractName), - compiler - } + return this.dropdownLogic.getSelectedContract(contractName, compilerAtributeName) } createInstanceCallback (selectedContract, data) { this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} pending...`) if (data) { data.contractName = selectedContract.name - data.linkReferences = selectedContract.contract.object.evm.bytecode.linkReferences - data.contractABI = selectedContract.contract.object.abi + data.linkReferences = selectedContract.bytecodeLinkReferences + data.contractABI = selectedContract.abi } this.parentSelf._deps.udapp.createContract(data, @@ -226,7 +220,7 @@ class ContractDropdownUI { } this.event.trigger('clearInstance') var address = isVM ? txResult.result.createdAddress : txResult.result.contractAddress - this.event.trigger('newContractInstanceAdded', [selectedContract.contract.object, address, this.selectContractNames.value]) + this.event.trigger('newContractInstanceAdded', [selectedContract, address, this.selectContractNames.value]) } ) } @@ -235,17 +229,16 @@ class ContractDropdownUI { createInstance (args, compiler) { var selectedContract = this.getSelectedContract() - if (selectedContract.contract.object.evm.bytecode.object.length === 0) { - modalDialogCustom.alert('This contract may be abstract, not implement an abstract parent\'s methods completely or not invoke an inherited contract\'s constructor correctly.') - return + if (selectedContract.bytecodeObject.length === 0) { + return modalDialogCustom.alert('This contract may be abstract, not implement an abstract parent\'s methods completely or not invoke an inherited contract\'s constructor correctly.') } var forceSend = () => { - var constructor = txHelper.getConstructorInterface(selectedContract.contract.object.abi) + var constructor = txHelper.getConstructorInterface(selectedContract.abi) this.parentSelf._deps.filePanel.compilerMetadata().deployMetadataOf(selectedContract.name, (error, contractMetadata) => { if (error) return this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} errored: ` + error) if (!contractMetadata || (contractMetadata && contractMetadata.autoDeployLib)) { - txFormat.buildData(selectedContract.name, selectedContract.contract.object, compiler.getContracts(), true, constructor, args, (error, data) => { + txFormat.buildData(selectedContract.name, selectedContract.object, compiler.getContracts(), true, constructor, args, (error, data) => { if (error) return this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} errored: ` + error) this.createInstanceCallback(selectedContract, data) }, (msg) => { @@ -312,8 +305,8 @@ class ContractDropdownUI { runTxCallback) }) } else { - if (Object.keys(selectedContract.contract.object.evm.bytecode.linkReferences).length) this.parentSelf._deps.logCallback(`linking ${JSON.stringify(selectedContract.contract.object.evm.bytecode.linkReferences, null, '\t')} using ${JSON.stringify(contractMetadata.linkReferences, null, '\t')}`) - txFormat.encodeConstructorCallAndLinkLibraries(selectedContract.contract.object, args, constructor, contractMetadata.linkReferences, selectedContract.contract.object.evm.bytecode.linkReferences, (error, data) => { + if (Object.keys(selectedContract.bytecodeLinkReferences).length) this.parentSelf._deps.logCallback(`linking ${JSON.stringify(selectedContract.bytecodeLinkReferences, null, '\t')} using ${JSON.stringify(contractMetadata.linkReferences, null, '\t')}`) + txFormat.encodeConstructorCallAndLinkLibraries(selectedContract.object, args, constructor, contractMetadata.linkReferences, selectedContract.bytecodeLinkReferences, (error, data) => { if (error) return this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} errored: ` + error) this.createInstanceCallback(selectedContract, data) }) @@ -321,7 +314,7 @@ class ContractDropdownUI { }) } - if (selectedContract.contract.object.evm.deployedBytecode && selectedContract.contract.object.evm.deployedBytecode.object.length / 2 > 24576) { + if (selectedContract.deployedBytecode && selectedContract.deployedBytecode.object.length / 2 > 24576) { modalDialog('Contract code size over limit', yo`
Contract creation initialization returns data with length of more than 24576 bytes. The deployment will likely fails.
More info: eip-170
`, @@ -356,7 +349,7 @@ class ContractDropdownUI { return this.event.trigger('newContractABIAdded', [abi, address]) } var selectedContract = this.getSelectedContract() - this.event.trigger('newContractInstanceAdded', [selectedContract.contract.object, address, this.selectContractNames.value]) + this.event.trigger('newContractInstanceAdded', [selectedContract.object, address, this.selectContractNames.value]) } ) } diff --git a/src/app/tabs/runTab/model/dropdownlogic.js b/src/app/tabs/runTab/model/dropdownlogic.js index fce17512d7..f5a2f4ab91 100644 --- a/src/app/tabs/runTab/model/dropdownlogic.js +++ b/src/app/tabs/runTab/model/dropdownlogic.js @@ -62,8 +62,24 @@ class DropdownLogic { return contracts } - getContractCompiler (name) { - return this.parentSelf._deps.compilersArtefacts[name] + getSelectedContract (contractName, compilerAtributeName) { + if (!contractName) return null + + var compiler = this.parentSelf._deps.compilersArtefacts[compilerAtributeName] + if (!compiler) return null + + var contract = compiler.getContract(contractName) + + return { + name: contractName, + contract: contract, + compiler: compiler, + abi: contract.object.abi, + bytecodeObject: contract.object.evm.bytecode.object, + bytecodeLinkReferences: contract.object.evm.bytecode.linkReferences, + object: contract.object, + deployedBytecode: contract.object.evm.deployedBytecode + } } } From fb6bad4add22aa48f0a699fe26b0297be42f605d Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Fri, 28 Dec 2018 14:52:10 -0500 Subject: [PATCH 34/50] remove execution context from contract dropdown --- src/app/tabs/runTab/contractDropdown.js | 45 +++++++++------------- src/app/tabs/runTab/model/dropdownlogic.js | 40 ++++++++++++++++++- 2 files changed, 58 insertions(+), 27 deletions(-) diff --git a/src/app/tabs/runTab/contractDropdown.js b/src/app/tabs/runTab/contractDropdown.js index d602f86333..d7eb237453 100644 --- a/src/app/tabs/runTab/contractDropdown.js +++ b/src/app/tabs/runTab/contractDropdown.js @@ -1,13 +1,10 @@ var yo = require('yo-yo') var css = require('../styles/run-tab-styles') -var executionContext = require('../../../execution-context') var modalDialogCustom = require('../../ui/modal-dialog-custom') var remixLib = require('remix-lib') var txExecution = remixLib.execution.txExecution var txFormat = remixLib.execution.txFormat -var txHelper = remixLib.execution.txHelper var EventManager = remixLib.EventManager -var typeConversion = remixLib.execution.typeConversion var confirmDialog = require('../../execution/confirmDialog') var modalDialog = require('../../ui/modaldialog') var MultiParamManager = require('../../../multiParamManager') @@ -100,11 +97,9 @@ class ContractDropdownUI { } var selectedContract = this.getSelectedContract() - var ctrabi = txHelper.getConstructorInterface(selectedContract.abi) - var ctrEVMbc = selectedContract.bytecodeObject - var createConstructorInstance = new MultiParamManager(0, ctrabi, (valArray, inputsValues) => { + var createConstructorInstance = new MultiParamManager(0, selectedContract.getConstructorInterface(), (valArray, inputsValues) => { this.createInstance(inputsValues, selectedContract.compiler) - }, txHelper.inputParametersDeclarationToString(ctrabi.inputs), 'Deploy', ctrEVMbc) + }, selectedContract.getConstructorInputs(), 'Deploy', selectedContract.bytecodeObject) this.createPanel.appendChild(createConstructorInstance.render()) } @@ -129,15 +124,15 @@ class ContractDropdownUI { if (network.name !== 'Main') { return continueTxExecution(null) } - var amount = executionContext.web3().fromWei(typeConversion.toInt(tx.value), 'ether') + var amount = this.dropdownLogic.fromWei(tx.value, true) var content = confirmDialog(tx, amount, gasEstimation, this.parentSelf, (gasPrice, cb) => { let txFeeText, priceStatus // TODO: this try catch feels like an anti pattern, can/should be // removed, but for now keeping the original logic try { - var fee = executionContext.web3().toBigNumber(tx.gas).mul(executionContext.web3().toBigNumber(executionContext.web3().toWei(gasPrice.toString(10), 'gwei'))) - txFeeText = ' ' + executionContext.web3().fromWei(fee.toString(10), 'ether') + ' Ether' + var fee = this.dropdownLogic.calculateFee(tx.gas, gasPrice, 'gwei') + txFeeText = ' ' + this.dropdownLogic.fromWei(fee) + ' Ether' priceStatus = true } catch (e) { txFeeText = ' Please fix this issue before sending any transaction. ' + e.message @@ -146,13 +141,13 @@ class ContractDropdownUI { cb(txFeeText, priceStatus) }, (cb) => { - executionContext.web3().eth.getGasPrice((error, gasPrice) => { + this.dropdownLogic.getGasPrice((error, gasPrice) => { var warnMessage = ' Please fix this issue before sending any transaction. ' if (error) { return cb('Unable to retrieve the current network gas price.' + warnMessage + error) } try { - var gasPriceValue = executionContext.web3().fromWei(gasPrice.toString(10), 'gwei') + var gasPriceValue = this.dropdownLogic.fromWei(gasPrice, false, 'gwei') cb(null, gasPriceValue) } catch (e) { cb(warnMessage + e.message, null, false) @@ -168,7 +163,7 @@ class ContractDropdownUI { if (!content.gasPriceStatus) { cancelCb('Given gas price is not correct') } else { - var gasPrice = executionContext.web3().toWei(content.querySelector('#gasprice').value, 'gwei') + var gasPrice = this.dropdownLogic.toWei(content.querySelector('#gasprice').value, 'gwei') continueTxExecution(gasPrice) } }}, { @@ -206,17 +201,15 @@ class ContractDropdownUI { if (error) { return this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} errored: ${error}`) } - var isVM = executionContext.isVM() + var isVM = this.dropdownLogic.isVM() if (isVM) { var vmError = txExecution.checkVMError(txResult) if (vmError.error) { - this.parentSelf._deps.logCallback(vmError.message) - return + return this.parentSelf._deps.logCallback(vmError.message) } } if (txResult.result.status && txResult.result.status === '0x0') { - this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} errored: transaction execution failed`) - return + return this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} errored: transaction execution failed`) } this.event.trigger('clearInstance') var address = isVM ? txResult.result.createdAddress : txResult.result.contractAddress @@ -234,7 +227,7 @@ class ContractDropdownUI { } var forceSend = () => { - var constructor = txHelper.getConstructorInterface(selectedContract.abi) + var constructor = selectedContract.getConstructorInterface() this.parentSelf._deps.filePanel.compilerMetadata().deployMetadataOf(selectedContract.name, (error, contractMetadata) => { if (error) return this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} errored: ` + error) if (!contractMetadata || (contractMetadata && contractMetadata.autoDeployLib)) { @@ -250,15 +243,15 @@ class ContractDropdownUI { if (network.name !== 'Main') { return continueTxExecution(null) } - var amount = executionContext.web3().fromWei(typeConversion.toInt(tx.value), 'ether') + var amount = this.dropdownLogic.fromWei(tx.value, true, 'ether') var content = confirmDialog(tx, amount, gasEstimation, this.parentSelf, (gasPrice, cb) => { let txFeeText, priceStatus // TODO: this try catch feels like an anti pattern, can/should be // removed, but for now keeping the original logic try { - var fee = executionContext.web3().toBigNumber(tx.gas).mul(executionContext.web3().toBigNumber(executionContext.web3().toWei(gasPrice.toString(10), 'gwei'))) - txFeeText = ' ' + executionContext.web3().fromWei(fee.toString(10), 'ether') + ' Ether' + var fee = this.dropdownLogic.calculateFee(tx.gas, gasPrice) + txFeeText = ' ' + this.dropdownLogic.fromWei(fee, false, 'ether') + ' Ether' priceStatus = true } catch (e) { txFeeText = ' Please fix this issue before sending any transaction. ' + e.message @@ -267,13 +260,13 @@ class ContractDropdownUI { cb(txFeeText, priceStatus) }, (cb) => { - executionContext.web3().eth.getGasPrice((error, gasPrice) => { + this.dropdownLogic.getGasPrice((error, gasPrice) => { var warnMessage = ' Please fix this issue before sending any transaction. ' if (error) { return cb('Unable to retrieve the current network gas price.' + warnMessage + error) } try { - var gasPriceValue = executionContext.web3().fromWei(gasPrice.toString(10), 'gwei') + var gasPriceValue = this.dropdownLogic.fromWei(gasPrice, false, 'gwei') cb(null, gasPriceValue) } catch (e) { cb(warnMessage + e.message, null, false) @@ -289,7 +282,7 @@ class ContractDropdownUI { if (!content.gasPriceStatus) { cancelCb('Given gas price is not correct') } else { - var gasPrice = executionContext.web3().toWei(content.querySelector('#gasprice').value, 'gwei') + var gasPrice = this.dropdownLogic.toWei(content.querySelector('#gasprice').value, 'gwei') continueTxExecution(gasPrice) } }}, { @@ -314,7 +307,7 @@ class ContractDropdownUI { }) } - if (selectedContract.deployedBytecode && selectedContract.deployedBytecode.object.length / 2 > 24576) { + if (selectedContract.isOverSizeLimit()) { modalDialog('Contract code size over limit', yo`
Contract creation initialization returns data with length of more than 24576 bytes. The deployment will likely fails.
More info: eip-170
`, diff --git a/src/app/tabs/runTab/model/dropdownlogic.js b/src/app/tabs/runTab/model/dropdownlogic.js index f5a2f4ab91..de525fb40f 100644 --- a/src/app/tabs/runTab/model/dropdownlogic.js +++ b/src/app/tabs/runTab/model/dropdownlogic.js @@ -1,5 +1,8 @@ var ethJSUtil = require('ethereumjs-util') var remixLib = require('remix-lib') +var txHelper = remixLib.execution.txHelper +var executionContext = remixLib.execution.executionContext +var typeConversion = remixLib.execution.typeConversion var CompilerAbstract = require('../../../compiler/compiler-abstract') var EventManager = remixLib.EventManager @@ -78,9 +81,44 @@ class DropdownLogic { bytecodeObject: contract.object.evm.bytecode.object, bytecodeLinkReferences: contract.object.evm.bytecode.linkReferences, object: contract.object, - deployedBytecode: contract.object.evm.deployedBytecode + deployedBytecode: contract.object.evm.deployedBytecode, + getConstructorInterface: () => { + return txHelper.getConstructorInterface(contract.object.abi) + }, + getConstructorInputs: () => { + var constructorInteface = txHelper.getConstructorInterface(contract.object.abi) + return txHelper.inputParametersDeclarationToString(constructorInteface.inputs) + }, + isOverSizeLimit: () => { + var deployedBytecode = contract.object.evm.deployedBytecode + return (deployedBytecode && deployedBytecode.object.length / 2 > 24576) + } } } + + fromWei (value, doTypeConversion, unit) { + if (doTypeConversion) { + return executionContext.web3().fromWei(typeConversion.toInt(value), unit || 'ether') + } + return executionContext.web3().fromWei(value.toString(10), unit || 'ether') + } + + toWei (value, unit) { + return executionContext.web3().toWei(value, unit || 'gwei') + } + + calculateFee (gas, gasPrice, unit) { + return executionContext.web3().toBigNumber(gas).mul(executionContext.web3().toBigNumber(executionContext.web3().toWei(gasPrice.toString(10), unit || 'gwei'))) + } + + getGasPrice (cb) { + return executionContext.web3().eth.getGasPrice(cb) + } + + isVM () { + return executionContext.isVM() + } + } module.exports = DropdownLogic From fb625c9d6eb49444c0ea71bc3fccddcb39c86213 Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Fri, 28 Dec 2018 15:56:02 -0500 Subject: [PATCH 35/50] move contract creation and transaction run from dropdown view into the dropdown logic module --- src/app/tabs/runTab/contractDropdown.js | 195 +++++---------------- src/app/tabs/runTab/model/dropdownlogic.js | 145 +++++++++++++++ 2 files changed, 184 insertions(+), 156 deletions(-) diff --git a/src/app/tabs/runTab/contractDropdown.js b/src/app/tabs/runTab/contractDropdown.js index d7eb237453..1b25a2fc92 100644 --- a/src/app/tabs/runTab/contractDropdown.js +++ b/src/app/tabs/runTab/contractDropdown.js @@ -2,7 +2,6 @@ var yo = require('yo-yo') var css = require('../styles/run-tab-styles') var modalDialogCustom = require('../../ui/modal-dialog-custom') var remixLib = require('remix-lib') -var txExecution = remixLib.execution.txExecution var txFormat = remixLib.execution.txFormat var EventManager = remixLib.EventManager var confirmDialog = require('../../execution/confirmDialog') @@ -111,114 +110,49 @@ class ContractDropdownUI { return this.dropdownLogic.getSelectedContract(contractName, compilerAtributeName) } + // =============== + // TODO: move this to DropdownLogic + // =============== createInstanceCallback (selectedContract, data) { this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} pending...`) - if (data) { - data.contractName = selectedContract.name - data.linkReferences = selectedContract.bytecodeLinkReferences - data.contractABI = selectedContract.abi - } - this.parentSelf._deps.udapp.createContract(data, - (network, tx, gasEstimation, continueTxExecution, cancelCb) => { - if (network.name !== 'Main') { - return continueTxExecution(null) - } - var amount = this.dropdownLogic.fromWei(tx.value, true) - var content = confirmDialog(tx, amount, gasEstimation, this.parentSelf, - (gasPrice, cb) => { - let txFeeText, priceStatus - // TODO: this try catch feels like an anti pattern, can/should be - // removed, but for now keeping the original logic - try { - var fee = this.dropdownLogic.calculateFee(tx.gas, gasPrice, 'gwei') - txFeeText = ' ' + this.dropdownLogic.fromWei(fee) + ' Ether' - priceStatus = true - } catch (e) { - txFeeText = ' Please fix this issue before sending any transaction. ' + e.message - priceStatus = false - } - cb(txFeeText, priceStatus) - }, - (cb) => { - this.dropdownLogic.getGasPrice((error, gasPrice) => { - var warnMessage = ' Please fix this issue before sending any transaction. ' - if (error) { - return cb('Unable to retrieve the current network gas price.' + warnMessage + error) - } - try { - var gasPriceValue = this.dropdownLogic.fromWei(gasPrice, false, 'gwei') - cb(null, gasPriceValue) - } catch (e) { - cb(warnMessage + e.message, null, false) - } - }) - } - ) - modalDialog('Confirm transaction', content, - { label: 'Confirm', + var continueCb = (error, continueTxExecution, cancelCb) => { + if (error) { + var msg = typeof error !== 'string' ? error.message : error + modalDialog('Gas estimation failed', yo`
Gas estimation errored with the following message (see below). + The transaction execution will likely fail. Do you want to force sending?
+ ${msg} +
`, + { + label: 'Send Transaction', fn: () => { - this.parentSelf._deps.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked) - // TODO: check if this is check is still valid given the refactor - if (!content.gasPriceStatus) { - cancelCb('Given gas price is not correct') - } else { - var gasPrice = this.dropdownLogic.toWei(content.querySelector('#gasprice').value, 'gwei') - continueTxExecution(gasPrice) - } + continueTxExecution() }}, { - label: 'Cancel', + label: 'Cancel Transaction', fn: () => { - return cancelCb('Transaction canceled by user.') + cancelCb() } }) - }, - (error, continueTxExecution, cancelCb) => { - if (error) { - var msg = typeof error !== 'string' ? error.message : error - modalDialog('Gas estimation failed', yo`
Gas estimation errored with the following message (see below). - The transaction execution will likely fail. Do you want to force sending?
- ${msg} -
`, - { - label: 'Send Transaction', - fn: () => { - continueTxExecution() - }}, { - label: 'Cancel Transaction', - fn: () => { - cancelCb() - } - }) - } else { - continueTxExecution() - } - }, - function (okCb, cancelCb) { - modalDialogCustom.promptPassphrase(null, 'Personal mode is enabled. Please provide passphrase of account', '', okCb, cancelCb) - }, - (error, txResult) => { - if (error) { - return this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} errored: ${error}`) - } - var isVM = this.dropdownLogic.isVM() - if (isVM) { - var vmError = txExecution.checkVMError(txResult) - if (vmError.error) { - return this.parentSelf._deps.logCallback(vmError.message) - } - } - if (txResult.result.status && txResult.result.status === '0x0') { - return this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} errored: transaction execution failed`) - } - this.event.trigger('clearInstance') - var address = isVM ? txResult.result.createdAddress : txResult.result.contractAddress - this.event.trigger('newContractInstanceAdded', [selectedContract, address, this.selectContractNames.value]) + } else { + continueTxExecution() } - ) + } + + var promptCb = function (okCb, cancelCb) { + modalDialogCustom.promptPassphrase(null, 'Personal mode is enabled. Please provide passphrase of account', '', okCb, cancelCb) + } + + this.dropdownLogic.createContract(selectedContract, data, continueCb, promptCb, modalDialog, confirmDialog, (error, contractObject, address) => { + this.event.trigger('clearInstance') + + if (error) { + return this.parentSelf._deps.logCallback(error) + } + + this.event.trigger('newContractInstanceAdded', [contractObject, address, this.selectContractNames.value]) + }) } - // DEPLOY INSTANCE createInstance (args, compiler) { var selectedContract = this.getSelectedContract() @@ -226,6 +160,9 @@ class ContractDropdownUI { return modalDialogCustom.alert('This contract may be abstract, not implement an abstract parent\'s methods completely or not invoke an inherited contract\'s constructor correctly.') } + // =============== + // TODO: move this to DropdownLogic + // =============== var forceSend = () => { var constructor = selectedContract.getConstructorInterface() this.parentSelf._deps.filePanel.compilerMetadata().deployMetadataOf(selectedContract.name, (error, contractMetadata) => { @@ -237,65 +174,11 @@ class ContractDropdownUI { }, (msg) => { this.parentSelf._deps.logCallback(msg) }, (data, runTxCallback) => { + var promptCb = (okCb, cancelCb) => { + modalDialogCustom.promptPassphrase(null, 'Personal mode is enabled. Please provide passphrase of account', '', okCb, cancelCb) + } // called for libraries deployment - this.parentSelf._deps.udapp.runTx(data, - (network, tx, gasEstimation, continueTxExecution, cancelCb) => { - if (network.name !== 'Main') { - return continueTxExecution(null) - } - var amount = this.dropdownLogic.fromWei(tx.value, true, 'ether') - var content = confirmDialog(tx, amount, gasEstimation, this.parentSelf, - (gasPrice, cb) => { - let txFeeText, priceStatus - // TODO: this try catch feels like an anti pattern, can/should be - // removed, but for now keeping the original logic - try { - var fee = this.dropdownLogic.calculateFee(tx.gas, gasPrice) - txFeeText = ' ' + this.dropdownLogic.fromWei(fee, false, 'ether') + ' Ether' - priceStatus = true - } catch (e) { - txFeeText = ' Please fix this issue before sending any transaction. ' + e.message - priceStatus = false - } - cb(txFeeText, priceStatus) - }, - (cb) => { - this.dropdownLogic.getGasPrice((error, gasPrice) => { - var warnMessage = ' Please fix this issue before sending any transaction. ' - if (error) { - return cb('Unable to retrieve the current network gas price.' + warnMessage + error) - } - try { - var gasPriceValue = this.dropdownLogic.fromWei(gasPrice, false, 'gwei') - cb(null, gasPriceValue) - } catch (e) { - cb(warnMessage + e.message, null, false) - } - }) - } - ) - modalDialog('Confirm transaction', content, - { label: 'Confirm', - fn: () => { - this.parentSelf._deps.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked) - // TODO: check if this is check is still valid given the refactor - if (!content.gasPriceStatus) { - cancelCb('Given gas price is not correct') - } else { - var gasPrice = this.dropdownLogic.toWei(content.querySelector('#gasprice').value, 'gwei') - continueTxExecution(gasPrice) - } - }}, { - label: 'Cancel', - fn: () => { - return cancelCb('Transaction canceled by user.') - } - }) - }, - function (okCb, cancelCb) { - modalDialogCustom.promptPassphrase(null, 'Personal mode is enabled. Please provide passphrase of account', '', okCb, cancelCb) - }, - runTxCallback) + this.dropdownLogic.runTransaction(data, promptCb, modalDialog, confirmDialog, runTxCallback) }) } else { if (Object.keys(selectedContract.bytecodeLinkReferences).length) this.parentSelf._deps.logCallback(`linking ${JSON.stringify(selectedContract.bytecodeLinkReferences, null, '\t')} using ${JSON.stringify(contractMetadata.linkReferences, null, '\t')}`) diff --git a/src/app/tabs/runTab/model/dropdownlogic.js b/src/app/tabs/runTab/model/dropdownlogic.js index de525fb40f..cbd3f33bce 100644 --- a/src/app/tabs/runTab/model/dropdownlogic.js +++ b/src/app/tabs/runTab/model/dropdownlogic.js @@ -3,6 +3,7 @@ var remixLib = require('remix-lib') var txHelper = remixLib.execution.txHelper var executionContext = remixLib.execution.executionContext var typeConversion = remixLib.execution.typeConversion +var txExecution = remixLib.execution.txExecution var CompilerAbstract = require('../../../compiler/compiler-abstract') var EventManager = remixLib.EventManager @@ -119,6 +120,150 @@ class DropdownLogic { return executionContext.isVM() } + // TODO: check if selectedContract and data can be joined + createContract (selectedContract, data, continueCb, promptCb, confirmDialog, modalDialog, finalCb) { + if (data) { + data.contractName = selectedContract.name + data.linkReferences = selectedContract.bytecodeLinkReferences + data.contractABI = selectedContract.abi + } + + var confirmationCb = (network, tx, gasEstimation, continueTxExecution, cancelCb) => { + if (network.name !== 'Main') { + return continueTxExecution(null) + } + var amount = executionContext.web3().fromWei(typeConversion.toInt(tx.value), 'ether') + + // TODO: there is still a UI dependency to remove here, it's still too coupled at this point to remove easily + var content = confirmDialog(tx, amount, gasEstimation, this.recorder, + (gasPrice, cb) => { + let txFeeText, priceStatus + // TODO: this try catch feels like an anti pattern, can/should be + // removed, but for now keeping the original logic + try { + var fee = executionContext.web3().toBigNumber(tx.gas).mul(executionContext.web3().toBigNumber(executionContext.web3().toWei(gasPrice.toString(10), 'gwei'))) + txFeeText = ' ' + executionContext.web3().fromWei(fee.toString(10), 'ether') + ' Ether' + priceStatus = true + } catch (e) { + txFeeText = ' Please fix this issue before sending any transaction. ' + e.message + priceStatus = false + } + cb(txFeeText, priceStatus) + }, + (cb) => { + executionContext.web3().eth.getGasPrice((error, gasPrice) => { + var warnMessage = ' Please fix this issue before sending any transaction. ' + if (error) { + return cb('Unable to retrieve the current network gas price.' + warnMessage + error) + } + try { + var gasPriceValue = executionContext.web3().fromWei(gasPrice.toString(10), 'gwei') + cb(null, gasPriceValue) + } catch (e) { + cb(warnMessage + e.message, null, false) + } + }) + } + ) + modalDialog('Confirm transaction', content, + { label: 'Confirm', + fn: () => { + this.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked) + // TODO: check if this is check is still valid given the refactor + if (!content.gasPriceStatus) { + cancelCb('Given gas price is not correct') + } else { + var gasPrice = executionContext.web3().toWei(content.querySelector('#gasprice').value, 'gwei') + continueTxExecution(gasPrice) + } + }}, { + label: 'Cancel', + fn: () => { + return cancelCb('Transaction canceled by user.') + } + }) + } + + this.parentSelf._deps.udapp.createContract(data, confirmationCb, continueCb, promptCb, + (error, txResult) => { + if (error) { + return finalCb(`creation of ${selectedContract.name} errored: ${error}`) + } + var isVM = executionContext.isVM() + if (isVM) { + var vmError = txExecution.checkVMError(txResult) + if (vmError.error) { + return finalCb(vmError.message) + } + } + if (txResult.result.status && txResult.result.status === '0x0') { + return finalCb(`creation of ${selectedContract.name} errored: transaction execution failed`) + } + var address = isVM ? txResult.result.createdAddress : txResult.result.contractAddress + finalCb(null, selectedContract, address) + } + ) + } + + runTransaction (data, promptCb, modalDialog, confirmDialog, finalCb) { + var confirmationCb = (network, tx, gasEstimation, continueTxExecution, cancelCb) => { + if (network.name !== 'Main') { + return continueTxExecution(null) + } + var amount = this.fromWei(tx.value, true, 'ether') + var content = confirmDialog(tx, amount, gasEstimation, this.parentSelf, + (gasPrice, cb) => { + let txFeeText, priceStatus + // TODO: this try catch feels like an anti pattern, can/should be + // removed, but for now keeping the original logic + try { + var fee = this.calculateFee(tx.gas, gasPrice) + txFeeText = ' ' + this.fromWei(fee, false, 'ether') + ' Ether' + priceStatus = true + } catch (e) { + txFeeText = ' Please fix this issue before sending any transaction. ' + e.message + priceStatus = false + } + cb(txFeeText, priceStatus) + }, + (cb) => { + this.getGasPrice((error, gasPrice) => { + var warnMessage = ' Please fix this issue before sending any transaction. ' + if (error) { + return cb('Unable to retrieve the current network gas price.' + warnMessage + error) + } + try { + var gasPriceValue = this.fromWei(gasPrice, false, 'gwei') + cb(null, gasPriceValue) + } catch (e) { + cb(warnMessage + e.message, null, false) + } + }) + } + ) + modalDialog('Confirm transaction', content, + { label: 'Confirm', + fn: () => { + this.parentSelf._deps.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked) + // TODO: check if this is check is still valid given the refactor + if (!content.gasPriceStatus) { + cancelCb('Given gas price is not correct') + } else { + var gasPrice = this.toWei(content.querySelector('#gasprice').value, 'gwei') + continueTxExecution(gasPrice) + } + }}, { + label: 'Cancel', + fn: () => { + return cancelCb('Transaction canceled by user.') + } + } + ) + } + + this.parentSelf._deps.udapp.runTx(data, confirmationCb, promptCb, finalCb) + } + } module.exports = DropdownLogic From 85b8863c66773ef6663c6c959adaa2bb0fd7309d Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Mon, 31 Dec 2018 12:02:19 -0500 Subject: [PATCH 36/50] refactor, move create instance from dropdown ui to logic class --- src/app/tabs/runTab/contractDropdown.js | 63 +++++----------------- src/app/tabs/runTab/model/dropdownlogic.js | 29 ++++++++++ 2 files changed, 41 insertions(+), 51 deletions(-) diff --git a/src/app/tabs/runTab/contractDropdown.js b/src/app/tabs/runTab/contractDropdown.js index 1b25a2fc92..1c612fdc05 100644 --- a/src/app/tabs/runTab/contractDropdown.js +++ b/src/app/tabs/runTab/contractDropdown.js @@ -2,7 +2,6 @@ var yo = require('yo-yo') var css = require('../styles/run-tab-styles') var modalDialogCustom = require('../../ui/modal-dialog-custom') var remixLib = require('remix-lib') -var txFormat = remixLib.execution.txFormat var EventManager = remixLib.EventManager var confirmDialog = require('../../execution/confirmDialog') var modalDialog = require('../../ui/modaldialog') @@ -97,7 +96,7 @@ class ContractDropdownUI { var selectedContract = this.getSelectedContract() var createConstructorInstance = new MultiParamManager(0, selectedContract.getConstructorInterface(), (valArray, inputsValues) => { - this.createInstance(inputsValues, selectedContract.compiler) + this.createInstance(inputsValues) }, selectedContract.getConstructorInputs(), 'Deploy', selectedContract.bytecodeObject) this.createPanel.appendChild(createConstructorInstance.render()) } @@ -110,11 +109,12 @@ class ContractDropdownUI { return this.dropdownLogic.getSelectedContract(contractName, compilerAtributeName) } - // =============== - // TODO: move this to DropdownLogic - // =============== - createInstanceCallback (selectedContract, data) { - this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} pending...`) + createInstance (args) { + var selectedContract = this.getSelectedContract() + + if (selectedContract.bytecodeObject.length === 0) { + return modalDialogCustom.alert('This contract may be abstract, not implement an abstract parent\'s methods completely or not invoke an inherited contract\'s constructor correctly.') + } var continueCb = (error, continueTxExecution, cancelCb) => { if (error) { @@ -138,11 +138,11 @@ class ContractDropdownUI { } } - var promptCb = function (okCb, cancelCb) { + var promptCb = (okCb, cancelCb) => { modalDialogCustom.promptPassphrase(null, 'Personal mode is enabled. Please provide passphrase of account', '', okCb, cancelCb) } - this.dropdownLogic.createContract(selectedContract, data, continueCb, promptCb, modalDialog, confirmDialog, (error, contractObject, address) => { + var finalCb = (error, contractObject, address) => { this.event.trigger('clearInstance') if (error) { @@ -150,63 +150,24 @@ class ContractDropdownUI { } this.event.trigger('newContractInstanceAdded', [contractObject, address, this.selectContractNames.value]) - }) - } - - createInstance (args, compiler) { - var selectedContract = this.getSelectedContract() - - if (selectedContract.bytecodeObject.length === 0) { - return modalDialogCustom.alert('This contract may be abstract, not implement an abstract parent\'s methods completely or not invoke an inherited contract\'s constructor correctly.') - } - - // =============== - // TODO: move this to DropdownLogic - // =============== - var forceSend = () => { - var constructor = selectedContract.getConstructorInterface() - this.parentSelf._deps.filePanel.compilerMetadata().deployMetadataOf(selectedContract.name, (error, contractMetadata) => { - if (error) return this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} errored: ` + error) - if (!contractMetadata || (contractMetadata && contractMetadata.autoDeployLib)) { - txFormat.buildData(selectedContract.name, selectedContract.object, compiler.getContracts(), true, constructor, args, (error, data) => { - if (error) return this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} errored: ` + error) - this.createInstanceCallback(selectedContract, data) - }, (msg) => { - this.parentSelf._deps.logCallback(msg) - }, (data, runTxCallback) => { - var promptCb = (okCb, cancelCb) => { - modalDialogCustom.promptPassphrase(null, 'Personal mode is enabled. Please provide passphrase of account', '', okCb, cancelCb) - } - // called for libraries deployment - this.dropdownLogic.runTransaction(data, promptCb, modalDialog, confirmDialog, runTxCallback) - }) - } else { - if (Object.keys(selectedContract.bytecodeLinkReferences).length) this.parentSelf._deps.logCallback(`linking ${JSON.stringify(selectedContract.bytecodeLinkReferences, null, '\t')} using ${JSON.stringify(contractMetadata.linkReferences, null, '\t')}`) - txFormat.encodeConstructorCallAndLinkLibraries(selectedContract.object, args, constructor, contractMetadata.linkReferences, selectedContract.bytecodeLinkReferences, (error, data) => { - if (error) return this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} errored: ` + error) - this.createInstanceCallback(selectedContract, data) - }) - } - }) } if (selectedContract.isOverSizeLimit()) { - modalDialog('Contract code size over limit', yo`
Contract creation initialization returns data with length of more than 24576 bytes. The deployment will likely fails.
+ return modalDialog('Contract code size over limit', yo`
Contract creation initialization returns data with length of more than 24576 bytes. The deployment will likely fails.
More info: eip-170
`, { label: 'Force Send', fn: () => { - forceSend() + this.dropdownLogic.forceSend(selectedContract, args, continueCb, promptCb, modalDialogCustom, confirmDialog, finalCb) }}, { label: 'Cancel', fn: () => { this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} canceled by user.`) } }) - } else { - forceSend() } + this.dropdownLogic.forceSend(selectedContract, args, continueCb, promptCb, modalDialogCustom, confirmDialog, finalCb) } loadFromAddress () { diff --git a/src/app/tabs/runTab/model/dropdownlogic.js b/src/app/tabs/runTab/model/dropdownlogic.js index cbd3f33bce..e1e023eb1a 100644 --- a/src/app/tabs/runTab/model/dropdownlogic.js +++ b/src/app/tabs/runTab/model/dropdownlogic.js @@ -1,6 +1,7 @@ var ethJSUtil = require('ethereumjs-util') var remixLib = require('remix-lib') var txHelper = remixLib.execution.txHelper +var txFormat = remixLib.execution.txFormat var executionContext = remixLib.execution.executionContext var typeConversion = remixLib.execution.typeConversion var txExecution = remixLib.execution.txExecution @@ -264,6 +265,34 @@ class DropdownLogic { this.parentSelf._deps.udapp.runTx(data, confirmationCb, promptCb, finalCb) } + forceSend (selectedContract, args, continueCb, promptCb, modalDialog, confirmDialog, cb) { + var constructor = selectedContract.getConstructorInterface() + this.parentSelf._deps.filePanel.compilerMetadata().deployMetadataOf(selectedContract.name, (error, contractMetadata) => { + if (error) return this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} errored: ` + error) + if (!contractMetadata || (contractMetadata && contractMetadata.autoDeployLib)) { + txFormat.buildData(selectedContract.name, selectedContract.object, selectedContract.compiler.getContracts(), true, constructor, args, (error, data) => { + if (error) return this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} errored: ` + error) + + this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} pending...`) + this.createContract(selectedContract, data, continueCb, promptCb, modalDialog, confirmDialog, cb) + }, (msg) => { + this.parentSelf._deps.logCallback(msg) + }, (data, runTxCallback) => { + // called for libraries deployment + this.runTransaction(data, promptCb, modalDialog, confirmDialog, runTxCallback) + }) + } else { + if (Object.keys(selectedContract.bytecodeLinkReferences).length) this.parentSelf._deps.logCallback(`linking ${JSON.stringify(selectedContract.bytecodeLinkReferences, null, '\t')} using ${JSON.stringify(contractMetadata.linkReferences, null, '\t')}`) + txFormat.encodeConstructorCallAndLinkLibraries(selectedContract.object, args, constructor, contractMetadata.linkReferences, selectedContract.bytecodeLinkReferences, (error, data) => { + if (error) return this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} errored: ` + error) + + this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} pending...`) + this.createContract(selectedContract, data, continueCb, promptCb, modalDialog, confirmDialog, cb) + }) + } + }) + } + } module.exports = DropdownLogic From 825ca33c2d506fe0f0905dd1e504f92205a112a0 Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Mon, 31 Dec 2018 12:36:03 -0500 Subject: [PATCH 37/50] remove need for self in dropdown logic --- src/app/execution/confirmDialog.js | 1 + src/app/files/compiler-metadata.js | 1 + src/app/tabs/run-tab.js | 11 +++- src/app/tabs/runTab/contractDropdown.js | 8 ++- src/app/tabs/runTab/model/dropdownlogic.js | 73 ++++++++++++---------- 5 files changed, 57 insertions(+), 37 deletions(-) diff --git a/src/app/execution/confirmDialog.js b/src/app/execution/confirmDialog.js index c8921ac3a8..c139cbf6c1 100644 --- a/src/app/execution/confirmDialog.js +++ b/src/app/execution/confirmDialog.js @@ -16,6 +16,7 @@ var css = csjs` } ` +// TODO: self is not actually used and can be removed function confirmDialog (tx, amount, gasEstimation, self, newGasPriceCb, initialParamsCb) { var onGasPriceChange = function () { var gasPrice = el.querySelector('#gasprice').value diff --git a/src/app/files/compiler-metadata.js b/src/app/files/compiler-metadata.js index 3790651c3a..59959aae9e 100644 --- a/src/app/files/compiler-metadata.js +++ b/src/app/files/compiler-metadata.js @@ -74,6 +74,7 @@ class CompilerMetadata { return metadata } + // TODO: is only called by dropdownLogic and can be moved there deployMetadataOf (contractName, callback) { var self = this var provider = self._opts.fileManager.currentFileProvider() diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index d3b90c34db..e9c4539c51 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -142,7 +142,16 @@ function runTab (opts, localRegistry) { this.event.trigger('clearInstance', []) }) - var dropdownLogic = new DropdownLogic(self) + var dropdownLogic = new DropdownLogic( + this.parentSelf._deps.fileManager, + this.parentSelf._deps.pluginManager, + this.parentSelf._deps.compilersArtefacts, + this.parentSelf._deps.compiler, + this.parentSelf._deps.config, + this.parentSelf._deps.editor, + this.parentSelf._deps.udapp, + this.parentSelf._deps.filePanel + ) var contractDropdownUI = new ContractDropdownUI(dropdownLogic, self) contractDropdownUI.event.register('clearInstance', () => { diff --git a/src/app/tabs/runTab/contractDropdown.js b/src/app/tabs/runTab/contractDropdown.js index 1c612fdc05..588db7c77d 100644 --- a/src/app/tabs/runTab/contractDropdown.js +++ b/src/app/tabs/runTab/contractDropdown.js @@ -142,6 +142,10 @@ class ContractDropdownUI { modalDialogCustom.promptPassphrase(null, 'Personal mode is enabled. Please provide passphrase of account', '', okCb, cancelCb) } + var statusCb = (msg) => { + return this.parentSelf._deps.logCallback(msg) + } + var finalCb = (error, contractObject, address) => { this.event.trigger('clearInstance') @@ -159,7 +163,7 @@ class ContractDropdownUI { { label: 'Force Send', fn: () => { - this.dropdownLogic.forceSend(selectedContract, args, continueCb, promptCb, modalDialogCustom, confirmDialog, finalCb) + this.dropdownLogic.forceSend(selectedContract, args, continueCb, promptCb, modalDialogCustom, confirmDialog, statusCb, finalCb) }}, { label: 'Cancel', fn: () => { @@ -167,7 +171,7 @@ class ContractDropdownUI { } }) } - this.dropdownLogic.forceSend(selectedContract, args, continueCb, promptCb, modalDialogCustom, confirmDialog, finalCb) + this.dropdownLogic.forceSend(selectedContract, args, continueCb, promptCb, modalDialogCustom, confirmDialog, statusCb, finalCb) } loadFromAddress () { diff --git a/src/app/tabs/runTab/model/dropdownlogic.js b/src/app/tabs/runTab/model/dropdownlogic.js index e1e023eb1a..d096777a66 100644 --- a/src/app/tabs/runTab/model/dropdownlogic.js +++ b/src/app/tabs/runTab/model/dropdownlogic.js @@ -9,32 +9,39 @@ var CompilerAbstract = require('../../../compiler/compiler-abstract') var EventManager = remixLib.EventManager class DropdownLogic { - constructor (parentSelf) { - this.parentSelf = parentSelf + constructor (fileManager, pluginManager, compilersArtefacts, compiler, config, editor, udapp, filePanel) { + this.pluginManager = pluginManager + this.compilersArtefacts = compilersArtefacts + this.compiler = compiler + this.config = config + this.editor = editor + this.udapp = udapp + this.filePanel = filePanel + this.event = new EventManager() this.listenToCompilationEvents() - this.parentSelf._deps.fileManager.event.register('currentFileChanged', (currentFile) => { + fileManager.event.register('currentFileChanged', (currentFile) => { this.event.trigger('currentFileChanged', [currentFile]) }) } listenToCompilationEvents () { - this.parentSelf._deps.pluginManager.event.register('sendCompilationResult', (file, source, languageVersion, data) => { + this.pluginManager.event.register('sendCompilationResult', (file, source, languageVersion, data) => { // TODO check whether the tab is configured let compiler = new CompilerAbstract(languageVersion, data) - this.parentSelf._deps.compilersArtefacts[languageVersion] = compiler - this.parentSelf._deps.compilersArtefacts['__last'] = compiler + this.compilersArtefacts[languageVersion] = compiler + this.compilersArtefacts['__last'] = compiler this.event.trigger('newlyCompiled', [true, data, source, compiler, languageVersion]) }) - this.parentSelf._deps.compiler.event.register('compilationFinished', (success, data, source) => { + this.compiler.event.register('compilationFinished', (success, data, source) => { var name = 'solidity' let compiler = new CompilerAbstract(name, data) - this.parentSelf._deps.compilersArtefacts[name] = compiler - this.parentSelf._deps.compilersArtefacts['__last'] = compiler - this.event.trigger('newlyCompiled', [success, data, source, this.parentSelf._deps.compiler, name]) + this.compilersArtefacts[name] = compiler + this.compilersArtefacts['__last'] = compiler + this.event.trigger('newlyCompiled', [success, data, source, this.compiler, name]) }) } @@ -45,11 +52,11 @@ class DropdownLogic { if (/[a-f]/.test(address) && /[A-F]/.test(address) && !ethJSUtil.isValidChecksumAddress(address)) { return cb('Invalid checksum address.') } - if (/.(.abi)$/.exec(this.parentSelf._deps.config.get('currentFile'))) { + if (/.(.abi)$/.exec(this.config.get('currentFile'))) { confirmCb(() => { var abi try { - abi = JSON.parse(this.parentSelf._deps.editor.currentContent()) + abi = JSON.parse(this.editor.currentContent()) } catch (e) { return cb('Failed to parse the current file as JSON ABI.') } @@ -70,7 +77,7 @@ class DropdownLogic { getSelectedContract (contractName, compilerAtributeName) { if (!contractName) return null - var compiler = this.parentSelf._deps.compilersArtefacts[compilerAtributeName] + var compiler = this.compilersArtefacts[compilerAtributeName] if (!compiler) return null var contract = compiler.getContract(contractName) @@ -185,7 +192,7 @@ class DropdownLogic { }) } - this.parentSelf._deps.udapp.createContract(data, confirmationCb, continueCb, promptCb, + this.udapp.createContract(data, confirmationCb, continueCb, promptCb, (error, txResult) => { if (error) { return finalCb(`creation of ${selectedContract.name} errored: ${error}`) @@ -212,7 +219,7 @@ class DropdownLogic { return continueTxExecution(null) } var amount = this.fromWei(tx.value, true, 'ether') - var content = confirmDialog(tx, amount, gasEstimation, this.parentSelf, + var content = confirmDialog(tx, amount, gasEstimation, null, (gasPrice, cb) => { let txFeeText, priceStatus // TODO: this try catch feels like an anti pattern, can/should be @@ -245,7 +252,7 @@ class DropdownLogic { modalDialog('Confirm transaction', content, { label: 'Confirm', fn: () => { - this.parentSelf._deps.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked) + this.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked) // TODO: check if this is check is still valid given the refactor if (!content.gasPriceStatus) { cancelCb('Given gas price is not correct') @@ -262,34 +269,32 @@ class DropdownLogic { ) } - this.parentSelf._deps.udapp.runTx(data, confirmationCb, promptCb, finalCb) + this.udapp.runTx(data, confirmationCb, promptCb, finalCb) } - forceSend (selectedContract, args, continueCb, promptCb, modalDialog, confirmDialog, cb) { + forceSend (selectedContract, args, continueCb, promptCb, modalDialog, confirmDialog, statusCb, cb) { var constructor = selectedContract.getConstructorInterface() - this.parentSelf._deps.filePanel.compilerMetadata().deployMetadataOf(selectedContract.name, (error, contractMetadata) => { - if (error) return this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} errored: ` + error) + // TODO: deployMetadataOf can be moved here + this.filePanel.compilerMetadata().deployMetadataOf(selectedContract.name, (error, contractMetadata) => { + if (error) return statusCb(`creation of ${selectedContract.name} errored: ` + error) if (!contractMetadata || (contractMetadata && contractMetadata.autoDeployLib)) { - txFormat.buildData(selectedContract.name, selectedContract.object, selectedContract.compiler.getContracts(), true, constructor, args, (error, data) => { - if (error) return this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} errored: ` + error) + return txFormat.buildData(selectedContract.name, selectedContract.object, selectedContract.compiler.getContracts(), true, constructor, args, (error, data) => { + if (error) return statusCb(`creation of ${selectedContract.name} errored: ` + error) - this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} pending...`) + statusCb(`creation of ${selectedContract.name} pending...`) this.createContract(selectedContract, data, continueCb, promptCb, modalDialog, confirmDialog, cb) - }, (msg) => { - this.parentSelf._deps.logCallback(msg) - }, (data, runTxCallback) => { + }, statusCb, (data, runTxCallback) => { // called for libraries deployment this.runTransaction(data, promptCb, modalDialog, confirmDialog, runTxCallback) }) - } else { - if (Object.keys(selectedContract.bytecodeLinkReferences).length) this.parentSelf._deps.logCallback(`linking ${JSON.stringify(selectedContract.bytecodeLinkReferences, null, '\t')} using ${JSON.stringify(contractMetadata.linkReferences, null, '\t')}`) - txFormat.encodeConstructorCallAndLinkLibraries(selectedContract.object, args, constructor, contractMetadata.linkReferences, selectedContract.bytecodeLinkReferences, (error, data) => { - if (error) return this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} errored: ` + error) - - this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} pending...`) - this.createContract(selectedContract, data, continueCb, promptCb, modalDialog, confirmDialog, cb) - }) } + if (Object.keys(selectedContract.bytecodeLinkReferences).length) statusCb(`linking ${JSON.stringify(selectedContract.bytecodeLinkReferences, null, '\t')} using ${JSON.stringify(contractMetadata.linkReferences, null, '\t')}`) + txFormat.encodeConstructorCallAndLinkLibraries(selectedContract.object, args, constructor, contractMetadata.linkReferences, selectedContract.bytecodeLinkReferences, (error, data) => { + if (error) return statusCb(`creation of ${selectedContract.name} errored: ` + error) + + statusCb(`creation of ${selectedContract.name} pending...`) + this.createContract(selectedContract, data, continueCb, promptCb, modalDialog, confirmDialog, cb) + }) }) } From 4f8c3099316ee2d3692ac82f093f8817bf479031 Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Mon, 31 Dec 2018 12:38:51 -0500 Subject: [PATCH 38/50] remove need for parent self in contract dropdown ui --- src/app/tabs/run-tab.js | 18 +++++++++--------- src/app/tabs/runTab/contractDropdown.js | 10 +++++----- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index e9c4539c51..576865c8ad 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -143,16 +143,16 @@ function runTab (opts, localRegistry) { }) var dropdownLogic = new DropdownLogic( - this.parentSelf._deps.fileManager, - this.parentSelf._deps.pluginManager, - this.parentSelf._deps.compilersArtefacts, - this.parentSelf._deps.compiler, - this.parentSelf._deps.config, - this.parentSelf._deps.editor, - this.parentSelf._deps.udapp, - this.parentSelf._deps.filePanel + this._deps.fileManager, + this._deps.pluginManager, + this._deps.compilersArtefacts, + this._deps.compiler, + this._deps.config, + this._deps.editor, + this._deps.udapp, + this._deps.filePanel ) - var contractDropdownUI = new ContractDropdownUI(dropdownLogic, self) + var contractDropdownUI = new ContractDropdownUI(dropdownLogic, this._deps.logCallback) contractDropdownUI.event.register('clearInstance', () => { var noInstancesText = this._view.noInstancesText diff --git a/src/app/tabs/runTab/contractDropdown.js b/src/app/tabs/runTab/contractDropdown.js index 588db7c77d..5d4fd60aec 100644 --- a/src/app/tabs/runTab/contractDropdown.js +++ b/src/app/tabs/runTab/contractDropdown.js @@ -8,9 +8,9 @@ var modalDialog = require('../../ui/modaldialog') var MultiParamManager = require('../../../multiParamManager') class ContractDropdownUI { - constructor (dropdownLogic, parentSelf) { - this.parentSelf = parentSelf + constructor (dropdownLogic, logCallback) { this.dropdownLogic = dropdownLogic + this.logCallback = logCallback this.event = new EventManager() this.listenToEvents() @@ -143,14 +143,14 @@ class ContractDropdownUI { } var statusCb = (msg) => { - return this.parentSelf._deps.logCallback(msg) + return this.logCallback(msg) } var finalCb = (error, contractObject, address) => { this.event.trigger('clearInstance') if (error) { - return this.parentSelf._deps.logCallback(error) + return this.logCallback(error) } this.event.trigger('newContractInstanceAdded', [contractObject, address, this.selectContractNames.value]) @@ -167,7 +167,7 @@ class ContractDropdownUI { }}, { label: 'Cancel', fn: () => { - this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} canceled by user.`) + this.logCallback(`creation of ${selectedContract.name} canceled by user.`) } }) } From 07d2e60f12b80051ae7bc821546a3c917e43a0fd Mon Sep 17 00:00:00 2001 From: yann300 Date: Fri, 4 Jan 2019 14:31:57 +0100 Subject: [PATCH 39/50] fix using compilersartefacts --- src/app.js | 2 +- src/app/panels/righthand-panel.js | 2 +- src/app/tabs/run-tab.js | 2 +- src/app/tabs/runTab/model/dropdownlogic.js | 12 ++---------- src/universal-dapp-ui.js | 8 ++++---- src/universal-dapp.js | 5 ++--- 6 files changed, 11 insertions(+), 20 deletions(-) diff --git a/src/app.js b/src/app.js index e72736fb8c..4ebd885dfe 100644 --- a/src/app.js +++ b/src/app.js @@ -313,7 +313,7 @@ Please make a backup of your contracts and start using http://remix.ethereum.org registry.put({api: self._components.compilersArtefacts, name: 'compilersartefacts'}) // ----------------- UniversalDApp ----------------- - var udapp = new UniversalDApp(registry.get('config').api) + var udapp = new UniversalDApp(registry) // TODO: to remove when possible registry.put({api: udapp, name: 'udapp'}) udapp.event.register('transactionBroadcasted', (txhash, networkName) => { diff --git a/src/app/panels/righthand-panel.js b/src/app/panels/righthand-panel.js index 723339d585..0bcb4f9877 100644 --- a/src/app/panels/righthand-panel.js +++ b/src/app/panels/righthand-panel.js @@ -47,7 +47,7 @@ const css = csjs` ` class RighthandPanel { - constructor (localRegistry) { + constructor ({pluginManager, tabs}, localRegistry) { const self = this self._components = {} self._components.registry = localRegistry || globalRegistry diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index 576865c8ad..3fc3a85a34 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -9,7 +9,7 @@ var css = require('./styles/run-tab-styles') var Settings = require('./runTab/model/settings.js') var SettingsUI = require('./runTab/settings.js') -var DropdownLogic = require('./runTab/model/dropdownLogic.js') +var DropdownLogic = require('./runTab/model/dropdownlogic.js') var ContractDropdownUI = require('./runTab/contractDropdown.js') var Recorder = require('./runTab/model/recorder.js') diff --git a/src/app/tabs/runTab/model/dropdownlogic.js b/src/app/tabs/runTab/model/dropdownlogic.js index d096777a66..112d6ede96 100644 --- a/src/app/tabs/runTab/model/dropdownlogic.js +++ b/src/app/tabs/runTab/model/dropdownlogic.js @@ -30,19 +30,11 @@ class DropdownLogic { listenToCompilationEvents () { this.pluginManager.event.register('sendCompilationResult', (file, source, languageVersion, data) => { // TODO check whether the tab is configured - let compiler = new CompilerAbstract(languageVersion, data) + let compiler = new CompilerAbstract(languageVersion, data, source) this.compilersArtefacts[languageVersion] = compiler this.compilersArtefacts['__last'] = compiler this.event.trigger('newlyCompiled', [true, data, source, compiler, languageVersion]) }) - - this.compiler.event.register('compilationFinished', (success, data, source) => { - var name = 'solidity' - let compiler = new CompilerAbstract(name, data) - this.compilersArtefacts[name] = compiler - this.compilersArtefacts['__last'] = compiler - this.event.trigger('newlyCompiled', [success, data, source, this.compiler, name]) - }) } loadContractFromAddress (address, confirmCb, cb) { @@ -278,7 +270,7 @@ class DropdownLogic { this.filePanel.compilerMetadata().deployMetadataOf(selectedContract.name, (error, contractMetadata) => { if (error) return statusCb(`creation of ${selectedContract.name} errored: ` + error) if (!contractMetadata || (contractMetadata && contractMetadata.autoDeployLib)) { - return txFormat.buildData(selectedContract.name, selectedContract.object, selectedContract.compiler.getContracts(), true, constructor, args, (error, data) => { + return txFormat.buildData(selectedContract.name, selectedContract.object, this.compilersArtefacts['__last'].getData().contracts, true, constructor, args, (error, data) => { if (error) return statusCb(`creation of ${selectedContract.name} errored: ` + error) statusCb(`creation of ${selectedContract.name} pending...`) diff --git a/src/universal-dapp-ui.js b/src/universal-dapp-ui.js index 78e8682fcc..276abf5186 100644 --- a/src/universal-dapp-ui.js +++ b/src/universal-dapp-ui.js @@ -26,9 +26,9 @@ function UniversalDAppUI (udapp, registry) { this.registry = registry this.compilerData = {contractsDetails: {}} - registry.get('compiler').api.event.register('compilationFinished', (success, data, source) => { - this.compilerData.contractsDetails = success && data ? data.contracts : {} - }) + this._deps = { + compilersartefacts: registry.get('compilersartefacts').api + } } function decodeResponseToTreeView (response, fnabi) { @@ -220,7 +220,7 @@ UniversalDAppUI.prototype.getCallButton = function (args) { } // contractsDetails is used to resolve libraries - txFormat.buildData(args.contractName, args.contractAbi, self.compilerData.contractsDetails, false, args.funABI, args.funABI.type !== 'fallback' ? value : '', (error, data) => { + txFormat.buildData(args.contractName, args.contractAbi, self._deps.compilersartefacts['__last'].getData().contracts, false, args.funABI, args.funABI.type !== 'fallback' ? value : '', (error, data) => { if (!error) { if (!args.funABI.constant) { self.registry.get('logCallback').api(`${logMsg} pending ... `) diff --git a/src/universal-dapp.js b/src/universal-dapp.js index e3753a2d30..3342478c60 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -8,12 +8,11 @@ var txHelper = remixLib.execution.txHelper var EventManager = remixLib.EventManager var executionContext = remixLib.execution.executionContext -function UniversalDApp (config) { +function UniversalDApp (registry) { this.event = new EventManager() var self = this self._deps = { - config: globalRegistry.get('config').api, - compiler: globalRegistry.get('compiler').api + config: registry.get('config').api } self._txRunnerAPI = { config: self._deps.config, From edebc4a8941609c14219508cf6a07bee1fa5efd1 Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 8 Jan 2019 17:11:21 +0100 Subject: [PATCH 40/50] typo --- src/app/tabs/run-tab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index 3fc3a85a34..d624fc894d 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -81,7 +81,7 @@ function runTab (opts, localRegistry) { var container = yo`
` - var recorder = new Recorder(self._deps.udapp, self._deps.fileManager, self._deps.udapp.config) + var recorder = new Recorder(self._deps.udapp, self._deps.fileManager, self._deps.config) recorder.event.register('newTxRecorded', (count) => { this.data.count = count this._view.recorderCount.innerText = count From 296c28cb354fa1f369793fbb1e43ae1b47716c51 Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 8 Jan 2019 17:54:34 +0100 Subject: [PATCH 41/50] add missing callback --- src/app/tabs/runTab/model/dropdownlogic.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/tabs/runTab/model/dropdownlogic.js b/src/app/tabs/runTab/model/dropdownlogic.js index 112d6ede96..f05b5a9ef8 100644 --- a/src/app/tabs/runTab/model/dropdownlogic.js +++ b/src/app/tabs/runTab/model/dropdownlogic.js @@ -205,7 +205,7 @@ class DropdownLogic { ) } - runTransaction (data, promptCb, modalDialog, confirmDialog, finalCb) { + runTransaction (data, continueCb, promptCb, modalDialog, confirmDialog, finalCb) { var confirmationCb = (network, tx, gasEstimation, continueTxExecution, cancelCb) => { if (network.name !== 'Main') { return continueTxExecution(null) @@ -261,7 +261,7 @@ class DropdownLogic { ) } - this.udapp.runTx(data, confirmationCb, promptCb, finalCb) + this.udapp.runTx(data, confirmationCb, continueCb, promptCb, finalCb) } forceSend (selectedContract, args, continueCb, promptCb, modalDialog, confirmDialog, statusCb, cb) { @@ -277,7 +277,7 @@ class DropdownLogic { this.createContract(selectedContract, data, continueCb, promptCb, modalDialog, confirmDialog, cb) }, statusCb, (data, runTxCallback) => { // called for libraries deployment - this.runTransaction(data, promptCb, modalDialog, confirmDialog, runTxCallback) + this.runTransaction(data, continueCb, promptCb, modalDialog, confirmDialog, runTxCallback) }) } if (Object.keys(selectedContract.bytecodeLinkReferences).length) statusCb(`linking ${JSON.stringify(selectedContract.bytecodeLinkReferences, null, '\t')} using ${JSON.stringify(contractMetadata.linkReferences, null, '\t')}`) From 017a82315815d6ea1fa4003608b748ae7b5a5e1c Mon Sep 17 00:00:00 2001 From: Rob Stupay Date: Thu, 3 Jan 2019 14:33:16 +0100 Subject: [PATCH 42/50] with Yann --- src/app.js | 201 +++++++++++++----- src/app/components/plugin-manager-api.js | 26 +++ .../components/plugin-manager-component.js | 62 ++++++ src/app/components/swap-panel-api.js | 26 +++ src/app/components/swap-panel-component.js | 57 +++++ src/app/components/vertical-icons-api.js | 23 ++ .../components/vertical-icons-component.js | 39 ++++ src/app/panels/left-icon-panel.js | 183 ++++++++++++++++ 8 files changed, 559 insertions(+), 58 deletions(-) create mode 100644 src/app/components/plugin-manager-api.js create mode 100644 src/app/components/plugin-manager-component.js create mode 100644 src/app/components/swap-panel-api.js create mode 100644 src/app/components/swap-panel-component.js create mode 100644 src/app/components/vertical-icons-api.js create mode 100644 src/app/components/vertical-icons-component.js create mode 100644 src/app/panels/left-icon-panel.js diff --git a/src/app.js b/src/app.js index 4ebd885dfe..63de8c3af4 100644 --- a/src/app.js +++ b/src/app.js @@ -37,7 +37,17 @@ var toolTip = require('./app/ui/tooltip') var TransactionReceiptResolver = require('./transactionReceiptResolver') const CompilerAbstract = require('./app/compiler/compiler-abstract') -const PluginManager = require('./app/plugin/pluginManager') +// const PluginManager = require('./app/plugin/pluginManager') + +// var IconPanel = require('./app/panels/left-icon-panel') + +const VerticalIconsComponent = require('./app/components/vertical-icons-component') +const VerticalIconsApi = require('./app/components/vertical-icons-api') +// var VerticalIconsProfile = require('./app/panels/vertical-icons-profile') + +const SwapPanelComponent = require('./app/component/swap-panel-component') +const SwapPanelComponent = require('./app/component/swap-panel-api') + const CompileTab = require('./app/tabs/compile-tab') const SettingsTab = require('./app/tabs/settings-tab') const AnalysisTab = require('./app/tabs/analysis-tab') @@ -46,6 +56,8 @@ const SupportTab = require('./app/tabs/support-tab') const TestTab = require('./app/tabs/test-tab') const RunTab = require('./app/tabs/run-tab') +const appManager = require('remix-plugin').appManager + var styleGuide = require('./app/ui/styles-guide/theme-chooser') var styles = styleGuide.chooser() @@ -188,46 +200,53 @@ class App { } } if (direction === 'left') { - self._view.leftpanel.style.width = delta + 'px' - self._view.centerpanel.style.left = delta + 'px' - } - if (direction === 'right') { - self._view.rightpanel.style.width = delta + 'px' - self._view.centerpanel.style.right = delta + 'px' + self._view.swappanel.style.width = delta + 'px' + self._view.mainpanel.style.left = delta + 'px' } + // if (direction === 'right') { + // self._view.mainpanel.style.width = delta + 'px' + // self._view.swappanel.style.right = delta + 'px' + // } } init () { var self = this run.apply(self) } + render () { var self = this if (self._view.el) return self._view.el - self._view.leftpanel = yo` -
- ${''} + // not resizable + self._view.iconpanel = yo` +
+ ${''}
` - self._view.centerpanel = yo` -
+ + // center panel, resizable + self._view.swappanel = yo` +
${''}
` - self._view.rightpanel = yo` -
+ + // handle the editor + terminal + self._view.mainpanel = yo` +
${''}
` + self._view.el = yo`
- ${self._view.leftpanel} - ${self._view.centerpanel} - ${self._view.rightpanel} + ${self._view.iconpanel} + ${self._view.swappanel} + ${self._view.mainpanel}
` // INIT self._adjustLayout('left', self.data._layout.left.offset) - self._adjustLayout('right', self.data._layout.right.offset) + // self._adjustLayout('right', self.data._layout.right.offset) return self._view.el } startdebugging (txHash) { @@ -365,23 +384,113 @@ Please make a backup of your contracts and start using http://remix.ethereum.org var fileManager = self._components.fileManager registry.put({api: fileManager, name: 'filemanager'}) - // ---------------- Plugin Manager ------------------------------- - - let pluginManager = new PluginManager( - self, - self._components.compilersArtefacts, - txlistener, - self._components.fileProviders, - self._components.fileManager, - udapp) - registry.put({api: pluginManager, name: 'pluginmanager'}) - - pluginManager.event.register('sendCompilationResult', (file, source, languageVersion, data) => { - // TODO check whether the tab is configured - let compiler = new CompilerAbstract(languageVersion, data, source) - self._components.compilersArtefacts['__last'] = compiler + // ----------------- app manager ---------------------------- + const VerticalIconsProfile = { + type: 'verticalIcons', + methods: ['addIcon', 'removeIcon'], + } + + const SwapPanelProfile = { + type: 'swapPanel', + methods: ['addView', 'showContent'], + } + + const PluginManagerProfile = { + type: 'pluginManager', + methods: [], + } + + const FileManagerProfile = { + type: 'fileManager', + methods: [], + } + + const SettingsProfile = { + type: 'settings', + methods: [], + } + + const appManager = new appManager() + + const swapPanelComponent = new SwapPanelComponent() + const pluginManagerComponent = new PluginManagerComponent(appManager) + const verticalIconComponent = new VerticalIconsComponent(appManager) + + const swapPanelApi = new SwapPanelApi(swapPanelComponent) + const verticalIconApi = new VerticalsIconApi(verticalIconComponent) + const pluginManagerAPI = new pluginManagerAPI(pluginManagerComponent) + + self._components.filePanel = new FilePanel() + registry.put({api: self._components.filePanel, name: 'filepanel'}) + + var settings = new SettingsTab() + + // All Plugins and Modules are registered in the contructor + // You cannot add module after + appManager.init({ + // Module should be activated by default + modules: [ + { json: VerticalIconsProfile, api: verticalIconApi }, + { json: SwapPanelProfile, api: swapPanelApi }, + { json: PluginManagerProfile, api: pluginManagerApi }, + { json: FileManagerProfile, api: self._components.filePanel }, + { json: SettingsProfile, api: settings } + ], + // Plugins need to be activated + plugins: [], + options: { + bootstrap: 'pluginManager' + } }) + const compileTab = new CompileTab(self._components.registry) + const compileTabProfile = { + type: 'solidityCompile', + methods: [], + } + appManager.addPlugin({json: compileTabProfile, api: compileTab}) + + const testTab = new TestTab(self._components.registry, compileTab) + const testTabProfile = { + type: 'solidityUnitTesting', + methods: [], + } + appManager.addPlugin({json: testTabProfile, api: testTab}) + + const runTab = new RunTab(self._components.registry) + const runTabProfile = { + type: 'runTransaction', + methods: [], + } + appManager.addPlugin({json: runTabProfile, api: runTab}) + + const analysisTab = new AnalysisTab(self._components.registry) + const analysisTabProfile = { + type: 'solidityStaticAnalisys', + methods: [], + } + appManager.addPlugin({json: analysisTabProfile, api: analysisTab}) + + const debuggerTab = new DebuggerTab(self._components.registry) + const debuggerTabProfile = { + type: 'debugger', + methods: [], + } + appManager.addPlugin({json: debuggerTabProfile, api: debuggerTab}) + + const supportTab = new SupportTab(self._components.registry) + const supportTabProfile = { + type: 'support', + methods: [], + } + appManager.addPlugin({json: supportTabProfile, api: supportTab}) + + self._components.iconpanel.appendChild(verticalIconComponent.render()) + self._components.iconpanel.event.register('resize', delta => self._adjustLayout('left', delta)) + + self._components.swappanel.appendChild(swapPanelComponent.render()) + self._components.swappanel.event.register('resize', delta => self._adjustLayout('center', delta)) + self._components.editorpanel.init() self._components.fileManager.init() @@ -409,35 +518,11 @@ Please make a backup of your contracts and start using http://remix.ethereum.org self.loadFiles(filesToLoad) } - // ---------------- FilePanel -------------------- - self._components.filePanel = new FilePanel() - self._view.leftpanel.appendChild(self._components.filePanel.render()) - self._components.filePanel.event.register('resize', delta => self._adjustLayout('left', delta)) - registry.put({api: self._components.filePanel, name: 'filepanel'}) - // ----------------- Renderer ----------------- var renderer = new Renderer() registry.put({api: renderer, name: 'renderer'}) - // ---------------- Tabs ------------------------------- - let compileTab = new CompileTab(self._components.registry) - let tabs = { - compile: compileTab, - run: new RunTab(self._components.registry), - settings: new SettingsTab(self._components.registry), - analysis: new AnalysisTab(self._components.registry), - debug: new DebuggerTab(self._components.registry), - support: new SupportTab(self._components.registry), - test: new TestTab(self._components.registry, compileTab) - } - - // ---------------- Righthand-panel -------------------- - self._components.righthandpanel = new RighthandPanel({ tabs, pluginManager }) - self._view.rightpanel.appendChild(self._components.righthandpanel.render()) - self._components.righthandpanel.init() - self._components.righthandpanel.event.register('resize', delta => self._adjustLayout('right', delta)) - - var txLogger = new TxLogger() // eslint-disable-line + var txLogger = new TxLogger() // eslint-disable-line var queryParams = new QueryParams() diff --git a/src/app/components/plugin-manager-api.js b/src/app/components/plugin-manager-api.js new file mode 100644 index 0000000000..3cadd1bdb1 --- /dev/null +++ b/src/app/components/plugin-manager-api.js @@ -0,0 +1,26 @@ +var yo = require('yo-yo') +var csjs = require('csjs-inject') +const remixLib = require('remix-lib') + +const styleguide = require('../ui/styles-guide/theme-chooser') +const styles = styleguide.chooser() + +const EventManager = remixLib.EventManager + +class SwapPanelApi { + constructor (swapPanelComponent) { + this.component = swapPanelComponent + } + + /* + viewTitle: string + content: DOM element + */ + addView(viewTitle, content) { + // add the DOM to the swappanel + this.component.addView(viewTitle, contents) + } +} + + +module.exports = SwapPanelApi diff --git a/src/app/components/plugin-manager-component.js b/src/app/components/plugin-manager-component.js new file mode 100644 index 0000000000..59de442953 --- /dev/null +++ b/src/app/components/plugin-manager-component.js @@ -0,0 +1,62 @@ +var yo = require('yo-yo') +var csjs = require('csjs-inject') +const remixLib = require('remix-lib') + +const styleguide = require('../ui/styles-guide/theme-chooser') +const styles = styleguide.chooser() + +const EventManager = remixLib.EventManager + +class PluginManagerComponent { + constructor (appManager) { + this.appManager = appManager + appManager.event.register('pluginLoaded', () => { + // call this.renderItem + }) + } + + render () { + var self = this + // loop over all this.modules and this.plugins + var view = yo` +
+
+ ` + } + + _activate(item) { + this.appManager.activate(item) + } + + _deactivate(item) { + this.appManager.deactivate(item) + } + + renderItem (item) { + var self = this + + var view = yo` +
+ ${item.name} + ${item.url} +
+ ` + } +} + +module.exports = SwapPanelComponent + +const css = csjs` + .plugins { + width : 300px; + } + .plugItIn { + display : none; + } + .plugItIn.active { + display :block; + } + .clearFunds { background-color: lightblue; } +` diff --git a/src/app/components/swap-panel-api.js b/src/app/components/swap-panel-api.js new file mode 100644 index 0000000000..3cadd1bdb1 --- /dev/null +++ b/src/app/components/swap-panel-api.js @@ -0,0 +1,26 @@ +var yo = require('yo-yo') +var csjs = require('csjs-inject') +const remixLib = require('remix-lib') + +const styleguide = require('../ui/styles-guide/theme-chooser') +const styles = styleguide.chooser() + +const EventManager = remixLib.EventManager + +class SwapPanelApi { + constructor (swapPanelComponent) { + this.component = swapPanelComponent + } + + /* + viewTitle: string + content: DOM element + */ + addView(viewTitle, content) { + // add the DOM to the swappanel + this.component.addView(viewTitle, contents) + } +} + + +module.exports = SwapPanelApi diff --git a/src/app/components/swap-panel-component.js b/src/app/components/swap-panel-component.js new file mode 100644 index 0000000000..bb6c213e09 --- /dev/null +++ b/src/app/components/swap-panel-component.js @@ -0,0 +1,57 @@ +var yo = require('yo-yo') +var csjs = require('csjs-inject') +const remixLib = require('remix-lib') + +const styleguide = require('../ui/styles-guide/theme-chooser') +const styles = styleguide.chooser() + +const EventManager = remixLib.EventManager + +class SwapPanelComponent { + constructor () { + } + + showContent (moduleName) { + // hiding the current view and display the `moduleName` + } + + addView (title, content) { + // add the DOM to the swappanel + } + + render () { + var self = this + + var view = yo` +
+
+

Plugin 1

+
  • Some Text
+
+
+

Plugin 2

+
  • Some Text
+
+
+

Plugin 3

+
  • Some Text
+
+
+ ` + } +} + +module.exports = SwapPanelComponent + +const css = csjs` + .plugins { + width : 300px; + } + .plugItIn { + display : none; + } + .plugItIn.active { + display :block; + } + .clearFunds { background-color: lightblue; } +` diff --git a/src/app/components/vertical-icons-api.js b/src/app/components/vertical-icons-api.js new file mode 100644 index 0000000000..ec4438d734 --- /dev/null +++ b/src/app/components/vertical-icons-api.js @@ -0,0 +1,23 @@ +var yo = require('yo-yo') +var csjs = require('csjs-inject') +const remixLib = require('remix-lib') + +const styleguide = require('../ui/styles-guide/theme-chooser') +const styles = styleguide.chooser() + +// API +class VerticalIconsApi { + + constructor(verticalIconsComponent) { + this.component = verticalIconsComponent + } + + addIcon(icon) { + this.component.event.trigger('addIcon', icon) + } + + removeIcon(icon) { + this.component.event.trigger('removeIcon', icon) + } +} +module.exports = VerticalIconsApi diff --git a/src/app/components/vertical-icons-component.js b/src/app/components/vertical-icons-component.js new file mode 100644 index 0000000000..57e375272f --- /dev/null +++ b/src/app/components/vertical-icons-component.js @@ -0,0 +1,39 @@ + +var yo = require('yo-yo') +var csjs = require('csjs-inject') +const remixLib = require('remix-lib') + +const styleguide = require('../ui/styles-guide/theme-chooser') +const styles = styleguide.chooser() + + // Component + class VerticalIconComponent { + + constructor(appManager) { + this.appManager = appManager + appManager.event.register('activated', (item) => { + this.addIcon(item) + }) + appManager.event.register('deactivated', (item) => { + this.removeIcon(item) + }) + } + + addIcon (item) { + + } + + removeIcon (item) { + + } + + render() { + yo` +
+

example

+
+ ` + } + } + + module.exports = VerticalIconComponent diff --git a/src/app/panels/left-icon-panel.js b/src/app/panels/left-icon-panel.js new file mode 100644 index 0000000000..10601dc9da --- /dev/null +++ b/src/app/panels/left-icon-panel.js @@ -0,0 +1,183 @@ +const yo = require('yo-yo') +const csjs = require('csjs-inject') +const remixLib = require('remix-lib') + +var globalRegistry = require('../../global/registry') + +const styleguide = require('../ui/styles-guide/theme-chooser') +const styles = styleguide.chooser() +// const PluginManager = require('../plugin/pluginManager') +// const TabbedMenu = require('../tabs/tabbed-menu') +// const CompileTab = require('../tabs/compile-tab') +// const SettingsTab = require('../tabs/settings-tab') +// const AnalysisTab = require('../tabs/analysis-tab') +// const DebuggerTab = require('../tabs/debugger-tab') +// const SupportTab = require('../tabs/support-tab') +// const PluginTab = require('../tabs/plugin-tab') +// const TestTab = require('../tabs/test-tab') +// const RunTab = require('../tabs/run-tab') +// const DraggableContent = require('../ui/draggableContent') + +const EventManager = remixLib.EventManager + +// var async = require('async') + +// var tooltip = require('../ui/tooltip') + +// var styleGuide = require('../ui/styles-guide/theme-chooser') + +// var css = require('./styles/file-panel-styles') + +// var canUpload = window.File || window.FileReader || window.FileList || window.Blob +// var ghostbar = yo`
` + +/* + Overview of APIs: + * fileManager: @args fileProviders (browser, shared-folder, swarm, github, etc ...) & config & editor + - listen on browser & localhost file provider (`fileRenamed` & `fileRemoved`) + - update the tabs, switchFile + - trigger `currentFileChanged` + - set the current file in the config + * fileProvider: currently browser, swarm, localhost, github, gist + - link to backend + - provide properties `type`, `readonly` + - provide API `resolveDirectory`, `remove`, `exists`, `rename`, `get`, `set` + - trigger `fileExternallyChanged`, `fileRemoved`, `fileRenamed`, `fileRenamedError`, `fileAdded` + * file-explorer: treeview @args fileProvider + - listen on events triggered by fileProvider + - call fileProvider API +*/ + +module.exports = class LeftIconPanel { + constructor (localRegistry) { + const self = this + self._components = {} + self._components.registry = localRegistry || globalRegistry + + self._components.registry.put({api: this, name: 'lefticonpanel'}) + self.event = new EventManager() + self._view = { + element: null, + tabbedMenu: null, + tabbedMenuViewport: null, + dragbar: null + } + + self.plugins = [ + {type: 'fileManager', displayName: 'File Manger', icon: 'fa fa-files-o fa-2x'}, + {type: 'pluginManager', displayName: 'Plugin Manger', icon: 'fa fa-puzzle-piece fa-2x'}, + {type: 'settings', displayName: 'setting', icon: 'fa fa-cog fa-2x'} + ] + } + + + swapPlugin (name) { + console.log(name) + } + + render () { + const self = this + // if (self._view.element) return self._view.element + // return self._view.element + return yo` +
+
    + ${self.plugins.map(function (pi) { + return yo`
  • + { self.swapPlugin(pi.type) }} style="size: 24px;" class="${pi.icon} ${css.plugin}"> +
  • ` + }) + } +
+
+ ` + } + + init () { + // @TODO: init is for resizable drag bar only and should be refactored in the future + const self = this + const limit = 60 + self._view.dragbar.addEventListener('mousedown', mousedown) + const ghostbar = yo`
` + function mousedown (event) { + event.preventDefault() + if (event.which === 1) { + moveGhostbar(event) + document.body.appendChild(ghostbar) + document.addEventListener('mousemove', moveGhostbar) + document.addEventListener('mouseup', removeGhostbar) + document.addEventListener('keydown', cancelGhostbar) + } + } + function cancelGhostbar (event) { + if (event.keyCode === 27) { + document.body.removeChild(ghostbar) + document.removeEventListener('mousemove', moveGhostbar) + document.removeEventListener('mouseup', removeGhostbar) + document.removeEventListener('keydown', cancelGhostbar) + } + } + function getPosition (event) { + const lhp = window['filepanel'].offsetWidth + const max = document.body.offsetWidth - limit + var newpos = (event.pageX > max) ? max : event.pageX + newpos = (newpos > (lhp + limit)) ? newpos : lhp + limit + return newpos + } + function moveGhostbar (event) { // @NOTE VERTICAL ghostbar + ghostbar.style.left = getPosition(event) + 'px' + } + function removeGhostbar (event) { + document.body.removeChild(ghostbar) + document.removeEventListener('mousemove', moveGhostbar) + document.removeEventListener('mouseup', removeGhostbar) + document.removeEventListener('keydown', cancelGhostbar) + self.event.trigger('resize', [document.body.offsetWidth - getPosition(event)]) + } + } +} + +const css = csjs` + .lefticonpanel { + display : flex; + flex-direction : column; + top : 0; + right : 0; + bottom : 0; + box-sizing : border-box; + overflow : hidden; + height : 100%; + } + .header { + height : 100%; + } + .dragbar { + position : absolute; + width : 0.5em; + top : 3em; + bottom : 0; + cursor : col-resize; + z-index : 999; + border-left : 2px solid ${styles.rightPanel.bar_Dragging}; + } + .ghostbar { + width : 3px; + background-color : ${styles.rightPanel.bar_Ghost}; + opacity : 0.5; + position : absolute; + cursor : col-resize; + z-index : 9999; + top : 0; + bottom : 0; + } + i.plugin { + cursor : pointer; + } + .container ul { + margin-top : 10px; + } + .container ul li { + text-align : center; + margin-top : 10px; + } +` From eccca7da1a2635bb1ac0762910d436e2f99d1e99 Mon Sep 17 00:00:00 2001 From: Rob Stupay Date: Thu, 3 Jan 2019 15:28:26 +0100 Subject: [PATCH 43/50] slight update of code from our conversation --- src/app.js | 2 +- src/app/components/plugin-manager-component.js | 9 +++++++-- src/app/components/swap-panel-api.js | 7 ++++++- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/app.js b/src/app.js index 63de8c3af4..e63d02b2d7 100644 --- a/src/app.js +++ b/src/app.js @@ -448,7 +448,7 @@ Please make a backup of your contracts and start using http://remix.ethereum.org type: 'solidityCompile', methods: [], } - appManager.addPlugin({json: compileTabProfile, api: compileTab}) + appManager.addPlugin({json: compileTabProfile, api: new InternalModule(compileTab) }) const testTab = new TestTab(self._components.registry, compileTab) const testTabProfile = { diff --git a/src/app/components/plugin-manager-component.js b/src/app/components/plugin-manager-component.js index 59de442953..85d62a46c1 100644 --- a/src/app/components/plugin-manager-component.js +++ b/src/app/components/plugin-manager-component.js @@ -25,11 +25,15 @@ class PluginManagerComponent { } _activate(item) { - this.appManager.activate(item) + this.event.emit('activation', item) } _deactivate(item) { - this.appManager.deactivate(item) + this.event.emit('deactivation', item) + } + + _addPlugin(url){ + this.event.emit('plugin loading', url) } renderItem (item) { @@ -42,6 +46,7 @@ class PluginManagerComponent {
+
- +
` } } -module.exports = SwapPanelComponent +module.exports = PluginManagerComponent const css = csjs` .plugins { diff --git a/src/app/components/plugin-manager-proxy.js b/src/app/components/plugin-manager-proxy.js new file mode 100644 index 0000000000..f4849aa534 --- /dev/null +++ b/src/app/components/plugin-manager-proxy.js @@ -0,0 +1,24 @@ +var yo = require('yo-yo') + +var registry = require('../../global/registry') + +const CompilerAbstract = require('../compiler/compiler-abstract') + +const EventManager = require('remix-lib').EventManager + +class PluginManagerProxy { + + constructor () { + this.event = new EventManager + } + + register (mod, instance) { + instance.event.on('compilationFinished', (file, source, languageVersion, data) => { + registry.get('compilersartefacts').api['__last'] = new CompilerAbstract(languageVersion, data, source) + this.event.trigger('sendCompilationResult', [file, source, languageVersion, data]) + }) + } + +} + +module.exports = PluginManagerProxy diff --git a/src/app/components/swap-panel-api.js b/src/app/components/swap-panel-api.js index b09809c98c..376f016f10 100644 --- a/src/app/components/swap-panel-api.js +++ b/src/app/components/swap-panel-api.js @@ -5,25 +5,30 @@ const remixLib = require('remix-lib') const styleguide = require('../ui/styles-guide/theme-chooser') const styles = styleguide.chooser() -const EventManager = remixLib.EventManager +const EventEmmitter = require('events') class SwapPanelApi { - constructor (swapPanelComponent, pluginManagerApi) { + constructor (swapPanelComponent, verticalIconsComponent, pluginManagerComponent) { this.component = swapPanelComponent + verticalIconsComponent.event.on('showContent', (moduleName) => { + this.component.showContent(moduleName) + }) + pluginManagerComponent.event.on('internalActivated', (mod, content) => { + this.add(mod.name, content) + }) } /* - viewTitle: string content: DOM element + by appManager */ - addView(viewTitle, content) { + add (moduleName, content) { // add the DOM to the swappanel - this.component.addView(viewTitle, contents) + return this.component.add(moduleName, content) } - activate() { - this.event.emit(activated) - this.pluginManagerApi.activated(this.type) + reference (modulename, domElement) { + this.nodes[modulename] = domElement } } diff --git a/src/app/components/swap-panel-component.js b/src/app/components/swap-panel-component.js index bb6c213e09..c10bfbe715 100644 --- a/src/app/components/swap-panel-component.js +++ b/src/app/components/swap-panel-component.js @@ -5,39 +5,40 @@ const remixLib = require('remix-lib') const styleguide = require('../ui/styles-guide/theme-chooser') const styles = styleguide.chooser() -const EventManager = remixLib.EventManager - class SwapPanelComponent { constructor () { + // list of contents + this.contents = {} + // name of the current displayed content + this.currentNode } showContent (moduleName) { // hiding the current view and display the `moduleName` + if (this.contents[moduleName]) { + this.contents[moduleName].style.display = 'block' + if (this.currentNode) { + this.contents[this.currentNode].style.display = 'none' + } + this.currentNode = moduleName + return + } + console.log(`${moduleName} not found`) } - addView (title, content) { - // add the DOM to the swappanel - } + + add (moduleName, content) { + this.contents[moduleName] = yo`
${content}
` + this.view.appendChild(this.contents[moduleName]) + this.showContent(moduleName) + } render () { - var self = this - - var view = yo` + this.view = yo`
-
-

Plugin 1

-
  • Some Text
-
-
-

Plugin 2

-
  • Some Text
-
-
-

Plugin 3

-
  • Some Text
-
` + return this.view } } @@ -45,7 +46,6 @@ module.exports = SwapPanelComponent const css = csjs` .plugins { - width : 300px; } .plugItIn { display : none; diff --git a/src/app/components/vertical-icons-api.js b/src/app/components/vertical-icons-api.js index a11d8a43a3..3cb69883d5 100644 --- a/src/app/components/vertical-icons-api.js +++ b/src/app/components/vertical-icons-api.js @@ -8,17 +8,9 @@ const styles = styleguide.chooser() // API class VerticalIconsApi { - constructor(verticalIconsComponent, pluginManagerApi) { - pluginManagerApi.event.on('activate', (module) => verticalIconsComponent.addIcon(module) ) + constructor(verticalIconsComponent, pluginManagerComponent) { this.component = verticalIconsComponent - } - - addIcon(icon) { - this.component.event.trigger('addIcon', icon) - } - - removeIcon(icon) { - this.component.event.trigger('removeIcon', icon) - } + pluginManagerComponent.event.on('internalActivated', (mod, content) => verticalIconsComponent.addIcon(mod) ) + } } module.exports = VerticalIconsApi diff --git a/src/app/components/vertical-icons-component.js b/src/app/components/vertical-icons-component.js index 57e375272f..3364da36e9 100644 --- a/src/app/components/vertical-icons-component.js +++ b/src/app/components/vertical-icons-component.js @@ -5,34 +5,39 @@ const remixLib = require('remix-lib') const styleguide = require('../ui/styles-guide/theme-chooser') const styles = styleguide.chooser() +const EventEmmitter = require('events') // Component class VerticalIconComponent { - constructor(appManager) { - this.appManager = appManager - appManager.event.register('activated', (item) => { - this.addIcon(item) - }) - appManager.event.register('deactivated', (item) => { - this.removeIcon(item) - }) + constructor() { + this.event = new EventEmmitter } - addIcon (item) { - + addIcon (mod) { + let self = this + this.view.appendChild(yo`
{ self._iconClick(mod.name)}} title=${mod.name}>${mod.name}
`) } removeIcon (item) { } + select (name) { + this.event.emit('showContent', name) + } + + _iconClick (name) { + // called when an icon has been clicked + this.event.emit('showContent', name) + } + render() { - yo` -
-

example

+ this.view = yo` +
` + return this.view } } diff --git a/src/app/panels/editor-panel.js b/src/app/panels/editor-panel.js index 8c4b2ef55d..6906222f01 100644 --- a/src/app/panels/editor-panel.js +++ b/src/app/panels/editor-panel.js @@ -244,12 +244,10 @@ class EditorPanel { function toggleLHP (event) { this.children[0].classList.toggle('fa-angle-double-right') this.children[0].classList.toggle('fa-angle-double-left') - self.event.trigger('resize', ['left']) } function toggleRHP (event) { this.children[0].classList.toggle('fa-angle-double-right') this.children[0].classList.toggle('fa-angle-double-left') - self.event.trigger('resize', ['right']) } function increase () { self._components.editor.editorFontSize(1) } function decrease () { self._components.editor.editorFontSize(-1) } diff --git a/src/app/panels/file-panel.js b/src/app/panels/file-panel.js index a1586dd56b..49433f1065 100644 --- a/src/app/panels/file-panel.js +++ b/src/app/panels/file-panel.js @@ -263,7 +263,6 @@ function filepanel (localRegistry) { document.removeEventListener('mousemove', moveGhostbar) document.removeEventListener('mouseup', removeGhostbar) document.removeEventListener('keydown', cancelGhostbar) - self.event.trigger('resize', [getPosition(event)]) } function createNewFile () { diff --git a/src/app/panels/left-icon-panel.js b/src/app/panels/left-icon-panel.js index 10601dc9da..c68a84235f 100644 --- a/src/app/panels/left-icon-panel.js +++ b/src/app/panels/left-icon-panel.js @@ -132,7 +132,6 @@ module.exports = class LeftIconPanel { document.removeEventListener('mousemove', moveGhostbar) document.removeEventListener('mouseup', removeGhostbar) document.removeEventListener('keydown', cancelGhostbar) - self.event.trigger('resize', [document.body.offsetWidth - getPosition(event)]) } } } diff --git a/src/app/panels/righthand-panel.js b/src/app/panels/righthand-panel.js deleted file mode 100644 index 0bcb4f9877..0000000000 --- a/src/app/panels/righthand-panel.js +++ /dev/null @@ -1,163 +0,0 @@ -const yo = require('yo-yo') -const csjs = require('csjs-inject') -const EventManager = require('../../lib/events') - -var globalRegistry = require('../../global/registry') - -const styleguide = require('../ui/styles-guide/theme-chooser') -const TabbedMenu = require('../tabs/tabbed-menu') -const PluginTab = require('../tabs/plugin-tab') -const DraggableContent = require('../ui/draggableContent') - -const styles = styleguide.chooser() - -const css = csjs` - .righthandpanel { - display : flex; - flex-direction : column; - top : 0; - right : 0; - bottom : 0; - box-sizing : border-box; - overflow : hidden; - height : 100%; - } - .header { - height : 100%; - } - .dragbar { - position : absolute; - width : 0.5em; - top : 3em; - bottom : 0; - cursor : col-resize; - z-index : 999; - border-left : 2px solid ${styles.rightPanel.bar_Dragging}; - } - .ghostbar { - width : 3px; - background-color : ${styles.rightPanel.bar_Ghost}; - opacity : 0.5; - position : absolute; - cursor : col-resize; - z-index : 9999; - top : 0; - bottom : 0; - } -` - -class RighthandPanel { - constructor ({pluginManager, tabs}, localRegistry) { - const self = this - self._components = {} - self._components.registry = localRegistry || globalRegistry - self._components.registry.put({api: this, name: 'righthandpanel'}) - self.event = new EventManager() - self._view = { - element: null, - tabbedMenu: null, - tabbedMenuViewport: null, - dragbar: null - } - - var tabbedMenu = new TabbedMenu(self._components.registry) - - self._components = { - tabbedMenu: tabbedMenu, - tabs - } - - self._components.tabs.settings.event.register('plugin-loadRequest', json => { - self.loadPlugin(json) - }) - - self.loadPlugin = function (json) { - var modal = new DraggableContent(() => { - pluginManager.unregister(json) - }) - var tab = new PluginTab(json) - var content = tab.render() - document.querySelector('body').appendChild(modal.render(json.title, json.url, content)) - pluginManager.register(json, modal, content) - } - - self._view.dragbar = yo`
` - self._view.element = yo` -
- ${self._view.dragbar} - -
` - - const { compile, run, settings, analysis, debug, support, test } = tabs - self._components.tabbedMenu.addTab('Compile', 'compileView', compile.render()) - self._components.tabbedMenu.addTab('Run', 'runView', run.render()) - self._components.tabbedMenu.addTab('Analysis', 'staticanalysisView', analysis.render()) - self._components.tabbedMenu.addTab('Testing', 'testView', test.render()) - self._components.tabbedMenu.addTab('Debugger', 'debugView', debug.render()) - self._components.tabbedMenu.addTab('Settings', 'settingsView', settings.render()) - self._components.tabbedMenu.addTab('Support', 'supportView', support.render()) - self._components.tabbedMenu.selectTabByTitle('Compile') - } - - render () { - const self = this - if (self._view.element) return self._view.element - return self._view.element - } - - debugger () { - return this._components.tabs.debug.debugger() - } - - focusOn (x) { - if (this._components.tabbedMenu) this._components.tabbedMenu.selectTabByClassName(x) - } - - init () { - // @TODO: init is for resizable drag bar only and should be refactored in the future - const self = this - const limit = 60 - self._view.dragbar.addEventListener('mousedown', mousedown) - const ghostbar = yo`
` - function mousedown (event) { - event.preventDefault() - if (event.which === 1) { - moveGhostbar(event) - document.body.appendChild(ghostbar) - document.addEventListener('mousemove', moveGhostbar) - document.addEventListener('mouseup', removeGhostbar) - document.addEventListener('keydown', cancelGhostbar) - } - } - function cancelGhostbar (event) { - if (event.keyCode === 27) { - document.body.removeChild(ghostbar) - document.removeEventListener('mousemove', moveGhostbar) - document.removeEventListener('mouseup', removeGhostbar) - document.removeEventListener('keydown', cancelGhostbar) - } - } - function getPosition (event) { - const lhp = window['filepanel'].offsetWidth - const max = document.body.offsetWidth - limit - var newpos = (event.pageX > max) ? max : event.pageX - newpos = (newpos > (lhp + limit)) ? newpos : lhp + limit - return newpos - } - function moveGhostbar (event) { // @NOTE VERTICAL ghostbar - ghostbar.style.left = getPosition(event) + 'px' - } - function removeGhostbar (event) { - document.body.removeChild(ghostbar) - document.removeEventListener('mousemove', moveGhostbar) - document.removeEventListener('mouseup', removeGhostbar) - document.removeEventListener('keydown', cancelGhostbar) - self.event.trigger('resize', [document.body.offsetWidth - getPosition(event)]) - } - } -} - -module.exports = RighthandPanel diff --git a/src/app/tabs/compile-tab.js b/src/app/tabs/compile-tab.js index f5b9aa5365..e2fba7c881 100644 --- a/src/app/tabs/compile-tab.js +++ b/src/app/tabs/compile-tab.js @@ -1,4 +1,5 @@ /* global Worker */ +const EventEmitter = require('events') const async = require('async') const $ = require('jquery') const yo = require('yo-yo') @@ -25,6 +26,7 @@ const styles = styleGuide.chooser() module.exports = class CompileTab { constructor (localRegistry) { const self = this + self.event = new EventEmitter() self._view = { el: null, autoCompile: null, @@ -115,9 +117,11 @@ module.exports = class CompileTab { self._view.compileIcon.setAttribute('title', '') }) self._components.compiler.event.register('compilationFinished', function finish (success, data, source) { + if (success) { + // forwarding the event to the appManager infra + self.event.emit('compilationFinished', null, source, 'Solidity', data) + } if (self._view.compileIcon) { - const compileTab = document.querySelector('.compileView') - compileTab.style.color = styles.colors.black self._view.compileIcon.style.color = styles.colors.black self._view.compileIcon.classList.remove(`${css.spinningIcon}`) self._view.compileIcon.classList.remove(`${css.bouncingIcon}`) @@ -129,7 +133,6 @@ module.exports = class CompileTab { self._view.contractNames.innerHTML = '' if (success) { // TODO consider using compile tab as a proper module instead of just forwarding event - self._deps.pluginManager.receivedDataFrom('sendCompilationResult', 'solidity-compiler', [data.target, source, self.data.selectedVersion, data]) self._view.contractNames.removeAttribute('disabled') self._components.compiler.visitContracts(contract => { self.data.contractsDetails[contract.name] = parseContracts(contract.name, contract.object, self._components.compiler.getSource(contract.file)) @@ -139,22 +142,16 @@ module.exports = class CompileTab { } else { self._view.contractNames.setAttribute('disabled', true) } - // hightlight the tab if error - if (success) document.querySelector('.compileView').style.color = '' // @TODO: compileView tab - else document.querySelector('.compileView').style.color = styles.colors.red // @TODO: compileView tab - // display warning error if any var error = false if (data['error']) { error = true self._deps.renderer.error(data['error'].formattedMessage, self._view.errorContainer, {type: data['error'].severity || 'error'}) if (data['error'].mode === 'panic') { - /* return modalDialogCustom.alert(yo`
The compiler returned with the following internal error:
${data['error'].formattedMessage}.
The compiler might be in a non-sane state, please be careful and do not use further compilation data to deploy to mainnet. It is heavily recommended to use another browser not affected by this issue (Firefox is known to not be affected).

Please join remix gitter channel for more information.
`) - */ } } if (data.errors && data.errors.length) { @@ -185,6 +182,13 @@ module.exports = class CompileTab { } }) } + profile () { + return { + type: 'solidityCompile', + methods: {}, + events: ['compilationFinished'] + } + } addWarning (msg, settings) { const self = this self._deps.renderer.error(msg, self._view.errorContainerHead, settings) diff --git a/src/app/tabs/settings-tab.js b/src/app/tabs/settings-tab.js index b8f38ed79c..525cd33c39 100644 --- a/src/app/tabs/settings-tab.js +++ b/src/app/tabs/settings-tab.js @@ -205,7 +205,6 @@ module.exports = class SettingsTab { const css = csjs` .settingsTabView { padding: 2%; - display: flex; } .info { ${styles.rightPanel.settingsTab.box_SolidityVersionInfo}; diff --git a/src/app/ui/renderer.js b/src/app/ui/renderer.js index f90e756971..b5b968c72c 100644 --- a/src/app/ui/renderer.js +++ b/src/app/ui/renderer.js @@ -16,7 +16,6 @@ function Renderer (localRegistry) { self._components.registry = localRegistry || globlalRegistry // dependencies self._deps = { - editor: self._components.registry.get('editor').api, fileManager: self._components.registry.get('filemanager').api, config: self._components.registry.get('config').api } @@ -27,13 +26,15 @@ function Renderer (localRegistry) { Renderer.prototype._error = function (file, error) { const self = this + const editor = self._components.registry.get('editor').api if (file === self._deps.config.get('currentFile')) { - self._deps.editor.addAnnotation(error) + editor.addAnnotation(error) } } Renderer.prototype._errorClick = function (errFile, errLine, errCol) { const self = this + const editor = self._components.registry.get('editor').api if (errFile !== self._deps.config.get('currentFile')) { // TODO: refactor with this._components.contextView.jumpTo var provider = self._deps.fileManager.fileProviderOf(errFile) @@ -41,11 +42,11 @@ Renderer.prototype._errorClick = function (errFile, errLine, errCol) { provider.exists(errFile, (error, exist) => { if (error) return console.log(error) self._deps.fileManager.switchFile(errFile) - self._deps.editor.gotoLine(errLine, errCol) + editor.gotoLine(errLine, errCol) }) } } else { - self._deps.editor.gotoLine(errLine, errCol) + editor.gotoLine(errLine, errCol) } } From b1d3ec1a884535ce936a6cfbe1055f47a1d64225 Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 7 Jan 2019 15:04:39 +0100 Subject: [PATCH 47/50] add minimal API for plugin --- package.json | 1 + src/app.js | 81 ++++++++++++++++--- .../components/plugin-manager-component.js | 35 +++++--- src/app/components/swap-panel-api.js | 2 +- src/app/components/vertical-icons-api.js | 2 +- src/app/editor/SourceHighlighters.js | 35 ++++++++ src/app/editor/editor.js | 3 + src/app/files/browser-files-tree.js | 7 ++ src/app/files/fileManager.js | 49 +++++++++++ src/app/panels/editor-panel.js | 48 +++++------ src/app/tabs/compile-tab.js | 5 +- src/universal-dapp.js | 36 +++++++++ 12 files changed, 257 insertions(+), 47 deletions(-) create mode 100644 src/app/editor/SourceHighlighters.js diff --git a/package.json b/package.json index c7fbb313fc..3b186abf8d 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "csslint": "^1.0.2", "deep-equal": "^1.0.1", "ethereumjs-util": "^5.1.2", + "events": "^3.0.0", "execr": "^1.0.1", "exorcist": "^0.4.0", "fast-async": "6.3.1", diff --git a/src/app.js b/src/app.js index ba31917b56..61893cf9fa 100644 --- a/src/app.js +++ b/src/app.js @@ -6,6 +6,7 @@ var async = require('async') var request = require('request') var remixLib = require('remix-lib') var EventManager = require('./lib/events') +var EventEmitter = require('events') var registry = require('./global/registry') var UniversalDApp = require('./universal-dapp.js') @@ -203,6 +204,13 @@ class App { var self = this run.apply(self) } + + profile () { + return { + type: 'app', + methods: ['getExecutionContextProvider', 'getProviderEndpoint', 'detectNetWork', 'addProvider', 'removeProvider'] + } + } render () { var self = this @@ -283,6 +291,34 @@ class App { if (callback) callback(error) }) } + + getExecutionContextProvider (cb) { + cb(null, executionContext.getProvider()) + } + + getProviderEndpoint (cb) { + if (executionContext.getProvider() === 'web3') { + cb(null, executionContext.web3().currentProvider.host) + } else { + cb('no endpoint: current provider is either injected or vm') + } + } + + detectNetWork (cb) { + executionContext.detectNetwork((error, network) => { + cb(error, network) + }) + } + + addProvider (name, url, cb) { + executionContext.addProvider({ name, url }) + cb() + } + + removeProvider (name, cb) { + executionContext.removeProvider(name) + cb() + } } module.exports = App @@ -362,33 +398,60 @@ Please make a backup of your contracts and start using http://remix.ethereum.org }) registry.put({api: eventsDecoder, name: 'eventsdecoder'}) + /* + that proxy is used by appManager to broadcast new transaction event + */ + const txListenerModuleProxy = { + event: new EventEmitter(), + profile() { + return { + type: 'txListener', + events: ['newTransaction'] + } + } + } + txlistener.event.register('newTransaction', (tx) => { + txListenerModule.event.emit('newTransaction', tx) + }) + txlistener.startListening() // TODO: There are still a lot of dep between editorpanel and filemanager - // ----------------- editor panel ---------------------- - self._components.editorpanel = new EditorPanel() - registry.put({ api: self._components.editorpanel, name: 'editorpanel' }) - // ----------------- file manager ---------------------------- self._components.fileManager = new FileManager() var fileManager = self._components.fileManager registry.put({api: fileManager, name: 'filemanager'}) + // ----------------- editor panel ---------------------- + self._components.editorpanel = new EditorPanel() + registry.put({ api: self._components.editorpanel, name: 'editorpanel' }) + // ----------------- Renderer ----------------- var renderer = new Renderer() registry.put({api: renderer, name: 'renderer'}) // ----------------- app manager ---------------------------- - const PluginManagerProfile = { - type: 'pluginManager', - methods: [] - } + + /* + TODOs: + - for each activated plugin, + an internal module (associated only with the plugin) should be created for accessing specific part of the UI. detail to be discussed + - the current API is not optimal. For instance methods of `app` only refers to `executionContext`, wich does not make really sense. + */ const appManager = new AppManager({modules: [],plugins : []}) const swapPanelComponent = new SwapPanelComponent() - const pluginManagerComponent = new PluginManagerComponent() + const pluginManagerComponent = new PluginManagerComponent( + { + app: this, + udapp: udapp, + fileManager: fileManager, + sourceHighlighters: registry.get('editor').api.sourceHighlighters, + config: self._components.filesProviders['config'], + txListener: txListenerModuleProxy + }) registry.put({api: pluginManagerComponent.proxy(), name: 'pluginmanager'}) self._components.editorpanel.init() diff --git a/src/app/components/plugin-manager-component.js b/src/app/components/plugin-manager-component.js index 08ede7bc40..1ca95bf82f 100644 --- a/src/app/components/plugin-manager-component.js +++ b/src/app/components/plugin-manager-component.js @@ -21,11 +21,20 @@ const EventEmitter = require ('events') class PluginManagerComponent { - constructor () { + constructor ({ app, udapp, fileManager, sourceHighlighters, config, txListener }) { this.event = new EventEmitter() this.modulesDefinition = { - 'FilePanel': { name: 'FilePanel', Type: FilePanel, icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDUwIDUwIiBoZWlnaHQ9IjUwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MCA1MCIgd2lkdGg9IjUwcHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iNTAiIHdpZHRoPSI1MCIvPjxwb2x5bGluZSBmaWxsPSJub25lIiBwb2ludHM9IjQ0LDIxIDQ0LDQ5IDYsNDkgICA2LDIxICIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLXdpZHRoPSIyIi8+PHBvbHlsaW5lIGZpbGw9Im5vbmUiIHBvaW50cz0iMTksNDkgMTksMjggMzEsMjggICAzMSw0OSAiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iMiIvPjxwb2x5Z29uIHBvaW50cz0iMzUsNSAzNSw4LjAxNiAzNywxMC4wOTQgMzcsNyAzOSw3IDM5LDEyLjIwMyA0MSwxNC4yNjYgNDEsNSAiLz48cG9seWxpbmUgZmlsbD0ibm9uZSIgcG9pbnRzPSIgIDEuMTEsMjUuOTQyIDI1LDEuMDUzIDQ4Ljg5LDI1Ljk0MyAiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iMiIvPjwvc3ZnPg==' }, + // service module. They can be seen as daemon + // they usually don't have UI and only represent the minimal API a plugins can access. + 'App': { name: 'App', target: app }, + 'Udapp': { name: 'Udapp', target: udapp }, + 'FileManager': { name: 'FileManager', target: fileManager }, + 'SourceHighlighters': { name: 'SourceHighlighters', target: sourceHighlighters }, + 'Config': { name: 'Config', target: config }, + 'TxListener': { name: 'TxListener', target: txListener }, + // internal components. They are mostly views, they don't provide external API for plugins 'Solidity Compile': { name: 'Solidity Compile', class: 'evm-compiler', Type: CompileTab, icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDUwIDUwIiBoZWlnaHQ9IjUwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MCA1MCIgd2lkdGg9IjUwcHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iNTAiIHdpZHRoPSI1MCIvPjxwYXRoIGQ9Ik00NiwxNXYtNCAgYzAtMS4xMDQtMC44OTYtMi0yLTJjMCwwLTI0LjY0OCwwLTI2LDBjLTEuNDY5LDAtMi40ODQtNC00LTRIM0MxLjg5Niw1LDEsNS44OTYsMSw3djR2Mjl2NGMwLDEuMTA0LDAuODk2LDIsMiwyaDM5ICBjMS4xMDQsMCwyLTAuODk2LDItMiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLXdpZHRoPSIyIi8+PHBhdGggZD0iTTEsNDRsNS0yNyAgYzAtMS4xMDQsMC44OTYtMiwyLTJoMzljMS4xMDQsMCwyLDAuODk2LDIsMmwtNSwyNyIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLXdpZHRoPSIyIi8+PC9zdmc+' }, + 'FilePanel': { name: 'FilePanel', Type: FilePanel, icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDUwIDUwIiBoZWlnaHQ9IjUwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MCA1MCIgd2lkdGg9IjUwcHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iNTAiIHdpZHRoPSI1MCIvPjxwb2x5bGluZSBmaWxsPSJub25lIiBwb2ludHM9IjQ0LDIxIDQ0LDQ5IDYsNDkgICA2LDIxICIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLXdpZHRoPSIyIi8+PHBvbHlsaW5lIGZpbGw9Im5vbmUiIHBvaW50cz0iMTksNDkgMTksMjggMzEsMjggICAzMSw0OSAiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iMiIvPjxwb2x5Z29uIHBvaW50cz0iMzUsNSAzNSw4LjAxNiAzNywxMC4wOTQgMzcsNyAzOSw3IDM5LDEyLjIwMyA0MSwxNC4yNjYgNDEsNSAiLz48cG9seWxpbmUgZmlsbD0ibm9uZSIgcG9pbnRzPSIgIDEuMTEsMjUuOTQyIDI1LDEuMDUzIDQ4Ljg5LDI1Ljk0MyAiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iMiIvPjwvc3ZnPg==' }, 'Test': { name: 'Test', dep: 'Solidity Compile', Type: TestTab, icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDUwIDUwIiBoZWlnaHQ9IjUwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MCA1MCIgd2lkdGg9IjUwcHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iNTAiIHdpZHRoPSI1MCIvPjxwYXRoIGQ9Ik00OSw0djI1YzAsMC01LjI3MywzLTEyLDMgIGMtMTEuOTI5LDAtMTUuODY5LTQtMjQtNFMxLDMwLDEsMzBWM2MwLDAsMi4wODUtMiwxMi0yczE0LjA0Nyw2LDI0LDZDNDMuMjgxLDcsNDguMTMsNC40NzEsNDksNHoiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iMiIvPjxsaW5lIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iMiIgeDE9IjEiIHgyPSIxIiB5MT0iMyIgeTI9IjQ5Ii8+PC9zdmc+' }, 'Run': { name: 'Run', Type: RunTab, icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDUwIDUwIiBoZWlnaHQ9IjUwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MCA1MCIgd2lkdGg9IjUwcHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iNTAiIHdpZHRoPSI1MCIvPjxwYXRoIGQ9Ik00My43MzQsMjFjLTMuNjMxLDAtMTUuMDkyLDAtMTUuMDkyLDAgIFMxNi4yNSw1LjE4OCwxNi4wNDcsNC45MzhzLTAuNDIyLTAuNTk0LTEuMTI1LTAuNjcyYy0wLjg1OS0wLjA5NS0xLjk2OS0wLjIwMy0yLjMyOC0wLjIzNGMtMC40MDYtMC4wMzUtMC43MTksMC4xNDEtMC40OTYsMC43MzQgIEMxMi4zODgsNS41MzksMTguNzQ4LDIxLDE4Ljc0OCwyMUg2LjAzNGMwLDAtMi40NTgtNC43MjItMi44NzgtNS41MzFDMi45NjUsMTUuMTAxLDIuNTU3LDE1LjAxNCwyLDE1SDEuMjk3ICBjLTAuMTI1LDAtMC4zMTIsMC0wLjI4MSwwLjM0NEMxLjA1OCwxNS44MTEsMywyNSwzLDI1cy0xLjg4OCw5LjE5Ny0xLjk4NCw5LjY1NkMwLjk1MywzNC45NTMsMS4xNzIsMzUsMS4yOTcsMzVIMiAgYzAuOTY2LTAuMDA5LDAuOTU0LTAuMDc5LDEuMTU2LTAuNDY5QzMuNTc2LDMzLjcyMiw2LjAzNCwyOSw2LjAzNCwyOWgxMi43MTRjMCwwLTYuMzYsMTUuNDYxLTYuNjUsMTYuMjM0ICBjLTAuMjIzLDAuNTk0LDAuMDksMC43NywwLjQ5NiwwLjczNGMwLjM1OS0wLjAzMSwxLjQ2OS0wLjEzOSwyLjMyOC0wLjIzNGMwLjcwMy0wLjA3OCwwLjkyMi0wLjQyMiwxLjEyNS0wLjY3MlMyOC42NDMsMjksMjguNjQzLDI5ICBzMTEuNDYxLDAsMTUuMDkyLDBjMy43NjYsMCw1LjI2NC0zLjAzMSw1LjI2NC00UzQ3LjQ4NCwyMSw0My43MzQsMjF6IiBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLXdpZHRoPSIyIi8+PC9zdmc+' }, 'Solidity Static Analysis': { name: 'Solidity Static Analysis', Type: AnalysisTab, icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDUwIDUwIiBoZWlnaHQ9IjUwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MCA1MCIgd2lkdGg9IjUwcHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iNTAiIHdpZHRoPSI1MCIvPjxjaXJjbGUgY3g9IjIiIGN5PSIyNSIgcj0iMiIvPjxjaXJjbGUgY3g9IjE1IiBjeT0iMTkiIHI9IjIiLz48Y2lyY2xlIGN4PSIyNSIgY3k9IjExIiByPSIyIi8+PGNpcmNsZSBjeD0iMzUiIGN5PSIxNyIgcj0iMiIvPjxjaXJjbGUgY3g9IjQ4IiBjeT0iNSIgcj0iMiIvPjxjaXJjbGUgY3g9IjIiIGN5PSIzOSIgcj0iMiIvPjxjaXJjbGUgY3g9IjE1IiBjeT0iNDEiIHI9IjIiLz48Y2lyY2xlIGN4PSIyNSIgY3k9IjMzIiByPSIyIi8+PGNpcmNsZSBjeD0iMzUiIGN5PSI0MyIgcj0iMiIvPjxjaXJjbGUgY3g9IjQ4IiBjeT0iMzEiIHI9IjIiLz48cG9seWxpbmUgZmlsbD0ibm9uZSIgcG9pbnRzPSIyLDI1IDE1LDE5IDI1LDExICAgMzUsMTcgNDgsNSAiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iMiIvPjxwb2x5bGluZSBmaWxsPSJub25lIiBwb2ludHM9IjIsMzkgMTUsNDEgMjUsMzMgICAzNSw0MyA0OCwzMSAiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iMiIvPjwvc3ZnPg==' }, @@ -45,6 +54,13 @@ class PluginManagerComponent { } initDefault () { + this.activateInternal('App') + this.activateInternal('Udapp') + this.activateInternal('FileManager') + this.activateInternal('SourceHighlighters') + this.activateInternal('Config') + this.activateInternal('TxListener') + this.activateInternal('FilePanel') this.activateInternal('Solidity Compile') this.activateInternal('Run') @@ -63,13 +79,12 @@ class PluginManagerComponent { ` } - activatePlugin (name, api) { - let profile = { json: Plugin1Profile, api: pluginManagerApi } + activatePlugin (jsonProfile, api) { + let profile = { json: jsonProfile, api } let plugin = new Plugin(profile, api) this.appManager.addPlugin(plugin) - // Plugin1Profile.location - // mainpanel or swappanel or bottom-bar - // plugin.render() // plugin.create() + this.event.emit('displayableModuleActivated', jsonProfile, plugin.render()) + this.activated[jsonProfile.name] = plugin } activateInternal (name) { @@ -78,12 +93,14 @@ class PluginManagerComponent { if (mod.dep) dep = this.activateInternal(mod.dep) let instance = mod.target if (!instance && mod.Type) instance = new mod.Type(registry, dep) - if (!instance) return console.log('PluginManagerComponent: no Type or instance to add') + if (!instance) return console.log(`PluginManagerComponent: no Type or instance to add: ${JSON.stringify(mod)}`) registry.put({api: instance, name: mod.name.toLocaleLowerCase()}) if (instance.profile && typeof instance.profile === 'function') { this.event.emit('requestActivation', instance.profile(), instance) } - this.event.emit('internalActivated', mod, instance.render()) + if (mod.icon && instance.render && typeof instance.render === 'function') { + this.event.emit('requestContainer', mod, instance.render()) + } // if of type evm-compiler, we forward to the internal components if (mod.class === 'evm-compiler') { this.data.proxy.register(mod, instance) diff --git a/src/app/components/swap-panel-api.js b/src/app/components/swap-panel-api.js index 376f016f10..24cbd40ce5 100644 --- a/src/app/components/swap-panel-api.js +++ b/src/app/components/swap-panel-api.js @@ -13,7 +13,7 @@ class SwapPanelApi { verticalIconsComponent.event.on('showContent', (moduleName) => { this.component.showContent(moduleName) }) - pluginManagerComponent.event.on('internalActivated', (mod, content) => { + pluginManagerComponent.event.on('requestContainer', (mod, content) => { this.add(mod.name, content) }) } diff --git a/src/app/components/vertical-icons-api.js b/src/app/components/vertical-icons-api.js index 3cb69883d5..0ddd3c21bc 100644 --- a/src/app/components/vertical-icons-api.js +++ b/src/app/components/vertical-icons-api.js @@ -10,7 +10,7 @@ class VerticalIconsApi { constructor(verticalIconsComponent, pluginManagerComponent) { this.component = verticalIconsComponent - pluginManagerComponent.event.on('internalActivated', (mod, content) => verticalIconsComponent.addIcon(mod) ) + pluginManagerComponent.event.on('requestContainer', (mod, content) => verticalIconsComponent.addIcon(mod) ) } } module.exports = VerticalIconsApi diff --git a/src/app/editor/SourceHighlighters.js b/src/app/editor/SourceHighlighters.js new file mode 100644 index 0000000000..e5eaa3d92b --- /dev/null +++ b/src/app/editor/SourceHighlighters.js @@ -0,0 +1,35 @@ +'use strict' +var SourceHighlighter = require('./sourceHighlighter') + +module.exports = class SourceHighlighters { + + constructor () { + this.highlighters = {} + } + + profile () { + return { + type: 'sourcehighlighter', + methods: ['highlight', 'discardHighlight'] + } + } + + // TODO what to do with mod? + highlight (mod, lineColumnPos, filePath, hexColor, cb) { + var position + try { + position = JSON.parse(lineColumnPos) + } catch (e) { + return cb(e.message) + } + if (!highlighters[mod]) highlighters[mod] = new SourceHighlighter() + highlighters[mod].currentSourceLocation(null) + highlighters[mod].currentSourceLocationFromfileName(position, filePath, hexColor) + cb() + } + + discardHighlight (mod, cb) { + if (highlighters[mod]) highlighters[mod].currentSourceLocation(null) + cb() + } +} \ No newline at end of file diff --git a/src/app/editor/editor.js b/src/app/editor/editor.js index e2d27838c4..bdb9734a2a 100644 --- a/src/app/editor/editor.js +++ b/src/app/editor/editor.js @@ -7,6 +7,7 @@ var ace = require('brace') require('brace/theme/tomorrow_night_blue') var globalRegistry = require('../../global/registry') +const SourceHighlighters = require('./SourceHighlighters') var Range = ace.acequire('ace/range').Range require('brace/ext/language_tools') @@ -317,6 +318,8 @@ function Editor (opts = {}, localRegistry) { editor.commands.bindKeys({ 'ctrl-t': null }) editor.setShowPrintMargin(false) editor.resize(true) + + this.sourceHighlighters = new SourceHighlighters() } function editorOnChange (self) { diff --git a/src/app/files/browser-files-tree.js b/src/app/files/browser-files-tree.js index bc434e0561..b527d4cd71 100644 --- a/src/app/files/browser-files-tree.js +++ b/src/app/files/browser-files-tree.js @@ -130,6 +130,13 @@ function FilesTree (name, storage) { if (path[0] === '/') return path.substring(1) return path } + + this.profile = function () { + return { + type: this.type, + methods: ['get', 'set', 'remove'] + } + } } module.exports = FilesTree diff --git a/src/app/files/fileManager.js b/src/app/files/fileManager.js index 5361093123..65b8d19cb2 100644 --- a/src/app/files/fileManager.js +++ b/src/app/files/fileManager.js @@ -2,6 +2,7 @@ var $ = require('jquery') var yo = require('yo-yo') +var EventEmitter = require ('events') var EventManager = require('../../lib/events') var globalRegistry = require('../../global/registry') var CompilerImport = require('../compiler/compiler-imports') @@ -15,6 +16,7 @@ class FileManager { constructor (localRegistry) { this.tabbedFiles = {} this.event = new EventManager() + this.nodeEvent = new EventEmitter() this._components = {} this._components.compilerImport = new CompilerImport() this._components.registry = localRegistry || globalRegistry @@ -42,6 +44,18 @@ class FileManager { self._deps.gistExplorer.event.register('fileRemoved', (path) => { this.fileRemovedEvent(path) }) self._deps.localhostExplorer.event.register('errored', (event) => { this.removeTabsOf(self._deps.localhostExplorer) }) self._deps.localhostExplorer.event.register('closed', (event) => { this.removeTabsOf(self._deps.localhostExplorer) }) + + self.event.register('currentFileChanged', (file, provider) => { + this.nodeEvent.emit('currentFileChanged', file) + }) + } + + profile () { + return { + type: 'fileManager', + methods: ['getFilesFromPath', 'getCurrentFile', 'getFile', 'setFile'], + events: ['currentFileChanged'] + } } fileRenamedEvent (oldName, newName, isFolder) { @@ -94,6 +108,41 @@ class FileManager { return path ? path[1] : null } + getCurrentFile (cb) { + var path = this.currentFile() + if (!path) { + cb('no file selected') + } else { + cb(null, path) + } + } + + getFile (path, cb) { + var provider = this.fileProviderOf(path) + if (provider) { + // TODO add approval to user for external plugin to get the content of the given `path` + provider.get(path, (error, content) => { + cb(error, content) + }) + } else { + cb(path + ' not available') + } + } + + setFile (path, content, cb) { + var provider = this.fileProviderOf(path) + if (provider) { + // TODO add approval to user for external plugin to set the content of the given `path` + provider.set(path, content, (error) => { + if (error) return cb(error) + this.syncEditor(path) + cb() + }) + } else { + cb(path + ' not available') + } + } + removeTabsOf (provider) { for (var tab in this.tabbedFiles) { if (this.fileProviderOf(tab).type === provider.type) { diff --git a/src/app/panels/editor-panel.js b/src/app/panels/editor-panel.js index 6906222f01..6dc8f84467 100644 --- a/src/app/panels/editor-panel.js +++ b/src/app/panels/editor-panel.js @@ -15,9 +15,11 @@ var css = styles.css class EditorPanel { constructor (localRegistry) { var self = this + self.event = new EventManager() self._components = {} self._components.registry = localRegistry || globalRegistry - self.event = new EventManager() + self._components.editor = new Editor({}) + self._components.registry.put({api: self._components.editor, name: 'editor'}) } init () { var self = this @@ -38,32 +40,26 @@ class EditorPanel { } } self._view = {} - var editor = new Editor({}) - self._components.registry.put({api: editor, name: 'editor'}) - - var contextualListener = new ContextualListener({editor, pluginManager: self._deps.pluginManager}) - var contextView = new ContextView({contextualListener, editor}) + + var contextualListener = new ContextualListener({editor: self._components.editor, pluginManager: self._deps.pluginManager}) + var contextView = new ContextView({contextualListener, editor: self._components.editor}) - self._components = { - editor: editor, - contextualListener: contextualListener, - contextView: contextView, - // TODO list of compilers is always empty; should find a path to add plugin compiler here - terminal: new Terminal({ - udapp: self._deps.udapp, - compilers: {} - }, - { - getPosition: (event) => { - var limitUp = 36 - var limitDown = 20 - var height = window.innerHeight - var newpos = (event.pageY < limitUp) ? limitUp : event.pageY - newpos = (newpos < height - limitDown) ? newpos : height - limitDown - return newpos - } - }) - } + self._components.contextualListener = contextualListener + self._components.contextView = contextView + self._components.terminal = new Terminal({ + udapp: self._deps.udapp, + compilers: {} + }, + { + getPosition: (event) => { + var limitUp = 36 + var limitDown = 20 + var height = window.innerHeight + var newpos = (event.pageY < limitUp) ? limitUp : event.pageY + newpos = (newpos < height - limitDown) ? newpos : height - limitDown + return newpos + } + }) self._components.terminal.event.register('filterChanged', (type, value) => { this.event.trigger('terminalFilterChanged', [type, value]) diff --git a/src/app/tabs/compile-tab.js b/src/app/tabs/compile-tab.js index e2fba7c881..262a78a4f1 100644 --- a/src/app/tabs/compile-tab.js +++ b/src/app/tabs/compile-tab.js @@ -182,10 +182,13 @@ module.exports = class CompileTab { } }) } + getCompilationResult (cb) { + cb(null, self._components.compiler.lastCompilationResult) + } profile () { return { type: 'solidityCompile', - methods: {}, + methods: ['getCompilationResult'], events: ['compilationFinished'] } } diff --git a/src/universal-dapp.js b/src/universal-dapp.js index 3342478c60..207fdbe1d4 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -29,6 +29,13 @@ function UniversalDApp (registry) { executionContext.event.register('contextChanged', this.resetEnvironment.bind(this)) } +UniversalDApp.prototype.profile = function () { + return { + type: 'udapp', + methods: ['runTestTx', 'getAccounts', 'createVMAccount'] + } +} + UniversalDApp.prototype.resetEnvironment = function () { this.accounts = {} if (executionContext.isVM()) { @@ -64,6 +71,7 @@ UniversalDApp.prototype.resetAPI = function (transactionContextAPI) { } UniversalDApp.prototype.createVMAccount = function (privateKey, balance, cb) { + if (executionContext.getProvider() !== 'vm') return cb('plugin API does not allow creating a new account through web3 connection. Only vm mode is allowed') this._addAccount(privateKey, balance) executionContext.vm().stateManager.cache.flush(function () {}) privateKey = Buffer.from(privateKey, 'hex') @@ -216,6 +224,34 @@ UniversalDApp.prototype.getInputs = function (funABI) { return txHelper.inputParametersDeclarationToString(funABI.inputs) } +/** + * This function send a tx only to javascript VM or testnet, will return an error for the mainnet + * SHOULD BE TAKEN CAREFULLY! + * + * @param {Object} tx - transaction. + * @param {Function} callback - callback. + */ +UniversalDApp.prototype.runTestTx = function (tx, cb) { + executionContext.detectNetwork((error, network) => { + if (error) return cb(error) + if (network.name === 'Main' && network.id === '1') { + return cb('It is not allowed to make this action against mainnet') + } + udapp.silentRunTx(tx, (error, result) => { + if (error) return cb(error) + cb(null, { + transactionHash: result.transactionHash, + status: result.result.status, + gasUsed: '0x' + result.result.gasUsed.toString('hex'), + error: result.result.vm.exceptionError, + return: result.result.vm.return ? '0x' + result.result.vm.return.toString('hex') : '0x', + createdAddress: result.result.createdAddress ? '0x' + result.result.createdAddress.toString('hex') : undefined + }) + }) + }) +} + + /** * This function send a tx without alerting the user (if mainnet or if gas estimation too high). * SHOULD BE TAKEN CAREFULLY! From 0b450f6b0b304fa0ba51cbe27e56ab7b9d2c8e28 Mon Sep 17 00:00:00 2001 From: Rob Stupay Date: Wed, 9 Jan 2019 12:54:21 +0100 Subject: [PATCH 48/50] update with commented out code, errors for discussion and a question --- src/app.js | 34 +++---- src/app/components/plugin-manager-api.js | 24 ++--- .../components/plugin-manager-component.js | 86 +++++++++++------ src/app/components/plugin-manager-proxy.js | 4 +- src/app/components/swap-panel-api.js | 9 +- src/app/components/swap-panel-component.js | 11 +-- src/app/components/vertical-icons-api.js | 13 +-- .../components/vertical-icons-component.js | 92 +++++++++++-------- src/app/editor/SourceHighlighters.js | 50 +++++----- src/app/files/fileManager.js | 4 +- src/app/panels/editor-panel.js | 2 +- src/app/panels/left-icon-panel.js | 1 - src/universal-dapp.js | 1 - 13 files changed, 170 insertions(+), 161 deletions(-) diff --git a/src/app.js b/src/app.js index 61893cf9fa..b8f230dd90 100644 --- a/src/app.js +++ b/src/app.js @@ -39,12 +39,12 @@ const PluginManagerComponent = require('./app/components/plugin-manager-componen const PluginManagerApi = require('./app/components/plugin-manager-api') const VerticalIconsComponent = require('./app/components/vertical-icons-component') -const VerticalIconsApi = require('./app/components/vertical-icons-api') +// const VerticalIconsApi = require('./app/components/vertical-icons-api') const SwapPanelComponent = require('./app/components/swap-panel-component') -const SwapPanelApi = require('./app/components/swap-panel-api') +// const SwapPanelApi = require('./app/components/swap-panel-api') -const AppManager = require('remix-plugin').AppManager +// const AppManager = require('remix-plugin').AppManager var styleGuide = require('./app/ui/styles-guide/theme-chooser') var styles = styleGuide.chooser() @@ -178,7 +178,7 @@ class App { } } _adjustLayout (direction, delta) { - /*var self = this + /* var self = this var layout = self.data._layout[direction] if (layout) { if (delta === undefined) { @@ -211,7 +211,7 @@ class App { methods: ['getExecutionContextProvider', 'getProviderEndpoint', 'detectNetWork', 'addProvider', 'removeProvider'] } } - + render () { var self = this if (self._view.el) return self._view.el @@ -235,7 +235,7 @@ class App { ${''}
` - + self._view.el = yo`
${self._view.iconpanel} @@ -310,12 +310,12 @@ class App { }) } - addProvider (name, url, cb) { + addProvider (name, url, cb) { executionContext.addProvider({ name, url }) cb() } - removeProvider (name, cb) { + removeProvider (name, cb) { executionContext.removeProvider(name) cb() } @@ -403,7 +403,7 @@ Please make a backup of your contracts and start using http://remix.ethereum.org */ const txListenerModuleProxy = { event: new EventEmitter(), - profile() { + profile () { return { type: 'txListener', events: ['newTransaction'] @@ -430,21 +430,21 @@ Please make a backup of your contracts and start using http://remix.ethereum.org // ----------------- Renderer ----------------- var renderer = new Renderer() registry.put({api: renderer, name: 'renderer'}) - + // ----------------- app manager ---------------------------- /* TODOs: - for each activated plugin, an internal module (associated only with the plugin) should be created for accessing specific part of the UI. detail to be discussed - - the current API is not optimal. For instance methods of `app` only refers to `executionContext`, wich does not make really sense. + - the current API is not optimal. For instance methods of `app` only refers to `executionContext`, wich does not make really sense. */ - const appManager = new AppManager({modules: [],plugins : []}) + // const appManager = new AppManager({modules: [], plugins: []}) const swapPanelComponent = new SwapPanelComponent() const pluginManagerComponent = new PluginManagerComponent( - { + { app: this, udapp: udapp, fileManager: fileManager, @@ -455,12 +455,12 @@ Please make a backup of your contracts and start using http://remix.ethereum.org registry.put({api: pluginManagerComponent.proxy(), name: 'pluginmanager'}) self._components.editorpanel.init() - self._components.fileManager.init() - + self._components.fileManager.init() + const verticalIconComponent = new VerticalIconsComponent() - const swapPanelApi = new SwapPanelApi(swapPanelComponent, verticalIconComponent, pluginManagerComponent) - const verticalIconsApi = new VerticalIconsApi(verticalIconComponent, pluginManagerComponent) + // const swapPanelApi = new SwapPanelApi(swapPanelComponent, verticalIconComponent, pluginManagerComponent) + // const verticalIconsApi = new VerticalIconsApi(verticalIconComponent, pluginManagerComponent) const pluginManagerAPI = new PluginManagerApi(pluginManagerComponent) self._view.mainpanel.appendChild(self._components.editorpanel.render()) diff --git a/src/app/components/plugin-manager-api.js b/src/app/components/plugin-manager-api.js index 865c0b3ce6..66acb32242 100644 --- a/src/app/components/plugin-manager-api.js +++ b/src/app/components/plugin-manager-api.js @@ -1,31 +1,19 @@ -var yo = require('yo-yo') -var csjs = require('csjs-inject') -const remixLib = require('remix-lib') - -const styleguide = require('../ui/styles-guide/theme-chooser') -const styles = styleguide.chooser() +// const remixLib = require('remix-lib') class PluginManagerApi { constructor (pluginManagerComponent) { + this.component = pluginManagerComponent pluginManagerComponent.event.on('activation', (item) => this.event.emit('activation', item)) // listen by appManager pluginManagerComponent.event.on('deactivation', (item) => this.event.emit('deactivation', item)) // listen by appManager } - /* - function called by appManager - */ - addItem (item) { - // add to appManager and then render - this.renderItem(item) - } - init (data) { - for (var m in data.modules) { - this.renderItem(item) + for (var m in data.modules) { + this.renderItem(m) } - for (var m in data.plugins) { - this.renderItem(item) + for (var n in data.plugins) { + this.renderItem(n) } } } diff --git a/src/app/components/plugin-manager-component.js b/src/app/components/plugin-manager-component.js index 1ca95bf82f..9f2b842639 100644 --- a/src/app/components/plugin-manager-component.js +++ b/src/app/components/plugin-manager-component.js @@ -12,12 +12,12 @@ const RunTab = require('../tabs/run-tab') var registry = require('../../global/registry') -const styleguide = require('../ui/styles-guide/theme-chooser') -const styles = styleguide.chooser() +// const styleguide = require('../ui/styles-guide/theme-chooser') +// const styles = styleguide.chooser() const PluginManagerProxy = require('./plugin-manager-proxy') -const EventEmitter = require ('events') +const EventEmitter = require('events') class PluginManagerComponent { @@ -44,9 +44,13 @@ class PluginManagerComponent { 'Settings': { name: 'Settings', Type: SettingsTab, icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDUwIDUwIiBoZWlnaHQ9IjUwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MCA1MCIgd2lkdGg9IjUwcHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iNTAiIHdpZHRoPSI1MCIvPjxwYXRoIGQ9Ik00OSwyNy45NTR2LTZsLTcuMTQxLTEuMTY3ICBjLTAuNDIzLTEuNjkxLTEuMDg3LTMuMjgxLTEuOTYyLTQuNzM3bDQuMTYyLTUuOTMybC00LjI0My00LjI0MWwtNS44NTYsNC4yMWMtMS40Ni0wLjg4NC0zLjA2LTEuNTU4LTQuNzYzLTEuOTgybC0xLjI0NS03LjEwNmgtNiAgbC0xLjE1Niw3LjA4M2MtMS43MDQsMC40MTgtMy4zMTMsMS4wODMtNC43NzcsMS45NjNMMTAuMTgsNS44NzNsLTQuMjQzLDQuMjQxbDQuMTA3LDUuODc0Yy0wLjg4OCwxLjQ3LTEuNTYzLDMuMDc3LTEuOTkyLDQuNzkyICBMMSwyMS45NTR2Nmw3LjA0NCwxLjI0OWMwLjQyNSwxLjcxMSwxLjEwMSwzLjMxOCwxLjk5Miw0Ljc5bC00LjE2Myw1LjgyM2w0LjI0MSw0LjI0NWw1Ljg4MS00LjExOSAgYzEuNDY4LDAuODgyLDMuMDczLDEuNTUyLDQuNzc3LDEuOTczbDEuMTgsNy4wODdoNmwxLjI2MS03LjEwNWMxLjY5NS0wLjQzLDMuMjk3LTEuMTA1LDQuNzUxLTEuOTlsNS45MjIsNC4xNTVsNC4yNDItNC4yNDUgIGwtNC4yMjctNS44N2MwLjg3NS0xLjQ1NiwxLjUzOS0zLjA0OCwxLjk1OC00LjczOUw0OSwyNy45NTR6IE0yNSwzM2MtNC40MTgsMC04LTMuNTgyLTgtOHMzLjU4Mi04LDgtOHM4LDMuNTgyLDgsOFMyOS40MTgsMzMsMjUsMzMgIHoiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iMiIvPjwvc3ZnPg==' } } this.activated = {} // list all activated modules + // this.activated = [] // list all activated modules this.plugins = [] this.data = {} this.data.proxy = new PluginManagerProxy() + + // This load the state from localstorage first then from the second parameter is nothing is found in localstorage + // this.store = Store.fromLocal('***', {}) // Or EntityStore.fromLocal('***', {}, 'id') } proxy () { @@ -70,21 +74,52 @@ class PluginManagerComponent { } render () { - var self = this + // var self = this // loop over all this.modules and this.plugins - return yo` + let pluginManagerDiv = yo`
- list all modules / plugins that can be activated +

Plugin Manager

+
+ ` + for (var mod in this.modulesDefinition) { + if (this.modulesDefinition[mod].icon) pluginManagerDiv.appendChild(this.renderItem(mod)) + } + console.log(this.activated) + return pluginManagerDiv + } + + renderItem (item) { + let ctrBtns + if (this.activated.hasOwnProperty(item)) { + ctrBtns = yo` + + ` + } else { + ctrBtns = yo` + + ` + } + this.plugins.push(item) + // var self = this + return yo` +
+ ${this.modulesDefinition[item].name} + ${ctrBtns}
` } activatePlugin (jsonProfile, api) { - let profile = { json: jsonProfile, api } - let plugin = new Plugin(profile, api) - this.appManager.addPlugin(plugin) - this.event.emit('displayableModuleActivated', jsonProfile, plugin.render()) - this.activated[jsonProfile.name] = plugin + // let profile = { json: jsonProfile, api } + // let plugin = new Plugin(profile, api) + // this.appManager.addPlugin(plugin) + // this.event.emit('displayableModuleActivated', jsonProfile, plugin.render()) + // this.activated[jsonProfile.name] = plugin + } + + deactivateInternal (name) { + delete this.activated[name] + this.event.emit('removingItem', name) } activateInternal (name) { @@ -116,32 +151,23 @@ class PluginManagerComponent { _deactivate (item) { this.event.emit('deactivation', item) } - - renderItem (item) { - this.plugins.push(item) - var self = this - var view = yo` -
- ${item.name} - ${item.url} - - -
- ` - } } module.exports = PluginManagerComponent const css = csjs` - .plugins { + .plugins { width : 300px; } - .plugItIn { - display : none; + .plugin { + border-bottom: 1px black solid; + padding: 10px 20px; + margin-bottom: 20px; + } + .plugItIn.active { + display: block; } - .plugItIn.active { - display :block; + .plugin button { + cursor: pointer; } - .clearFunds { background-color: lightblue; } ` diff --git a/src/app/components/plugin-manager-proxy.js b/src/app/components/plugin-manager-proxy.js index f4849aa534..2aa9508235 100644 --- a/src/app/components/plugin-manager-proxy.js +++ b/src/app/components/plugin-manager-proxy.js @@ -1,5 +1,3 @@ -var yo = require('yo-yo') - var registry = require('../../global/registry') const CompilerAbstract = require('../compiler/compiler-abstract') @@ -9,7 +7,7 @@ const EventManager = require('remix-lib').EventManager class PluginManagerProxy { constructor () { - this.event = new EventManager + this.event = new EventManager() } register (mod, instance) { diff --git a/src/app/components/swap-panel-api.js b/src/app/components/swap-panel-api.js index 24cbd40ce5..9a37f76075 100644 --- a/src/app/components/swap-panel-api.js +++ b/src/app/components/swap-panel-api.js @@ -1,11 +1,4 @@ -var yo = require('yo-yo') -var csjs = require('csjs-inject') -const remixLib = require('remix-lib') - -const styleguide = require('../ui/styles-guide/theme-chooser') -const styles = styleguide.chooser() - -const EventEmmitter = require('events') +// const EventEmmitter = require('events') class SwapPanelApi { constructor (swapPanelComponent, verticalIconsComponent, pluginManagerComponent) { diff --git a/src/app/components/swap-panel-component.js b/src/app/components/swap-panel-component.js index c10bfbe715..525c2e8667 100644 --- a/src/app/components/swap-panel-component.js +++ b/src/app/components/swap-panel-component.js @@ -1,9 +1,9 @@ var yo = require('yo-yo') var csjs = require('csjs-inject') -const remixLib = require('remix-lib') +// const remixLib = require('remix-lib') -const styleguide = require('../ui/styles-guide/theme-chooser') -const styles = styleguide.chooser() +// const styleguide = require('../ui/styles-guide/theme-chooser') +// const styles = styleguide.chooser() class SwapPanelComponent { constructor () { @@ -26,15 +26,14 @@ class SwapPanelComponent { console.log(`${moduleName} not found`) } - add (moduleName, content) { this.contents[moduleName] = yo`
${content}
` this.view.appendChild(this.contents[moduleName]) this.showContent(moduleName) - } + } render () { - this.view = yo` + this.view = yo`
` diff --git a/src/app/components/vertical-icons-api.js b/src/app/components/vertical-icons-api.js index 0ddd3c21bc..3c4ba39ef0 100644 --- a/src/app/components/vertical-icons-api.js +++ b/src/app/components/vertical-icons-api.js @@ -1,16 +1,9 @@ -var yo = require('yo-yo') -var csjs = require('csjs-inject') -const remixLib = require('remix-lib') - -const styleguide = require('../ui/styles-guide/theme-chooser') -const styles = styleguide.chooser() - // API class VerticalIconsApi { - constructor(verticalIconsComponent, pluginManagerComponent) { + constructor (verticalIconsComponent, pluginManagerComponent) { this.component = verticalIconsComponent - pluginManagerComponent.event.on('requestContainer', (mod, content) => verticalIconsComponent.addIcon(mod) ) - } + pluginManagerComponent.event.on('requestContainer', (mod, content) => verticalIconsComponent.addIcon(mod)) + } } module.exports = VerticalIconsApi diff --git a/src/app/components/vertical-icons-component.js b/src/app/components/vertical-icons-component.js index 3364da36e9..6977234217 100644 --- a/src/app/components/vertical-icons-component.js +++ b/src/app/components/vertical-icons-component.js @@ -1,44 +1,58 @@ var yo = require('yo-yo') var csjs = require('csjs-inject') -const remixLib = require('remix-lib') +// const remixLib = require('remix-lib') -const styleguide = require('../ui/styles-guide/theme-chooser') -const styles = styleguide.chooser() +// const styleguide = require('../ui/styles-guide/theme-chooser') +// const styles = styleguide.chooser() const EventEmmitter = require('events') - - // Component - class VerticalIconComponent { - - constructor() { - this.event = new EventEmmitter - } - - addIcon (mod) { - let self = this - this.view.appendChild(yo`
{ self._iconClick(mod.name)}} title=${mod.name}>${mod.name}
`) - } - - removeIcon (item) { - - } - - select (name) { - this.event.emit('showContent', name) - } - - _iconClick (name) { - // called when an icon has been clicked - this.event.emit('showContent', name) - } - - render() { - this.view = yo` -
-
- ` - return this.view - } - } - - module.exports = VerticalIconComponent + +// Component +class VerticalIconComponent { + + constructor () { + this.event = new EventEmmitter() + } + + addIcon (mod) { + let self = this + this.view.appendChild(yo`
{ self._iconClick(mod.name) }} title=${mod.name}>${mod.name}
`) + } + + removeIcon (item) { + + } + + select (name) { + this.event.emit('showContent', name) + } + + _iconClick (name) { + // called when an icon has been clicked + this.event.emit('showContent', name) + } + + render () { + this.view = yo` +
+
+ ` + return this.view + } +} + +module.exports = VerticalIconComponent + +const css = csjs` + .icons { + margin-left: 10px; + margin-top: 15px; + } + .icon { + cursor: pointer; + margin-bottom: 25px; + } + .icon img { + width: 25px; + } +` diff --git a/src/app/editor/SourceHighlighters.js b/src/app/editor/SourceHighlighters.js index e5eaa3d92b..8902235fe5 100644 --- a/src/app/editor/SourceHighlighters.js +++ b/src/app/editor/SourceHighlighters.js @@ -3,33 +3,33 @@ var SourceHighlighter = require('./sourceHighlighter') module.exports = class SourceHighlighters { - constructor () { - this.highlighters = {} - } + constructor () { + this.highlighters = {} + } - profile () { - return { - type: 'sourcehighlighter', - methods: ['highlight', 'discardHighlight'] - } + profile () { + return { + type: 'sourcehighlighter', + methods: ['highlight', 'discardHighlight'] } + } - // TODO what to do with mod? - highlight (mod, lineColumnPos, filePath, hexColor, cb) { - var position - try { - position = JSON.parse(lineColumnPos) - } catch (e) { - return cb(e.message) - } - if (!highlighters[mod]) highlighters[mod] = new SourceHighlighter() - highlighters[mod].currentSourceLocation(null) - highlighters[mod].currentSourceLocationFromfileName(position, filePath, hexColor) - cb() + // TODO what to do with mod? + highlight (mod, lineColumnPos, filePath, hexColor, cb) { + var position + try { + position = JSON.parse(lineColumnPos) + } catch (e) { + return cb(e.message) } + if (!highlighters[mod]) highlighters[mod] = new SourceHighlighter() + highlighters[mod].currentSourceLocation(null) + highlighters[mod].currentSourceLocationFromfileName(position, filePath, hexColor) + cb() + } - discardHighlight (mod, cb) { - if (highlighters[mod]) highlighters[mod].currentSourceLocation(null) - cb() - } -} \ No newline at end of file + discardHighlight (mod, cb) { + if (highlighters[mod]) highlighters[mod].currentSourceLocation(null) + cb() + } +} diff --git a/src/app/files/fileManager.js b/src/app/files/fileManager.js index 65b8d19cb2..a32ef613c8 100644 --- a/src/app/files/fileManager.js +++ b/src/app/files/fileManager.js @@ -2,7 +2,7 @@ var $ = require('jquery') var yo = require('yo-yo') -var EventEmitter = require ('events') +var EventEmitter = require('events') var EventManager = require('../../lib/events') var globalRegistry = require('../../global/registry') var CompilerImport = require('../compiler/compiler-imports') @@ -116,7 +116,7 @@ class FileManager { cb(null, path) } } - + getFile (path, cb) { var provider = this.fileProviderOf(path) if (provider) { diff --git a/src/app/panels/editor-panel.js b/src/app/panels/editor-panel.js index 6dc8f84467..6a8fda6d07 100644 --- a/src/app/panels/editor-panel.js +++ b/src/app/panels/editor-panel.js @@ -40,7 +40,7 @@ class EditorPanel { } } self._view = {} - + var contextualListener = new ContextualListener({editor: self._components.editor, pluginManager: self._deps.pluginManager}) var contextView = new ContextView({contextualListener, editor: self._components.editor}) diff --git a/src/app/panels/left-icon-panel.js b/src/app/panels/left-icon-panel.js index c68a84235f..e6121dad3a 100644 --- a/src/app/panels/left-icon-panel.js +++ b/src/app/panels/left-icon-panel.js @@ -69,7 +69,6 @@ module.exports = class LeftIconPanel { {type: 'settings', displayName: 'setting', icon: 'fa fa-cog fa-2x'} ] } - swapPlugin (name) { console.log(name) diff --git a/src/universal-dapp.js b/src/universal-dapp.js index 207fdbe1d4..94b8f06341 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -251,7 +251,6 @@ UniversalDApp.prototype.runTestTx = function (tx, cb) { }) } - /** * This function send a tx without alerting the user (if mainnet or if gas estimation too high). * SHOULD BE TAKEN CAREFULLY! From a2d65662b1dd055c8319e8639bb0559ec649c266 Mon Sep 17 00:00:00 2001 From: Rob Stupay Date: Wed, 9 Jan 2019 14:07:36 +0100 Subject: [PATCH 49/50] update with buttons - in progress --- src/app.js | 12 +++--- .../components/plugin-manager-component.js | 39 ++++++++++++------- src/app/components/swap-panel-component.js | 1 - 3 files changed, 30 insertions(+), 22 deletions(-) diff --git a/src/app.js b/src/app.js index b8f230dd90..87379f67a3 100644 --- a/src/app.js +++ b/src/app.js @@ -39,12 +39,12 @@ const PluginManagerComponent = require('./app/components/plugin-manager-componen const PluginManagerApi = require('./app/components/plugin-manager-api') const VerticalIconsComponent = require('./app/components/vertical-icons-component') -// const VerticalIconsApi = require('./app/components/vertical-icons-api') +const VerticalIconsApi = require('./app/components/vertical-icons-api') const SwapPanelComponent = require('./app/components/swap-panel-component') -// const SwapPanelApi = require('./app/components/swap-panel-api') +const SwapPanelApi = require('./app/components/swap-panel-api') -// const AppManager = require('remix-plugin').AppManager +const AppManager = require('remix-plugin').AppManager var styleGuide = require('./app/ui/styles-guide/theme-chooser') var styles = styleGuide.chooser() @@ -440,7 +440,7 @@ Please make a backup of your contracts and start using http://remix.ethereum.org - the current API is not optimal. For instance methods of `app` only refers to `executionContext`, wich does not make really sense. */ - // const appManager = new AppManager({modules: [], plugins: []}) + const appManager = new AppManager({modules: [], plugins: []}) const swapPanelComponent = new SwapPanelComponent() const pluginManagerComponent = new PluginManagerComponent( @@ -459,8 +459,8 @@ Please make a backup of your contracts and start using http://remix.ethereum.org const verticalIconComponent = new VerticalIconsComponent() - // const swapPanelApi = new SwapPanelApi(swapPanelComponent, verticalIconComponent, pluginManagerComponent) - // const verticalIconsApi = new VerticalIconsApi(verticalIconComponent, pluginManagerComponent) + const swapPanelApi = new SwapPanelApi(swapPanelComponent, verticalIconComponent, pluginManagerComponent) + const verticalIconsApi = new VerticalIconsApi(verticalIconComponent, pluginManagerComponent) const pluginManagerAPI = new PluginManagerApi(pluginManagerComponent) self._view.mainpanel.appendChild(self._components.editorpanel.render()) diff --git a/src/app/components/plugin-manager-component.js b/src/app/components/plugin-manager-component.js index 9f2b842639..6502efd8e6 100644 --- a/src/app/components/plugin-manager-component.js +++ b/src/app/components/plugin-manager-component.js @@ -33,14 +33,14 @@ class PluginManagerComponent { 'Config': { name: 'Config', target: config }, 'TxListener': { name: 'TxListener', target: txListener }, // internal components. They are mostly views, they don't provide external API for plugins - 'Solidity Compile': { name: 'Solidity Compile', class: 'evm-compiler', Type: CompileTab, icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDUwIDUwIiBoZWlnaHQ9IjUwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MCA1MCIgd2lkdGg9IjUwcHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iNTAiIHdpZHRoPSI1MCIvPjxwYXRoIGQ9Ik00NiwxNXYtNCAgYzAtMS4xMDQtMC44OTYtMi0yLTJjMCwwLTI0LjY0OCwwLTI2LDBjLTEuNDY5LDAtMi40ODQtNC00LTRIM0MxLjg5Niw1LDEsNS44OTYsMSw3djR2Mjl2NGMwLDEuMTA0LDAuODk2LDIsMiwyaDM5ICBjMS4xMDQsMCwyLTAuODk2LDItMiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLXdpZHRoPSIyIi8+PHBhdGggZD0iTTEsNDRsNS0yNyAgYzAtMS4xMDQsMC44OTYtMiwyLTJoMzljMS4xMDQsMCwyLDAuODk2LDIsMmwtNSwyNyIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLXdpZHRoPSIyIi8+PC9zdmc+' }, + 'SolidityCompile': { name: 'Solidity Compile', class: 'evm-compiler', Type: CompileTab, icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDUwIDUwIiBoZWlnaHQ9IjUwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MCA1MCIgd2lkdGg9IjUwcHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iNTAiIHdpZHRoPSI1MCIvPjxwYXRoIGQ9Ik00NiwxNXYtNCAgYzAtMS4xMDQtMC44OTYtMi0yLTJjMCwwLTI0LjY0OCwwLTI2LDBjLTEuNDY5LDAtMi40ODQtNC00LTRIM0MxLjg5Niw1LDEsNS44OTYsMSw3djR2Mjl2NGMwLDEuMTA0LDAuODk2LDIsMiwyaDM5ICBjMS4xMDQsMCwyLTAuODk2LDItMiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLXdpZHRoPSIyIi8+PHBhdGggZD0iTTEsNDRsNS0yNyAgYzAtMS4xMDQsMC44OTYtMiwyLTJoMzljMS4xMDQsMCwyLDAuODk2LDIsMmwtNSwyNyIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLXdpZHRoPSIyIi8+PC9zdmc+' }, 'FilePanel': { name: 'FilePanel', Type: FilePanel, icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDUwIDUwIiBoZWlnaHQ9IjUwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MCA1MCIgd2lkdGg9IjUwcHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iNTAiIHdpZHRoPSI1MCIvPjxwb2x5bGluZSBmaWxsPSJub25lIiBwb2ludHM9IjQ0LDIxIDQ0LDQ5IDYsNDkgICA2LDIxICIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLXdpZHRoPSIyIi8+PHBvbHlsaW5lIGZpbGw9Im5vbmUiIHBvaW50cz0iMTksNDkgMTksMjggMzEsMjggICAzMSw0OSAiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iMiIvPjxwb2x5Z29uIHBvaW50cz0iMzUsNSAzNSw4LjAxNiAzNywxMC4wOTQgMzcsNyAzOSw3IDM5LDEyLjIwMyA0MSwxNC4yNjYgNDEsNSAiLz48cG9seWxpbmUgZmlsbD0ibm9uZSIgcG9pbnRzPSIgIDEuMTEsMjUuOTQyIDI1LDEuMDUzIDQ4Ljg5LDI1Ljk0MyAiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iMiIvPjwvc3ZnPg==' }, 'Test': { name: 'Test', dep: 'Solidity Compile', Type: TestTab, icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDUwIDUwIiBoZWlnaHQ9IjUwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MCA1MCIgd2lkdGg9IjUwcHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iNTAiIHdpZHRoPSI1MCIvPjxwYXRoIGQ9Ik00OSw0djI1YzAsMC01LjI3MywzLTEyLDMgIGMtMTEuOTI5LDAtMTUuODY5LTQtMjQtNFMxLDMwLDEsMzBWM2MwLDAsMi4wODUtMiwxMi0yczE0LjA0Nyw2LDI0LDZDNDMuMjgxLDcsNDguMTMsNC40NzEsNDksNHoiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iMiIvPjxsaW5lIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iMiIgeDE9IjEiIHgyPSIxIiB5MT0iMyIgeTI9IjQ5Ii8+PC9zdmc+' }, 'Run': { name: 'Run', Type: RunTab, icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDUwIDUwIiBoZWlnaHQ9IjUwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MCA1MCIgd2lkdGg9IjUwcHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iNTAiIHdpZHRoPSI1MCIvPjxwYXRoIGQ9Ik00My43MzQsMjFjLTMuNjMxLDAtMTUuMDkyLDAtMTUuMDkyLDAgIFMxNi4yNSw1LjE4OCwxNi4wNDcsNC45MzhzLTAuNDIyLTAuNTk0LTEuMTI1LTAuNjcyYy0wLjg1OS0wLjA5NS0xLjk2OS0wLjIwMy0yLjMyOC0wLjIzNGMtMC40MDYtMC4wMzUtMC43MTksMC4xNDEtMC40OTYsMC43MzQgIEMxMi4zODgsNS41MzksMTguNzQ4LDIxLDE4Ljc0OCwyMUg2LjAzNGMwLDAtMi40NTgtNC43MjItMi44NzgtNS41MzFDMi45NjUsMTUuMTAxLDIuNTU3LDE1LjAxNCwyLDE1SDEuMjk3ICBjLTAuMTI1LDAtMC4zMTIsMC0wLjI4MSwwLjM0NEMxLjA1OCwxNS44MTEsMywyNSwzLDI1cy0xLjg4OCw5LjE5Ny0xLjk4NCw5LjY1NkMwLjk1MywzNC45NTMsMS4xNzIsMzUsMS4yOTcsMzVIMiAgYzAuOTY2LTAuMDA5LDAuOTU0LTAuMDc5LDEuMTU2LTAuNDY5QzMuNTc2LDMzLjcyMiw2LjAzNCwyOSw2LjAzNCwyOWgxMi43MTRjMCwwLTYuMzYsMTUuNDYxLTYuNjUsMTYuMjM0ICBjLTAuMjIzLDAuNTk0LDAuMDksMC43NywwLjQ5NiwwLjczNGMwLjM1OS0wLjAzMSwxLjQ2OS0wLjEzOSwyLjMyOC0wLjIzNGMwLjcwMy0wLjA3OCwwLjkyMi0wLjQyMiwxLjEyNS0wLjY3MlMyOC42NDMsMjksMjguNjQzLDI5ICBzMTEuNDYxLDAsMTUuMDkyLDBjMy43NjYsMCw1LjI2NC0zLjAzMSw1LjI2NC00UzQ3LjQ4NCwyMSw0My43MzQsMjF6IiBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLXdpZHRoPSIyIi8+PC9zdmc+' }, - 'Solidity Static Analysis': { name: 'Solidity Static Analysis', Type: AnalysisTab, icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDUwIDUwIiBoZWlnaHQ9IjUwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MCA1MCIgd2lkdGg9IjUwcHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iNTAiIHdpZHRoPSI1MCIvPjxjaXJjbGUgY3g9IjIiIGN5PSIyNSIgcj0iMiIvPjxjaXJjbGUgY3g9IjE1IiBjeT0iMTkiIHI9IjIiLz48Y2lyY2xlIGN4PSIyNSIgY3k9IjExIiByPSIyIi8+PGNpcmNsZSBjeD0iMzUiIGN5PSIxNyIgcj0iMiIvPjxjaXJjbGUgY3g9IjQ4IiBjeT0iNSIgcj0iMiIvPjxjaXJjbGUgY3g9IjIiIGN5PSIzOSIgcj0iMiIvPjxjaXJjbGUgY3g9IjE1IiBjeT0iNDEiIHI9IjIiLz48Y2lyY2xlIGN4PSIyNSIgY3k9IjMzIiByPSIyIi8+PGNpcmNsZSBjeD0iMzUiIGN5PSI0MyIgcj0iMiIvPjxjaXJjbGUgY3g9IjQ4IiBjeT0iMzEiIHI9IjIiLz48cG9seWxpbmUgZmlsbD0ibm9uZSIgcG9pbnRzPSIyLDI1IDE1LDE5IDI1LDExICAgMzUsMTcgNDgsNSAiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iMiIvPjxwb2x5bGluZSBmaWxsPSJub25lIiBwb2ludHM9IjIsMzkgMTUsNDEgMjUsMzMgICAzNSw0MyA0OCwzMSAiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iMiIvPjwvc3ZnPg==' }, + 'SolidityStaticAnalysis': { name: 'Solidity Static Analysis', Type: AnalysisTab, icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDUwIDUwIiBoZWlnaHQ9IjUwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MCA1MCIgd2lkdGg9IjUwcHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iNTAiIHdpZHRoPSI1MCIvPjxjaXJjbGUgY3g9IjIiIGN5PSIyNSIgcj0iMiIvPjxjaXJjbGUgY3g9IjE1IiBjeT0iMTkiIHI9IjIiLz48Y2lyY2xlIGN4PSIyNSIgY3k9IjExIiByPSIyIi8+PGNpcmNsZSBjeD0iMzUiIGN5PSIxNyIgcj0iMiIvPjxjaXJjbGUgY3g9IjQ4IiBjeT0iNSIgcj0iMiIvPjxjaXJjbGUgY3g9IjIiIGN5PSIzOSIgcj0iMiIvPjxjaXJjbGUgY3g9IjE1IiBjeT0iNDEiIHI9IjIiLz48Y2lyY2xlIGN4PSIyNSIgY3k9IjMzIiByPSIyIi8+PGNpcmNsZSBjeD0iMzUiIGN5PSI0MyIgcj0iMiIvPjxjaXJjbGUgY3g9IjQ4IiBjeT0iMzEiIHI9IjIiLz48cG9seWxpbmUgZmlsbD0ibm9uZSIgcG9pbnRzPSIyLDI1IDE1LDE5IDI1LDExICAgMzUsMTcgNDgsNSAiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iMiIvPjxwb2x5bGluZSBmaWxsPSJub25lIiBwb2ludHM9IjIsMzkgMTUsNDEgMjUsMzMgICAzNSw0MyA0OCwzMSAiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iMiIvPjwvc3ZnPg==' }, 'Debugger': { name: 'Debugger', Type: DebuggerTab, icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDUwIDUwIiBoZWlnaHQ9IjUwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MCA1MCIgd2lkdGg9IjUwcHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iNTAiIHdpZHRoPSI1MCIvPjxwYXRoIGQ9Ik00NiwxNXYtNCAgYzAtMS4xMDQtMC44OTYtMi0yLTJjMCwwLTI0LjY0OCwwLTI2LDBjLTEuNDY5LDAtMi40ODQtNC00LTRIM0MxLjg5Niw1LDEsNS44OTYsMSw3djR2Mjl2NGMwLDEuMTA0LDAuODk2LDIsMiwyaDM5ICBjMS4xMDQsMCwyLTAuODk2LDItMiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLXdpZHRoPSIyIi8+PHBhdGggZD0iTTEsNDRsNS0yNyAgYzAtMS4xMDQsMC44OTYtMiwyLTJoMzljMS4xMDQsMCwyLDAuODk2LDIsMmwtNSwyNyIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLXdpZHRoPSIyIi8+PC9zdmc+' }, 'Support': { name: 'Support', Type: SupportTab, icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDUwIDUwIiBoZWlnaHQ9IjUwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MCA1MCIgd2lkdGg9IjUwcHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iNTAiIHdpZHRoPSI1MCIvPjxwYXRoIGQ9Ik0zMC45MzMsMzIuNTI4Yy0wLjE0Ni0xLjYxMi0wLjA5LTIuNzM3LTAuMDktNC4yMWMwLjczLTAuMzgzLDIuMDM4LTIuODI1LDIuMjU5LTQuODg4YzAuNTc0LTAuMDQ3LDEuNDc5LTAuNjA3LDEuNzQ0LTIuODE4ICBjMC4xNDMtMS4xODctMC40MjUtMS44NTUtMC43NzEtMi4wNjVjMC45MzQtMi44MDksMi44NzQtMTEuNDk5LTMuNTg4LTEyLjM5N2MtMC42NjUtMS4xNjgtMi4zNjgtMS43NTktNC41ODEtMS43NTkgIGMtOC44NTQsMC4xNjMtOS45MjIsNi42ODYtNy45ODEsMTQuMTU2Yy0wLjM0NSwwLjIxLTAuOTEzLDAuODc4LTAuNzcxLDIuMDY1YzAuMjY2LDIuMjExLDEuMTcsMi43NzEsMS43NDQsMi44MTggIGMwLjIyLDIuMDYyLDEuNTgsNC41MDUsMi4zMTIsNC44ODhjMCwxLjQ3MywwLjA1NSwyLjU5OC0wLjA5MSw0LjIxQzE5LjM2NywzNy4yMzgsNy41NDYsMzUuOTE2LDcsNDVoMzggIEM0NC40NTUsMzUuOTE2LDMyLjY4NSwzNy4yMzgsMzAuOTMzLDMyLjUyOHoiLz48L3N2Zz4=' }, - 'Plugin Manager': { name: 'Plugin Manager', target: this, icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDUwIDUwIiBoZWlnaHQ9IjUwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MCA1MCIgd2lkdGg9IjUwcHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iNTAiIHdpZHRoPSI1MCIvPjxwYXRoIGQ9IiAgTTMyLDM1YzAsMCw4LjMxMiwwLDkuMDk4LDBDNDUuNDYzLDM1LDQ5LDMxLjQ2Myw0OSwyNy4wOTlzLTMuNTM3LTcuOTAyLTcuOTAyLTcuOTAyYy0wLjAyLDAtMC4wMzgsMC4wMDMtMC4wNTgsMC4wMDMgIGMwLjA2MS0wLjQ5NCwwLjEwMy0wLjk5NCwwLjEwMy0xLjUwNGMwLTYuNzEtNS40MzktMTIuMTUtMTIuMTUtMTIuMTVjLTUuMjI5LDAtOS42NzIsMy4zMDktMTEuMzg2LDcuOTQxICBjLTEuMDg3LTEuMDg5LTIuNTkxLTEuNzY0LTQuMjUxLTEuNzY0Yy0zLjMxOSwwLTYuMDA5LDIuNjktNi4wMDksNi4wMDhjMCwwLjA4NSwwLjAxLDAuMTY3LDAuMDEzLDAuMjUxICBDMy42OTUsMTguOTk1LDEsMjIuMzQ0LDEsMjYuMzMxQzEsMzEuMTE5LDQuODgxLDM1LDkuNjcsMzVjMC44MjcsMCw4LjMzLDAsOC4zMywwIiBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2Utd2lkdGg9IjIiLz48cG9seWxpbmUgZmlsbD0ibm9uZSIgcG9pbnRzPSIzMCw0MSAyNSw0NiAyMCw0MSAgICIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLXdpZHRoPSIyIi8+PGxpbmUgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLXdpZHRoPSIyIiB4MT0iMjUiIHgyPSIyNSIgeTE9IjI2IiB5Mj0iNDUuNjY4Ii8+PC9zdmc+' }, + 'PluginManager': { name: 'Plugin Manager', target: this, icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDUwIDUwIiBoZWlnaHQ9IjUwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MCA1MCIgd2lkdGg9IjUwcHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iNTAiIHdpZHRoPSI1MCIvPjxwYXRoIGQ9IiAgTTMyLDM1YzAsMCw4LjMxMiwwLDkuMDk4LDBDNDUuNDYzLDM1LDQ5LDMxLjQ2Myw0OSwyNy4wOTlzLTMuNTM3LTcuOTAyLTcuOTAyLTcuOTAyYy0wLjAyLDAtMC4wMzgsMC4wMDMtMC4wNTgsMC4wMDMgIGMwLjA2MS0wLjQ5NCwwLjEwMy0wLjk5NCwwLjEwMy0xLjUwNGMwLTYuNzEtNS40MzktMTIuMTUtMTIuMTUtMTIuMTVjLTUuMjI5LDAtOS42NzIsMy4zMDktMTEuMzg2LDcuOTQxICBjLTEuMDg3LTEuMDg5LTIuNTkxLTEuNzY0LTQuMjUxLTEuNzY0Yy0zLjMxOSwwLTYuMDA5LDIuNjktNi4wMDksNi4wMDhjMCwwLjA4NSwwLjAxLDAuMTY3LDAuMDEzLDAuMjUxICBDMy42OTUsMTguOTk1LDEsMjIuMzQ0LDEsMjYuMzMxQzEsMzEuMTE5LDQuODgxLDM1LDkuNjcsMzVjMC44MjcsMCw4LjMzLDAsOC4zMywwIiBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2Utd2lkdGg9IjIiLz48cG9seWxpbmUgZmlsbD0ibm9uZSIgcG9pbnRzPSIzMCw0MSAyNSw0NiAyMCw0MSAgICIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLXdpZHRoPSIyIi8+PGxpbmUgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLXdpZHRoPSIyIiB4MT0iMjUiIHgyPSIyNSIgeTE9IjI2IiB5Mj0iNDUuNjY4Ii8+PC9zdmc+' }, 'Settings': { name: 'Settings', Type: SettingsTab, icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDUwIDUwIiBoZWlnaHQ9IjUwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MCA1MCIgd2lkdGg9IjUwcHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iNTAiIHdpZHRoPSI1MCIvPjxwYXRoIGQ9Ik00OSwyNy45NTR2LTZsLTcuMTQxLTEuMTY3ICBjLTAuNDIzLTEuNjkxLTEuMDg3LTMuMjgxLTEuOTYyLTQuNzM3bDQuMTYyLTUuOTMybC00LjI0My00LjI0MWwtNS44NTYsNC4yMWMtMS40Ni0wLjg4NC0zLjA2LTEuNTU4LTQuNzYzLTEuOTgybC0xLjI0NS03LjEwNmgtNiAgbC0xLjE1Niw3LjA4M2MtMS43MDQsMC40MTgtMy4zMTMsMS4wODMtNC43NzcsMS45NjNMMTAuMTgsNS44NzNsLTQuMjQzLDQuMjQxbDQuMTA3LDUuODc0Yy0wLjg4OCwxLjQ3LTEuNTYzLDMuMDc3LTEuOTkyLDQuNzkyICBMMSwyMS45NTR2Nmw3LjA0NCwxLjI0OWMwLjQyNSwxLjcxMSwxLjEwMSwzLjMxOCwxLjk5Miw0Ljc5bC00LjE2Myw1LjgyM2w0LjI0MSw0LjI0NWw1Ljg4MS00LjExOSAgYzEuNDY4LDAuODgyLDMuMDczLDEuNTUyLDQuNzc3LDEuOTczbDEuMTgsNy4wODdoNmwxLjI2MS03LjEwNWMxLjY5NS0wLjQzLDMuMjk3LTEuMTA1LDQuNzUxLTEuOTlsNS45MjIsNC4xNTVsNC4yNDItNC4yNDUgIGwtNC4yMjctNS44N2MwLjg3NS0xLjQ1NiwxLjUzOS0zLjA0OCwxLjk1OC00LjczOUw0OSwyNy45NTR6IE0yNSwzM2MtNC40MTgsMC04LTMuNTgyLTgtOHMzLjU4Mi04LDgtOHM4LDMuNTgyLDgsOFMyOS40MTgsMzMsMjUsMzMgIHoiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iMiIvPjwvc3ZnPg==' } } this.activated = {} // list all activated modules @@ -66,9 +66,9 @@ class PluginManagerComponent { this.activateInternal('TxListener') this.activateInternal('FilePanel') - this.activateInternal('Solidity Compile') + this.activateInternal('SolidityCompile') this.activateInternal('Run') - this.activateInternal('Plugin Manager') + this.activateInternal('PluginManager') this.activateInternal('Settings') this.activateInternal('Support') } @@ -82,7 +82,9 @@ class PluginManagerComponent {
` for (var mod in this.modulesDefinition) { - if (this.modulesDefinition[mod].icon) pluginManagerDiv.appendChild(this.renderItem(mod)) + if (this.modulesDefinition[mod].icon) { + pluginManagerDiv.appendChild(this.renderItem(mod)) + } } console.log(this.activated) return pluginManagerDiv @@ -90,23 +92,28 @@ class PluginManagerComponent { renderItem (item) { let ctrBtns - if (this.activated.hasOwnProperty(item)) { - ctrBtns = yo` - - ` - } else { - ctrBtns = yo` - - ` + + let action = (event) => { + if (this.activated.hasOwnProperty(item)) { + this.deactivateInternal(item) + } else { + this.activateInternal(item) + } } + + ctrBtns = yo`
+ +
` + this.plugins.push(item) // var self = this - return yo` + this.view = yo`
${this.modulesDefinition[item].name} ${ctrBtns}
` + return this.view } activatePlugin (jsonProfile, api) { @@ -120,6 +127,7 @@ class PluginManagerComponent { deactivateInternal (name) { delete this.activated[name] this.event.emit('removingItem', name) + if (this.view) this.view.querySelector(`#${name} button`).innerHTML = 'activate' } activateInternal (name) { @@ -141,6 +149,7 @@ class PluginManagerComponent { this.data.proxy.register(mod, instance) } this.activated[mod.name] = mod + if (this.view) this.view.querySelector(`#${name} button`).innerHTML = 'deactivate' return instance } diff --git a/src/app/components/swap-panel-component.js b/src/app/components/swap-panel-component.js index 525c2e8667..683e712e7d 100644 --- a/src/app/components/swap-panel-component.js +++ b/src/app/components/swap-panel-component.js @@ -29,7 +29,6 @@ class SwapPanelComponent { add (moduleName, content) { this.contents[moduleName] = yo`
${content}
` this.view.appendChild(this.contents[moduleName]) - this.showContent(moduleName) } render () { From 533ada1ddb9cd1aa6f1b01b65f87a1e56caeb0b6 Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 9 Jan 2019 14:44:19 +0100 Subject: [PATCH 50/50] fixing adding removing module --- .../components/plugin-manager-component.js | 20 ++++++++++--------- src/app/components/swap-panel-api.js | 7 +++++++ src/app/components/swap-panel-component.js | 7 ++++++- src/app/components/vertical-icons-api.js | 1 + .../components/vertical-icons-component.js | 7 ++++--- 5 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/app/components/plugin-manager-component.js b/src/app/components/plugin-manager-component.js index 6502efd8e6..f4cf2bd18e 100644 --- a/src/app/components/plugin-manager-component.js +++ b/src/app/components/plugin-manager-component.js @@ -33,14 +33,14 @@ class PluginManagerComponent { 'Config': { name: 'Config', target: config }, 'TxListener': { name: 'TxListener', target: txListener }, // internal components. They are mostly views, they don't provide external API for plugins - 'SolidityCompile': { name: 'Solidity Compile', class: 'evm-compiler', Type: CompileTab, icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDUwIDUwIiBoZWlnaHQ9IjUwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MCA1MCIgd2lkdGg9IjUwcHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iNTAiIHdpZHRoPSI1MCIvPjxwYXRoIGQ9Ik00NiwxNXYtNCAgYzAtMS4xMDQtMC44OTYtMi0yLTJjMCwwLTI0LjY0OCwwLTI2LDBjLTEuNDY5LDAtMi40ODQtNC00LTRIM0MxLjg5Niw1LDEsNS44OTYsMSw3djR2Mjl2NGMwLDEuMTA0LDAuODk2LDIsMiwyaDM5ICBjMS4xMDQsMCwyLTAuODk2LDItMiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLXdpZHRoPSIyIi8+PHBhdGggZD0iTTEsNDRsNS0yNyAgYzAtMS4xMDQsMC44OTYtMiwyLTJoMzljMS4xMDQsMCwyLDAuODk2LDIsMmwtNSwyNyIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLXdpZHRoPSIyIi8+PC9zdmc+' }, + 'SolidityCompile': { name: 'SolidityCompile', class: 'evm-compiler', Type: CompileTab, icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDUwIDUwIiBoZWlnaHQ9IjUwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MCA1MCIgd2lkdGg9IjUwcHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iNTAiIHdpZHRoPSI1MCIvPjxwYXRoIGQ9Ik00NiwxNXYtNCAgYzAtMS4xMDQtMC44OTYtMi0yLTJjMCwwLTI0LjY0OCwwLTI2LDBjLTEuNDY5LDAtMi40ODQtNC00LTRIM0MxLjg5Niw1LDEsNS44OTYsMSw3djR2Mjl2NGMwLDEuMTA0LDAuODk2LDIsMiwyaDM5ICBjMS4xMDQsMCwyLTAuODk2LDItMiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLXdpZHRoPSIyIi8+PHBhdGggZD0iTTEsNDRsNS0yNyAgYzAtMS4xMDQsMC44OTYtMiwyLTJoMzljMS4xMDQsMCwyLDAuODk2LDIsMmwtNSwyNyIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLXdpZHRoPSIyIi8+PC9zdmc+' }, 'FilePanel': { name: 'FilePanel', Type: FilePanel, icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDUwIDUwIiBoZWlnaHQ9IjUwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MCA1MCIgd2lkdGg9IjUwcHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iNTAiIHdpZHRoPSI1MCIvPjxwb2x5bGluZSBmaWxsPSJub25lIiBwb2ludHM9IjQ0LDIxIDQ0LDQ5IDYsNDkgICA2LDIxICIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLXdpZHRoPSIyIi8+PHBvbHlsaW5lIGZpbGw9Im5vbmUiIHBvaW50cz0iMTksNDkgMTksMjggMzEsMjggICAzMSw0OSAiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iMiIvPjxwb2x5Z29uIHBvaW50cz0iMzUsNSAzNSw4LjAxNiAzNywxMC4wOTQgMzcsNyAzOSw3IDM5LDEyLjIwMyA0MSwxNC4yNjYgNDEsNSAiLz48cG9seWxpbmUgZmlsbD0ibm9uZSIgcG9pbnRzPSIgIDEuMTEsMjUuOTQyIDI1LDEuMDUzIDQ4Ljg5LDI1Ljk0MyAiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iMiIvPjwvc3ZnPg==' }, - 'Test': { name: 'Test', dep: 'Solidity Compile', Type: TestTab, icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDUwIDUwIiBoZWlnaHQ9IjUwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MCA1MCIgd2lkdGg9IjUwcHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iNTAiIHdpZHRoPSI1MCIvPjxwYXRoIGQ9Ik00OSw0djI1YzAsMC01LjI3MywzLTEyLDMgIGMtMTEuOTI5LDAtMTUuODY5LTQtMjQtNFMxLDMwLDEsMzBWM2MwLDAsMi4wODUtMiwxMi0yczE0LjA0Nyw2LDI0LDZDNDMuMjgxLDcsNDguMTMsNC40NzEsNDksNHoiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iMiIvPjxsaW5lIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iMiIgeDE9IjEiIHgyPSIxIiB5MT0iMyIgeTI9IjQ5Ii8+PC9zdmc+' }, + 'Test': { name: 'Test', dep: 'SolidityCompile', Type: TestTab, icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDUwIDUwIiBoZWlnaHQ9IjUwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MCA1MCIgd2lkdGg9IjUwcHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iNTAiIHdpZHRoPSI1MCIvPjxwYXRoIGQ9Ik00OSw0djI1YzAsMC01LjI3MywzLTEyLDMgIGMtMTEuOTI5LDAtMTUuODY5LTQtMjQtNFMxLDMwLDEsMzBWM2MwLDAsMi4wODUtMiwxMi0yczE0LjA0Nyw2LDI0LDZDNDMuMjgxLDcsNDguMTMsNC40NzEsNDksNHoiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iMiIvPjxsaW5lIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iMiIgeDE9IjEiIHgyPSIxIiB5MT0iMyIgeTI9IjQ5Ii8+PC9zdmc+' }, 'Run': { name: 'Run', Type: RunTab, icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDUwIDUwIiBoZWlnaHQ9IjUwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MCA1MCIgd2lkdGg9IjUwcHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iNTAiIHdpZHRoPSI1MCIvPjxwYXRoIGQ9Ik00My43MzQsMjFjLTMuNjMxLDAtMTUuMDkyLDAtMTUuMDkyLDAgIFMxNi4yNSw1LjE4OCwxNi4wNDcsNC45MzhzLTAuNDIyLTAuNTk0LTEuMTI1LTAuNjcyYy0wLjg1OS0wLjA5NS0xLjk2OS0wLjIwMy0yLjMyOC0wLjIzNGMtMC40MDYtMC4wMzUtMC43MTksMC4xNDEtMC40OTYsMC43MzQgIEMxMi4zODgsNS41MzksMTguNzQ4LDIxLDE4Ljc0OCwyMUg2LjAzNGMwLDAtMi40NTgtNC43MjItMi44NzgtNS41MzFDMi45NjUsMTUuMTAxLDIuNTU3LDE1LjAxNCwyLDE1SDEuMjk3ICBjLTAuMTI1LDAtMC4zMTIsMC0wLjI4MSwwLjM0NEMxLjA1OCwxNS44MTEsMywyNSwzLDI1cy0xLjg4OCw5LjE5Ny0xLjk4NCw5LjY1NkMwLjk1MywzNC45NTMsMS4xNzIsMzUsMS4yOTcsMzVIMiAgYzAuOTY2LTAuMDA5LDAuOTU0LTAuMDc5LDEuMTU2LTAuNDY5QzMuNTc2LDMzLjcyMiw2LjAzNCwyOSw2LjAzNCwyOWgxMi43MTRjMCwwLTYuMzYsMTUuNDYxLTYuNjUsMTYuMjM0ICBjLTAuMjIzLDAuNTk0LDAuMDksMC43NywwLjQ5NiwwLjczNGMwLjM1OS0wLjAzMSwxLjQ2OS0wLjEzOSwyLjMyOC0wLjIzNGMwLjcwMy0wLjA3OCwwLjkyMi0wLjQyMiwxLjEyNS0wLjY3MlMyOC42NDMsMjksMjguNjQzLDI5ICBzMTEuNDYxLDAsMTUuMDkyLDBjMy43NjYsMCw1LjI2NC0zLjAzMSw1LjI2NC00UzQ3LjQ4NCwyMSw0My43MzQsMjF6IiBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLXdpZHRoPSIyIi8+PC9zdmc+' }, - 'SolidityStaticAnalysis': { name: 'Solidity Static Analysis', Type: AnalysisTab, icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDUwIDUwIiBoZWlnaHQ9IjUwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MCA1MCIgd2lkdGg9IjUwcHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iNTAiIHdpZHRoPSI1MCIvPjxjaXJjbGUgY3g9IjIiIGN5PSIyNSIgcj0iMiIvPjxjaXJjbGUgY3g9IjE1IiBjeT0iMTkiIHI9IjIiLz48Y2lyY2xlIGN4PSIyNSIgY3k9IjExIiByPSIyIi8+PGNpcmNsZSBjeD0iMzUiIGN5PSIxNyIgcj0iMiIvPjxjaXJjbGUgY3g9IjQ4IiBjeT0iNSIgcj0iMiIvPjxjaXJjbGUgY3g9IjIiIGN5PSIzOSIgcj0iMiIvPjxjaXJjbGUgY3g9IjE1IiBjeT0iNDEiIHI9IjIiLz48Y2lyY2xlIGN4PSIyNSIgY3k9IjMzIiByPSIyIi8+PGNpcmNsZSBjeD0iMzUiIGN5PSI0MyIgcj0iMiIvPjxjaXJjbGUgY3g9IjQ4IiBjeT0iMzEiIHI9IjIiLz48cG9seWxpbmUgZmlsbD0ibm9uZSIgcG9pbnRzPSIyLDI1IDE1LDE5IDI1LDExICAgMzUsMTcgNDgsNSAiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iMiIvPjxwb2x5bGluZSBmaWxsPSJub25lIiBwb2ludHM9IjIsMzkgMTUsNDEgMjUsMzMgICAzNSw0MyA0OCwzMSAiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iMiIvPjwvc3ZnPg==' }, + 'SolidityStaticAnalysis': { name: 'SolidityStaticAnalysis', Type: AnalysisTab, icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDUwIDUwIiBoZWlnaHQ9IjUwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MCA1MCIgd2lkdGg9IjUwcHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iNTAiIHdpZHRoPSI1MCIvPjxjaXJjbGUgY3g9IjIiIGN5PSIyNSIgcj0iMiIvPjxjaXJjbGUgY3g9IjE1IiBjeT0iMTkiIHI9IjIiLz48Y2lyY2xlIGN4PSIyNSIgY3k9IjExIiByPSIyIi8+PGNpcmNsZSBjeD0iMzUiIGN5PSIxNyIgcj0iMiIvPjxjaXJjbGUgY3g9IjQ4IiBjeT0iNSIgcj0iMiIvPjxjaXJjbGUgY3g9IjIiIGN5PSIzOSIgcj0iMiIvPjxjaXJjbGUgY3g9IjE1IiBjeT0iNDEiIHI9IjIiLz48Y2lyY2xlIGN4PSIyNSIgY3k9IjMzIiByPSIyIi8+PGNpcmNsZSBjeD0iMzUiIGN5PSI0MyIgcj0iMiIvPjxjaXJjbGUgY3g9IjQ4IiBjeT0iMzEiIHI9IjIiLz48cG9seWxpbmUgZmlsbD0ibm9uZSIgcG9pbnRzPSIyLDI1IDE1LDE5IDI1LDExICAgMzUsMTcgNDgsNSAiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iMiIvPjxwb2x5bGluZSBmaWxsPSJub25lIiBwb2ludHM9IjIsMzkgMTUsNDEgMjUsMzMgICAzNSw0MyA0OCwzMSAiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iMiIvPjwvc3ZnPg==' }, 'Debugger': { name: 'Debugger', Type: DebuggerTab, icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDUwIDUwIiBoZWlnaHQ9IjUwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MCA1MCIgd2lkdGg9IjUwcHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iNTAiIHdpZHRoPSI1MCIvPjxwYXRoIGQ9Ik00NiwxNXYtNCAgYzAtMS4xMDQtMC44OTYtMi0yLTJjMCwwLTI0LjY0OCwwLTI2LDBjLTEuNDY5LDAtMi40ODQtNC00LTRIM0MxLjg5Niw1LDEsNS44OTYsMSw3djR2Mjl2NGMwLDEuMTA0LDAuODk2LDIsMiwyaDM5ICBjMS4xMDQsMCwyLTAuODk2LDItMiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLXdpZHRoPSIyIi8+PHBhdGggZD0iTTEsNDRsNS0yNyAgYzAtMS4xMDQsMC44OTYtMiwyLTJoMzljMS4xMDQsMCwyLDAuODk2LDIsMmwtNSwyNyIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLXdpZHRoPSIyIi8+PC9zdmc+' }, 'Support': { name: 'Support', Type: SupportTab, icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDUwIDUwIiBoZWlnaHQ9IjUwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MCA1MCIgd2lkdGg9IjUwcHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iNTAiIHdpZHRoPSI1MCIvPjxwYXRoIGQ9Ik0zMC45MzMsMzIuNTI4Yy0wLjE0Ni0xLjYxMi0wLjA5LTIuNzM3LTAuMDktNC4yMWMwLjczLTAuMzgzLDIuMDM4LTIuODI1LDIuMjU5LTQuODg4YzAuNTc0LTAuMDQ3LDEuNDc5LTAuNjA3LDEuNzQ0LTIuODE4ICBjMC4xNDMtMS4xODctMC40MjUtMS44NTUtMC43NzEtMi4wNjVjMC45MzQtMi44MDksMi44NzQtMTEuNDk5LTMuNTg4LTEyLjM5N2MtMC42NjUtMS4xNjgtMi4zNjgtMS43NTktNC41ODEtMS43NTkgIGMtOC44NTQsMC4xNjMtOS45MjIsNi42ODYtNy45ODEsMTQuMTU2Yy0wLjM0NSwwLjIxLTAuOTEzLDAuODc4LTAuNzcxLDIuMDY1YzAuMjY2LDIuMjExLDEuMTcsMi43NzEsMS43NDQsMi44MTggIGMwLjIyLDIuMDYyLDEuNTgsNC41MDUsMi4zMTIsNC44ODhjMCwxLjQ3MywwLjA1NSwyLjU5OC0wLjA5MSw0LjIxQzE5LjM2NywzNy4yMzgsNy41NDYsMzUuOTE2LDcsNDVoMzggIEM0NC40NTUsMzUuOTE2LDMyLjY4NSwzNy4yMzgsMzAuOTMzLDMyLjUyOHoiLz48L3N2Zz4=' }, - 'PluginManager': { name: 'Plugin Manager', target: this, icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDUwIDUwIiBoZWlnaHQ9IjUwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MCA1MCIgd2lkdGg9IjUwcHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iNTAiIHdpZHRoPSI1MCIvPjxwYXRoIGQ9IiAgTTMyLDM1YzAsMCw4LjMxMiwwLDkuMDk4LDBDNDUuNDYzLDM1LDQ5LDMxLjQ2Myw0OSwyNy4wOTlzLTMuNTM3LTcuOTAyLTcuOTAyLTcuOTAyYy0wLjAyLDAtMC4wMzgsMC4wMDMtMC4wNTgsMC4wMDMgIGMwLjA2MS0wLjQ5NCwwLjEwMy0wLjk5NCwwLjEwMy0xLjUwNGMwLTYuNzEtNS40MzktMTIuMTUtMTIuMTUtMTIuMTVjLTUuMjI5LDAtOS42NzIsMy4zMDktMTEuMzg2LDcuOTQxICBjLTEuMDg3LTEuMDg5LTIuNTkxLTEuNzY0LTQuMjUxLTEuNzY0Yy0zLjMxOSwwLTYuMDA5LDIuNjktNi4wMDksNi4wMDhjMCwwLjA4NSwwLjAxLDAuMTY3LDAuMDEzLDAuMjUxICBDMy42OTUsMTguOTk1LDEsMjIuMzQ0LDEsMjYuMzMxQzEsMzEuMTE5LDQuODgxLDM1LDkuNjcsMzVjMC44MjcsMCw4LjMzLDAsOC4zMywwIiBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2Utd2lkdGg9IjIiLz48cG9seWxpbmUgZmlsbD0ibm9uZSIgcG9pbnRzPSIzMCw0MSAyNSw0NiAyMCw0MSAgICIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLXdpZHRoPSIyIi8+PGxpbmUgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLXdpZHRoPSIyIiB4MT0iMjUiIHgyPSIyNSIgeTE9IjI2IiB5Mj0iNDUuNjY4Ii8+PC9zdmc+' }, + 'PluginManager': { name: 'PluginManager', target: this, icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDUwIDUwIiBoZWlnaHQ9IjUwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MCA1MCIgd2lkdGg9IjUwcHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iNTAiIHdpZHRoPSI1MCIvPjxwYXRoIGQ9IiAgTTMyLDM1YzAsMCw4LjMxMiwwLDkuMDk4LDBDNDUuNDYzLDM1LDQ5LDMxLjQ2Myw0OSwyNy4wOTlzLTMuNTM3LTcuOTAyLTcuOTAyLTcuOTAyYy0wLjAyLDAtMC4wMzgsMC4wMDMtMC4wNTgsMC4wMDMgIGMwLjA2MS0wLjQ5NCwwLjEwMy0wLjk5NCwwLjEwMy0xLjUwNGMwLTYuNzEtNS40MzktMTIuMTUtMTIuMTUtMTIuMTVjLTUuMjI5LDAtOS42NzIsMy4zMDktMTEuMzg2LDcuOTQxICBjLTEuMDg3LTEuMDg5LTIuNTkxLTEuNzY0LTQuMjUxLTEuNzY0Yy0zLjMxOSwwLTYuMDA5LDIuNjktNi4wMDksNi4wMDhjMCwwLjA4NSwwLjAxLDAuMTY3LDAuMDEzLDAuMjUxICBDMy42OTUsMTguOTk1LDEsMjIuMzQ0LDEsMjYuMzMxQzEsMzEuMTE5LDQuODgxLDM1LDkuNjcsMzVjMC44MjcsMCw4LjMzLDAsOC4zMywwIiBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2Utd2lkdGg9IjIiLz48cG9seWxpbmUgZmlsbD0ibm9uZSIgcG9pbnRzPSIzMCw0MSAyNSw0NiAyMCw0MSAgICIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLXdpZHRoPSIyIi8+PGxpbmUgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLXdpZHRoPSIyIiB4MT0iMjUiIHgyPSIyNSIgeTE9IjI2IiB5Mj0iNDUuNjY4Ii8+PC9zdmc+' }, 'Settings': { name: 'Settings', Type: SettingsTab, icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDUwIDUwIiBoZWlnaHQ9IjUwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MCA1MCIgd2lkdGg9IjUwcHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iNTAiIHdpZHRoPSI1MCIvPjxwYXRoIGQ9Ik00OSwyNy45NTR2LTZsLTcuMTQxLTEuMTY3ICBjLTAuNDIzLTEuNjkxLTEuMDg3LTMuMjgxLTEuOTYyLTQuNzM3bDQuMTYyLTUuOTMybC00LjI0My00LjI0MWwtNS44NTYsNC4yMWMtMS40Ni0wLjg4NC0zLjA2LTEuNTU4LTQuNzYzLTEuOTgybC0xLjI0NS03LjEwNmgtNiAgbC0xLjE1Niw3LjA4M2MtMS43MDQsMC40MTgtMy4zMTMsMS4wODMtNC43NzcsMS45NjNMMTAuMTgsNS44NzNsLTQuMjQzLDQuMjQxbDQuMTA3LDUuODc0Yy0wLjg4OCwxLjQ3LTEuNTYzLDMuMDc3LTEuOTkyLDQuNzkyICBMMSwyMS45NTR2Nmw3LjA0NCwxLjI0OWMwLjQyNSwxLjcxMSwxLjEwMSwzLjMxOCwxLjk5Miw0Ljc5bC00LjE2Myw1LjgyM2w0LjI0MSw0LjI0NWw1Ljg4MS00LjExOSAgYzEuNDY4LDAuODgyLDMuMDczLDEuNTUyLDQuNzc3LDEuOTczbDEuMTgsNy4wODdoNmwxLjI2MS03LjEwNWMxLjY5NS0wLjQzLDMuMjk3LTEuMTA1LDQuNzUxLTEuOTlsNS45MjIsNC4xNTVsNC4yNDItNC4yNDUgIGwtNC4yMjctNS44N2MwLjg3NS0xLjQ1NiwxLjUzOS0zLjA0OCwxLjk1OC00LjczOUw0OSwyNy45NTR6IE0yNSwzM2MtNC40MTgsMC04LTMuNTgyLTgtOHMzLjU4Mi04LDgtOHM4LDMuNTgyLDgsOFMyOS40MTgsMzMsMjUsMzMgIHoiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iMiIvPjwvc3ZnPg==' } } this.activated = {} // list all activated modules @@ -96,12 +96,14 @@ class PluginManagerComponent { let action = (event) => { if (this.activated.hasOwnProperty(item)) { this.deactivateInternal(item) + event.target.innerHTML = 'activate' } else { this.activateInternal(item) + event.target.innerHTML = 'deactivate' } } - ctrBtns = yo`
+ ctrBtns = yo`
` @@ -125,12 +127,13 @@ class PluginManagerComponent { } deactivateInternal (name) { + if (!this.activated[name]) return + this.event.emit('removingItem', this.activated[name]) delete this.activated[name] - this.event.emit('removingItem', name) - if (this.view) this.view.querySelector(`#${name} button`).innerHTML = 'activate' } activateInternal (name) { + if (this.activated[name]) return const mod = this.modulesDefinition[name] let dep if (mod.dep) dep = this.activateInternal(mod.dep) @@ -148,8 +151,7 @@ class PluginManagerComponent { if (mod.class === 'evm-compiler') { this.data.proxy.register(mod, instance) } - this.activated[mod.name] = mod - if (this.view) this.view.querySelector(`#${name} button`).innerHTML = 'deactivate' + this.activated[mod.name] = mod return instance } diff --git a/src/app/components/swap-panel-api.js b/src/app/components/swap-panel-api.js index 9a37f76075..eb128b17c6 100644 --- a/src/app/components/swap-panel-api.js +++ b/src/app/components/swap-panel-api.js @@ -9,6 +9,9 @@ class SwapPanelApi { pluginManagerComponent.event.on('requestContainer', (mod, content) => { this.add(mod.name, content) }) + pluginManagerComponent.event.on('removingItem', (mod) => { + this.remove(mod.name) + }) } /* @@ -20,6 +23,10 @@ class SwapPanelApi { return this.component.add(moduleName, content) } + remove (moduleName) { + return this.component.remove(moduleName) + } + reference (modulename, domElement) { this.nodes[modulename] = domElement } diff --git a/src/app/components/swap-panel-component.js b/src/app/components/swap-panel-component.js index 683e712e7d..db6208b844 100644 --- a/src/app/components/swap-panel-component.js +++ b/src/app/components/swap-panel-component.js @@ -27,10 +27,15 @@ class SwapPanelComponent { } add (moduleName, content) { - this.contents[moduleName] = yo`
${content}
` + this.contents[moduleName] = yo`
${content}
` this.view.appendChild(this.contents[moduleName]) } + remove (moduleName) { + var el = this.view.querySelector(`div#${moduleName}Content`) + el.parentElement.removeChild(el) + } + render () { this.view = yo`
diff --git a/src/app/components/vertical-icons-api.js b/src/app/components/vertical-icons-api.js index 3c4ba39ef0..a167dab0c0 100644 --- a/src/app/components/vertical-icons-api.js +++ b/src/app/components/vertical-icons-api.js @@ -4,6 +4,7 @@ class VerticalIconsApi { constructor (verticalIconsComponent, pluginManagerComponent) { this.component = verticalIconsComponent pluginManagerComponent.event.on('requestContainer', (mod, content) => verticalIconsComponent.addIcon(mod)) + pluginManagerComponent.event.on('removingItem', (mod) => verticalIconsComponent.removeIcon(mod)) } } module.exports = VerticalIconsApi diff --git a/src/app/components/vertical-icons-component.js b/src/app/components/vertical-icons-component.js index 6977234217..b2011f2472 100644 --- a/src/app/components/vertical-icons-component.js +++ b/src/app/components/vertical-icons-component.js @@ -16,11 +16,12 @@ class VerticalIconComponent { addIcon (mod) { let self = this - this.view.appendChild(yo`
{ self._iconClick(mod.name) }} title=${mod.name}>${mod.name}
`) + this.view.appendChild(yo`
{ self._iconClick(mod.name) }} title=${mod.name}>${mod.name}
`) } - removeIcon (item) { - + removeIcon (mod) { + var el = this.view.querySelector(`#${mod.name}Icon`) + el.parentElement.removeChild(el) } select (name) {