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