Merge pull request #971 from ethereum/askToConfirmTx

Ask to confirm tx
pull/3094/head
yann300 7 years ago committed by GitHub
commit 8a32dc609a
  1. 4
      src/app.js
  2. 131
      src/app/execution/txRunner.js
  3. 100
      src/app/tabs/settings-tab.js
  4. 9
      src/config.js
  5. 8
      src/universal-dapp.js

@ -247,6 +247,10 @@ function run () {
api: { api: {
logMessage: (msg) => { logMessage: (msg) => {
self._components.editorpanel.log({ type: 'log', value: msg }) self._components.editorpanel.log({ type: 'log', value: msg })
},
config: self._api.config,
detectNetwork: (cb) => {
executionContext.detectNetwork(cb)
} }
}, },
opt: { removable: false, removable_instances: true } opt: { removable: false, removable_instances: true }

@ -4,11 +4,33 @@ var EthJSBlock = require('ethereumjs-block')
var ethJSUtil = require('ethereumjs-util') var ethJSUtil = require('ethereumjs-util')
var BN = ethJSUtil.BN var BN = ethJSUtil.BN
var executionContext = require('../../execution-context') 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 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 TxRunner (vmaccounts, opts) { function TxRunner (vmaccounts, opts) {
this.personalMode = opts.personalMode this.personalMode = opts.personalMode
this.blockNumber = 0 this.blockNumber = 0
this.runAsync = true this.runAsync = true
this.config = opts.config
this.detectNetwork = opts.detectNetwork
if (executionContext.isVM()) { if (executionContext.isVM()) {
this.blockNumber = 1150000 // The VM is running in Homestead mode, which started at this block. this.blockNumber = 1150000 // The VM is running in Homestead mode, which started at this block.
this.runAsync = false // We have to run like this cause the VM Event Manager does not support running multiple txs at the same time. this.runAsync = false // We have to run like this cause the VM Event Manager does not support running multiple txs at the same time.
@ -23,8 +45,23 @@ TxRunner.prototype.rawRun = function (args, cb) {
} }
TxRunner.prototype.execute = function (args, callback) { TxRunner.prototype.execute = function (args, callback) {
var self = this function execute (gasPrice) {
if (gasPrice) tx.gasPrice = executionContext.web3().toHex(gasPrice)
var sendTransaction = self.personalMode ? executionContext.web3().personal.sendTransaction : executionContext.web3().eth.sendTransaction
try {
sendTransaction(tx, function (err, resp) {
if (err) {
return callback(err, resp)
}
tryTillResponse(resp, callback)
})
} catch (e) {
return callback(`Send transaction failed: ${e.message} . if you use an injected provider, please check it is properly unlocked. `)
}
}
var self = this
var from = args.from var from = args.from
var to = args.to var to = args.to
var data = args.data var data = args.data
@ -42,6 +79,7 @@ TxRunner.prototype.execute = function (args, callback) {
data: data, data: data,
value: value value: value
} }
if (args.useCall) { if (args.useCall) {
tx.gas = gasLimit tx.gas = gasLimit
executionContext.web3().eth.call(tx, function (error, result) { executionContext.web3().eth.call(tx, function (error, result) {
@ -59,7 +97,7 @@ TxRunner.prototype.execute = function (args, callback) {
// NOTE: estimateGas very likely will return a large limit if execution of the code failed // 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 // we want to be able to run the code in order to debug and find the cause for the failure
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).' 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) { if (gasEstimation > gasLimit) {
return callback('Gas required exceeds limit: ' + gasLimit + '. ' + warnEstimation) return callback('Gas required exceeds limit: ' + gasLimit + '. ' + warnEstimation)
} }
@ -68,17 +106,37 @@ TxRunner.prototype.execute = function (args, callback) {
} }
tx.gas = gasEstimation tx.gas = gasEstimation
var sendTransaction = self.personalMode ? executionContext.web3().personal.sendTransaction : executionContext.web3().eth.sendTransaction
try { if (!self.config.getUnpersistedProperty('doNotShowTransactionConfirmationAgain')) {
sendTransaction(tx, function (err, resp) { self.detectNetwork((err, network) => {
if (err) { if (err) {
return callback(err, resp) console.log(err)
} else {
if (network.name === 'Main') {
var content = confirmDialog(tx, gasEstimation, self)
modalDialog('Confirm transaction', content,
{ label: 'Confirm',
fn: () => {
self.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()
}
} }
tryTillResponse(resp, callback)
}) })
} catch (e) { } else {
return callback(`Send transaction failed: ${e.message} . if you use an injected provider, please check it is properly unlocked. `) execute()
} }
}) })
} }
@ -164,4 +222,57 @@ function run (self, tx, stamp, callback) {
} }
} }
function confirmDialog (tx, gasEstimation, self) {
var amount = executionContext.web3().fromWei(typeConversion.toInt(tx.value), 'ether')
var input = yo`<input id='confirmsetting' type="checkbox">`
var el = yo`
<div>
<div>You are creating a transaction on the main network. Click confirm if you are sure to continue.</div>
<div class=${css.txInfoBox}>
<div>From: ${tx.from}</div>
<div>To: ${tx.to ? tx.to : '(Contract Creation)'}</div>
<div>Amount: ${amount} Ether</div>
<div>Gas estimation: ${gasEstimation}</div>
<div>Gas limit: ${tx.gas}</div>
<div>Gas price: <input id='gasprice' oninput=${gasPriceChanged} /> Gwei</div>
<div>Max transaction fee:<span id='txfee'></span></div>
<div>Data:</div>
<pre class=${css.wrapword}>${tx.data}</pre>
</div>
<div class=${css.checkbox}>
${input}
<i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Do not ask for confirmation again. (the setting will not be persisted for the next page reload)
</div>
</div>
`
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 module.exports = TxRunner

@ -19,15 +19,22 @@ var css = csjs`
} }
.info { .info {
${styles.rightPanel.settingsTab.box_SolidityVersionInfo} ${styles.rightPanel.settingsTab.box_SolidityVersionInfo}
margin-bottom: 2em; margin-bottom: 1em;
word-break: break-word; word-break: break-word;
} }
.title {
font-size: 1.1em;
font-weight: bold;
margin-bottom: 1em;
}
.crow { .crow {
display: flex; display: flex;
overflow: auto; overflow: auto;
clear: both; clear: both;
padding: .5em; padding: .2em;
font-weight: bold; }
.checkboxText {
font-weight: normal;
} }
.crow label { .crow label {
cursor:pointer; cursor:pointer;
@ -35,10 +42,15 @@ var css = csjs`
.crowNoFlex { .crowNoFlex {
overflow: auto; overflow: auto;
clear: both; clear: both;
}
.attention {
margin-bottom: 1em;
padding: .5em; padding: .5em;
font-weight: bold; font-weight: bold;
} }
.select { .select {
font-weight: bold;
margin-top: 1em;
${styles.rightPanel.settingsTab.dropdown_SelectCompiler} ${styles.rightPanel.settingsTab.dropdown_SelectCompiler}
} }
.heading { .heading {
@ -57,7 +69,6 @@ var css = csjs`
} }
.pluginTextArea { .pluginTextArea {
font-family: unset; font-family: unset;
margin-top: 5px;
} }
.pluginLoad { .pluginLoad {
vertical-align: top; vertical-align: top;
@ -65,6 +76,9 @@ var css = csjs`
i.warnIt { i.warnIt {
color: ${styles.appProperties.warningText_Color}; color: ${styles.appProperties.warningText_Color};
} }
.icon {
margin-right: .5em;
}
} }
` `
module.exports = SettingsTab module.exports = SettingsTab
@ -79,45 +93,57 @@ function SettingsTab (container, appAPI, appEvents, opts) {
var el = yo` var el = yo`
<div class="${css.settingsTabView} "id="settingsView"> <div class="${css.settingsTabView} "id="settingsView">
<div class="${css.info}"> <div class="${css.info}">
<div>Your current Solidity version is</div> <div class=${css.title}>Solidity version</div>
<div id="version"></div> <span>Current version:</span> <span id="version"></span>
</div> <div class="${css.crow}">
<div class="${css.crow}"> <select class="${css.select}" id="versionSelector"></select>
<select class="${css.select}" id="versionSelector"></select> </div>
</div>
<div class="${css.crow}">
<div><input id="editorWrap" type="checkbox"></div>
<span class="${css.checkboxText}">Text Wrap</span>
</div>
<div class="${css.crow}">
<div>${optionVM}</div>
<span class="${css.checkboxText}">Always use VM at Load</span>
</div>
<div class="${css.crow}">
<div><input id="optimize" type="checkbox"></div>
<span class="${css.checkboxText}">Enable Optimization</span>
</div>
<hr>
<h4 class="${css.heading}">Themes ( Selecting a theme will trigger a page reload )</h4>
<div class="${css.crow}">
<input class="${css.col1}" name="theme" id="themeLight" type="radio">
<label for="themeLight">Light Theme</label>
</div> </div>
<div class="${css.crow}"> <div class="${css.info}">
<input class="${css.col1}" name="theme" id="themeDark" type="radio"> <div class=${css.title}>General settings</div>
<label for="themeDark">Dark Theme</label> <div class="${css.crow}">
<div>${optionVM}</div>
<span class="${css.checkboxText}">Always use Ethereum VM at Load</span>
</div>
<div class="${css.crow}">
<div><input id="editorWrap" type="checkbox"></div>
<span class="${css.checkboxText}">Text Wrap</span>
</div>
<div class="${css.crow}">
<div><input id="optimize" type="checkbox"></div>
<span class="${css.checkboxText}">Enable Optimization</span>
</div>
</div> </div>
<hr> <div class="${css.info}">
<div class="${css.crowNoFlex}"> <div class=${css.title}>Themes</div>
<div>Plugin ( <i title="Do not use this feature yet" class="${css.warnIt} fa fa-exclamation-triangle" aria-hidden="true"></i><span> Do not use this alpha feature if you are not sure what you are doing!</span> ) <div class=${css.attention}>
<i title="Select the theme" class="${css.icon} fa fa-exclamation-triangle" aria-hidden="true"></i>
<span>Selecting a theme will trigger a page reload</span>
</div>
<div class="${css.crow}">
<input class="${css.col1}" name="theme" id="themeLight" type="checkbox">
<label for="themeLight">Light Theme</label>
</div>
<div class="${css.crow}">
<input class="${css.col1}" name="theme" id="themeDark" type="checkbox">
<label for="themeDark">Dark Theme</label>
</div> </div>
<div> </div>
<textarea rows="4" cols="70" id="plugininput" type="text" class="${css.pluginTextArea}" ></textarea> <div class="${css.info}">
<br /> <div class=${css.title}>Plugin</div>
<input onclick=${loadPlugin} type="button" value="Load" class="${css.pluginLoad}"> <div class="${css.crowNoFlex}">
<div class=${css.attention}>
<i title="Do not use this feature yet" class="${css.icon} fa fa-exclamation-triangle" aria-hidden="true"></i>
<span> Do not use this alpha feature if you are not sure what you are doing!</span>
</div>
<div>
<textarea rows="4" cols="70" id="plugininput" type="text" class="${css.pluginTextArea}" ></textarea>
<input onclick=${loadPlugin} type="button" value="Load" class="${css.pluginLoad}">
</div>
</div> </div>
</div> </div>
</div> </div>
` `
function loadPlugin () { function loadPlugin () {

@ -4,6 +4,7 @@ var CONFIG_FILE = '.remix.config'
function Config (storage) { function Config (storage) {
this.items = {} this.items = {}
this.unpersistedItems = {}
// load on instantiation // load on instantiation
try { try {
@ -44,6 +45,14 @@ function Config (storage) {
} }
} }
} }
this.getUnpersistedProperty = function (key) {
return this.unpersistedItems[key]
}
this.setUnpersistedProperty = function (key, value) {
this.unpersistedItems[key] = value
}
} }
module.exports = Config module.exports = Config

@ -166,7 +166,9 @@ function UniversalDApp (opts = {}) {
self.reset(self.contracts) self.reset(self.contracts)
}) })
self.txRunner = new TxRunner({}, { self.txRunner = new TxRunner({}, {
personalMode: this.personalMode personalMode: this.personalMode,
config: self._api.config,
detectNetwork: self._api.detectNetwork
}) })
} }
@ -186,7 +188,9 @@ UniversalDApp.prototype.reset = function (contracts, transactionContextAPI) {
executionContext.vm().stateManager.cache.flush(function () {}) executionContext.vm().stateManager.cache.flush(function () {})
} }
this.txRunner = new TxRunner(this.accounts, { this.txRunner = new TxRunner(this.accounts, {
personalMode: this.personalMode personalMode: this.personalMode,
config: this._api.config,
detectNetwork: this._api.detectNetwork
}) })
} }

Loading…
Cancel
Save