diff --git a/src/app/execution/confirmDialog.js b/src/app/execution/confirmDialog.js
new file mode 100644
index 0000000000..60654b8092
--- /dev/null
+++ b/src/app/execution/confirmDialog.js
@@ -0,0 +1,66 @@
+var yo = require('yo-yo')
+var csjs = require('csjs-inject')
+var remixLib = require('remix-lib')
+var styleGuide = remixLib.ui.styleGuide
+var styles = styleGuide()
+
+var css = csjs`
+ .txInfoBox {
+ ${styles.rightPanel.compileTab.box_CompileContainer}; // add askToConfirmTXContainer to Remix and then replace this styling
+ }
+ .wrapword {
+ white-space: pre-wrap; /* Since CSS 2.1 */
+ white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
+ white-space: -pre-wrap; /* Opera 4-6 */
+ white-space: -o-pre-wrap; /* Opera 7 */
+ word-wrap: break-word; /* Internet Explorer 5.5+ */
+ }
+`
+
+function confirmDialog (tx, amount, gasEstimation, self, newGasPriceCb, initialParamsCb) {
+ var onGasPriceChange = function () {
+ var gasPrice = el.querySelector('#gasprice').value
+ newGasPriceCb(gasPrice, (priceStatus, txFeeText) => {
+ el.querySelector('#txfee').innerHTML = txFeeText
+ el.gasPriceStatus = priceStatus
+ })
+ }
+
+ var el = yo`
+
+
You are creating a transaction on the main network. Click confirm if you are sure to continue.
+
+
From: ${tx.from}
+
To: ${tx.to ? tx.to : '(Contract Creation)'}
+
Amount: ${amount} Ether
+
Gas estimation: ${gasEstimation}
+
Gas limit: ${tx.gas}
+
+
Max transaction fee:
+
Data:
+
${tx.data}
+
+
+
+ Do not ask for confirmation again. (the setting will not be persisted for the next page reload)
+
+
+ `
+
+ initialParamsCb((txFeeText, gasPriceValue, gasPriceStatus) => {
+ if (txFeeText) {
+ el.querySelector('#txfee').innerHTML = txFeeText
+ }
+ if (gasPriceValue) {
+ el.querySelector('#gasprice').value = gasPriceValue
+ onGasPriceChange()
+ }
+ if (gasPriceStatus !== undefined) {
+ el.gasPriceStatus = gasPriceStatus
+ }
+ })
+
+ return el
+}
+
+module.exports = confirmDialog
diff --git a/src/app/execution/txRunner.js b/src/app/execution/txRunner.js
index 0d48c8e485..e109881860 100644
--- a/src/app/execution/txRunner.js
+++ b/src/app/execution/txRunner.js
@@ -5,26 +5,10 @@ var ethJSUtil = require('ethereumjs-util')
var BN = ethJSUtil.BN
var executionContext = require('../../execution-context')
var modalDialog = require('../ui/modaldialog')
-var yo = require('yo-yo')
-var typeConversion = require('../../lib/typeConversion')
-var csjs = require('csjs-inject')
-var remixLib = require('remix-lib')
-var styleGuide = remixLib.ui.styleGuide
-var styles = styleGuide()
var modal = require('../ui/modal-dialog-custom')
+var typeConversion = require('../../lib/typeConversion')
-var css = csjs`
- .txInfoBox {
- ${styles.rightPanel.compileTab.box_CompileContainer}; // add askToConfirmTXContainer to Remix and then replace this styling
- }
- .wrapword {
- white-space: pre-wrap; /* Since CSS 2.1 */
- white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
- white-space: -pre-wrap; /* Opera 4-6 */
- white-space: -o-pre-wrap; /* Opera 7 */
- word-wrap: break-word; /* Internet Explorer 5.5+ */
- }
-`
+var confirmDialog = require('./confirmDialog')
function TxRunner (vmaccounts, api) {
this._api = api
@@ -43,150 +27,179 @@ TxRunner.prototype.rawRun = function (args, cb) {
run(this, args, Date.now(), cb)
}
-TxRunner.prototype.execute = function (args, callback) {
- var self = this
- function execute (gasPrice) {
- if (gasPrice) tx.gasPrice = executionContext.web3().toHex(gasPrice)
+function executeTx (tx, gasPrice, api, callback) {
+ if (gasPrice) tx.gasPrice = executionContext.web3().toHex(gasPrice)
- if (self._api.personalMode()) {
- modal.promptPassphrase(null, 'Personal mode is enabled. Please provide passphrase of account ' + tx.from, '', (value) => {
- sendTransaction(executionContext.web3().personal.sendTransaction, tx, value, callback)
- }, () => {
- return callback('Canceled by user.')
- })
- } else {
- sendTransaction(executionContext.web3().eth.sendTransaction, tx, null, callback)
- }
+ if (api.personalMode()) {
+ modal.promptPassphrase(null, 'Personal mode is enabled. Please provide passphrase of account ' + tx.from, '', (value) => {
+ sendTransaction(executionContext.web3().personal.sendTransaction, tx, value, callback)
+ }, () => {
+ return callback('Canceled by user.')
+ })
+ } else {
+ sendTransaction(executionContext.web3().eth.sendTransaction, tx, null, callback)
}
+}
+
+TxRunner.prototype.execute = function (args, callback) {
+ var self = this
- var from = args.from
- var to = args.to
var data = args.data
if (data.slice(0, 2) !== '0x') {
data = '0x' + data
}
- var value = args.value
- var gasLimit = args.gasLimit
- var tx
if (!executionContext.isVM()) {
- tx = {
- from: from,
- to: to,
- data: data,
- value: value
+ self.runInNode(args.from, args.to, data, args.value, args.gasLimit, args.useCall, callback)
+ } else {
+ try {
+ self.runInVm(args.from, args.to, data, args.value, args.gasLimit, args.useCall, callback)
+ } catch (e) {
+ callback(e, null)
}
+ }
+}
- if (args.useCall) {
- tx.gas = gasLimit
- executionContext.web3().eth.call(tx, function (error, result) {
- callback(error, {
- result: result,
- transactionHash: result.transactionHash
- })
- })
- } else {
- executionContext.web3().eth.estimateGas(tx, function (err, gasEstimation) {
- if (err) {
- return callback(err, gasEstimation)
- }
- var blockGasLimit = executionContext.currentblockGasLimit()
- // NOTE: estimateGas very likely will return a large limit if execution of the code failed
- // we want to be able to run the code in order to debug and find the cause for the failure
+TxRunner.prototype.runInVm = function (from, to, data, value, gasLimit, useCall, callback) {
+ const self = this
+ var account = self.vmaccounts[from]
+ if (!account) {
+ return callback('Invalid account selected')
+ }
+ var tx = new EthJSTX({
+ nonce: new BN(account.nonce++),
+ gasPrice: new BN(1),
+ gasLimit: new BN(gasLimit, 10),
+ to: to,
+ value: new BN(value, 10),
+ data: new Buffer(data.slice(2), 'hex')
+ })
+ tx.sign(account.privateKey)
+
+ const coinbases = [ '0x0e9281e9c6a0808672eaba6bd1220e144c9bb07a', '0x8945a1288dc78a6d8952a92c77aee6730b414778', '0x94d76e24f818426ae84aa404140e8d5f60e10e7e' ]
+ const difficulties = [ new BN('69762765929000', 10), new BN('70762765929000', 10), new BN('71762765929000', 10) ]
+ var block = new EthJSBlock({
+ header: {
+ timestamp: new Date().getTime() / 1000 | 0,
+ number: self.blockNumber,
+ coinbase: coinbases[self.blockNumber % coinbases.length],
+ difficulty: difficulties[self.blockNumber % difficulties.length],
+ gasLimit: new BN(gasLimit, 10).imuln(2)
+ },
+ transactions: [],
+ uncleHeaders: []
+ })
+ if (!useCall) {
+ ++self.blockNumber
+ } else {
+ executionContext.vm().stateManager.checkpoint()
+ }
- var warnEstimation = " An important gas estimation might also be the sign of a problem in the contract code. Please check loops and be sure you did not sent value to a non payable function (that's also the reason of strong gas estimation)."
- if (gasEstimation > gasLimit) {
- return callback('Gas required exceeds limit: ' + gasLimit + '. ' + warnEstimation)
- }
- if (gasEstimation > blockGasLimit) {
- return callback('Gas required exceeds block gas limit: ' + gasLimit + '. ' + warnEstimation)
- }
+ executionContext.vm().runTx({block: block, tx: tx, skipBalance: true, skipNonce: true}, function (err, result) {
+ if (useCall) {
+ executionContext.vm().stateManager.revert(function () {})
+ }
+ err = err ? err.message : err
+ result.status = '0x' + result.vm.exception.toString(16)
+ callback(err, {
+ result: result,
+ transactionHash: ethJSUtil.bufferToHex(new Buffer(tx.hash()))
+ })
+ })
+}
- tx.gas = gasEstimation
+TxRunner.prototype.runInNode = function (from, to, data, value, gasLimit, useCall, callback) {
+ const self = this
+ var tx = { from: from, to: to, data: data, value: value }
- if (!self._api.config.getUnpersistedProperty('doNotShowTransactionConfirmationAgain')) {
- self._api.detectNetwork((err, network) => {
- if (err) {
- console.log(err)
- } else {
- if (network.name === 'Main') {
- var content = confirmDialog(tx, gasEstimation, self)
- modalDialog('Confirm transaction', content,
- { label: 'Confirm',
- fn: () => {
- self._api.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked)
- if (!content.gasPriceStatus) {
- callback('Given gas grice is not correct')
- } else {
- var gasPrice = executionContext.web3().toWei(content.querySelector('#gasprice').value, 'gwei')
- execute(gasPrice)
- }
- }}, {
- label: 'Cancel',
- fn: () => {
- return callback('Transaction canceled by user.')
- }
- })
- } else {
- execute()
- }
- }
- })
- } else {
- execute()
- }
+ if (useCall) {
+ tx.gas = gasLimit
+ return executionContext.web3().eth.call(tx, function (error, result) {
+ callback(error, {
+ result: result,
+ transactionHash: result.transactionHash
})
+ })
+ }
+ executionContext.web3().eth.estimateGas(tx, function (err, gasEstimation) {
+ if (err) {
+ return callback(err, gasEstimation)
}
- } else {
- try {
- var account = self.vmaccounts[from]
- if (!account) {
- return callback('Invalid account selected')
- }
- tx = new EthJSTX({
- nonce: new BN(account.nonce++),
- gasPrice: new BN(1),
- gasLimit: new BN(gasLimit, 10),
- to: to,
- value: new BN(value, 10),
- data: new Buffer(data.slice(2), 'hex')
- })
- tx.sign(account.privateKey)
+ var blockGasLimit = executionContext.currentblockGasLimit()
+ // NOTE: estimateGas very likely will return a large limit if execution of the code failed
+ // we want to be able to run the code in order to debug and find the cause for the failure
- const coinbases = [ '0x0e9281e9c6a0808672eaba6bd1220e144c9bb07a', '0x8945a1288dc78a6d8952a92c77aee6730b414778', '0x94d76e24f818426ae84aa404140e8d5f60e10e7e' ]
- const difficulties = [ new BN('69762765929000', 10), new BN('70762765929000', 10), new BN('71762765929000', 10) ]
- var block = new EthJSBlock({
- header: {
- timestamp: new Date().getTime() / 1000 | 0,
- number: self.blockNumber,
- coinbase: coinbases[self.blockNumber % coinbases.length],
- difficulty: difficulties[self.blockNumber % difficulties.length],
- gasLimit: new BN(gasLimit, 10).imuln(2)
- },
- transactions: [],
- uncleHeaders: []
- })
- if (!args.useCall) {
- ++self.blockNumber
- } else {
- executionContext.vm().stateManager.checkpoint()
+ var warnEstimation = " An important gas estimation might also be the sign of a problem in the contract code. Please check loops and be sure you did not sent value to a non payable function (that's also the reason of strong gas estimation)."
+ if (gasEstimation > gasLimit) {
+ return callback('Gas required exceeds limit: ' + gasLimit + '. ' + warnEstimation)
+ }
+ if (gasEstimation > blockGasLimit) {
+ return callback('Gas required exceeds block gas limit: ' + gasLimit + '. ' + warnEstimation)
+ }
+
+ tx.gas = gasEstimation
+
+ if (self._api.config.getUnpersistedProperty('doNotShowTransactionConfirmationAgain')) {
+ return executeTx(tx, null, self._api, callback)
+ }
+ self._api.detectNetwork((err, network) => {
+ if (err) {
+ console.log(err)
+ return
+ }
+ if (network.name !== 'Main') {
+ return executeTx(tx, null, self._api, callback)
}
- executionContext.vm().runTx({block: block, tx: tx, skipBalance: true, skipNonce: true}, function (err, result) {
- if (args.useCall) {
- executionContext.vm().stateManager.revert(function () {})
+ 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(priceStatus, txFeeText)
+ },
+ (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)
+ }
+ })
}
- err = err ? err.message : err
- result.status = '0x' + result.vm.exception.toString(16)
- callback(err, {
- result: result,
- transactionHash: ethJSUtil.bufferToHex(new Buffer(tx.hash()))
- })
- })
- } catch (e) {
- callback(e, null)
- }
- }
+ )
+
+ modalDialog('Confirm transaction', content,
+ { label: 'Confirm',
+ fn: () => {
+ self._api.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked)
+ if (!content.gasPriceStatus) {
+ return callback('Given gas grice is not correct')
+ }
+ var gasPrice = executionContext.web3().toWei(content.querySelector('#gasprice').value, 'gwei')
+ executeTx(tx, gasPrice, self._api, callback)
+ }}, {
+ label: 'Cancel',
+ fn: () => {
+ return callback('Transaction canceled by user.')
+ }
+ })
+ })
+ })
}
function tryTillResponse (txhash, done) {
@@ -234,57 +247,4 @@ function run (self, tx, stamp, callback) {
}
}
-function confirmDialog (tx, gasEstimation, self) {
- var amount = executionContext.web3().fromWei(typeConversion.toInt(tx.value), 'ether')
- var input = yo``
- var el = yo`
-
-
You are creating a transaction on the main network. Click confirm if you are sure to continue.
-
-
From: ${tx.from}
-
To: ${tx.to ? tx.to : '(Contract Creation)'}
-
Amount: ${amount} Ether
-
Gas estimation: ${gasEstimation}
-
Gas limit: ${tx.gas}
-
-
Max transaction fee:
-
Data:
-
${tx.data}
-
-
- ${input}
- Do not ask for confirmation again. (the setting will not be persisted for the next page reload)
-
-
- `
-
- var warnMessage = ' Please fix this issue before sending any transaction. '
- function gasPriceChanged () {
- try {
- var gasPrice = el.querySelector('#gasprice').value
- var fee = executionContext.web3().toBigNumber(tx.gas).mul(executionContext.web3().toBigNumber(executionContext.web3().toWei(gasPrice.toString(10), 'gwei')))
- el.querySelector('#txfee').innerHTML = ' ' + executionContext.web3().fromWei(fee.toString(10), 'ether') + ' Ether'
- el.gasPriceStatus = true
- } catch (e) {
- el.querySelector('#txfee').innerHTML = warnMessage + e.message
- el.gasPriceStatus = false
- }
- }
-
- executionContext.web3().eth.getGasPrice((error, gasPrice) => {
- if (error) {
- el.querySelector('#txfee').innerHTML = 'Unable to retrieve the current network gas price.' + warnMessage + error
- } else {
- try {
- el.querySelector('#gasprice').value = executionContext.web3().fromWei(gasPrice.toString(10), 'gwei')
- gasPriceChanged()
- } catch (e) {
- el.querySelector('#txfee').innerHTML = warnMessage + e.message
- el.gasPriceStatus = false
- }
- }
- })
- return el
-}
-
module.exports = TxRunner