From 2524879e96176ced5422814e249b03409fb1165f Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Mon, 24 Dec 2018 11:35:06 -0500 Subject: [PATCH 01/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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')}`)