Merge pull request #971 from ethereum/askToConfirmTx

Ask to confirm tx
pull/1/head
yann300 7 years ago committed by GitHub
commit 5e5a04c44b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  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: {
logMessage: (msg) => {
self._components.editorpanel.log({ type: 'log', value: msg })
},
config: self._api.config,
detectNetwork: (cb) => {
executionContext.detectNetwork(cb)
}
},
opt: { removable: false, removable_instances: true }

@ -4,11 +4,33 @@ var EthJSBlock = require('ethereumjs-block')
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 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) {
this.personalMode = opts.personalMode
this.blockNumber = 0
this.runAsync = true
this.config = opts.config
this.detectNetwork = opts.detectNetwork
if (executionContext.isVM()) {
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.
@ -23,8 +45,23 @@ TxRunner.prototype.rawRun = function (args, cb) {
}
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 to = args.to
var data = args.data
@ -42,6 +79,7 @@ TxRunner.prototype.execute = function (args, callback) {
data: data,
value: value
}
if (args.useCall) {
tx.gas = gasLimit
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
// 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) {
return callback('Gas required exceeds limit: ' + gasLimit + '. ' + warnEstimation)
}
@ -68,17 +106,37 @@ TxRunner.prototype.execute = function (args, callback) {
}
tx.gas = gasEstimation
var sendTransaction = self.personalMode ? executionContext.web3().personal.sendTransaction : executionContext.web3().eth.sendTransaction
try {
sendTransaction(tx, function (err, resp) {
if (!self.config.getUnpersistedProperty('doNotShowTransactionConfirmationAgain')) {
self.detectNetwork((err, network) => {
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) {
return callback(`Send transaction failed: ${e.message} . if you use an injected provider, please check it is properly unlocked. `)
} else {
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

@ -19,15 +19,22 @@ var css = csjs`
}
.info {
${styles.rightPanel.settingsTab.box_SolidityVersionInfo}
margin-bottom: 2em;
margin-bottom: 1em;
word-break: break-word;
}
.title {
font-size: 1.1em;
font-weight: bold;
margin-bottom: 1em;
}
.crow {
display: flex;
overflow: auto;
clear: both;
padding: .5em;
font-weight: bold;
padding: .2em;
}
.checkboxText {
font-weight: normal;
}
.crow label {
cursor:pointer;
@ -35,10 +42,15 @@ var css = csjs`
.crowNoFlex {
overflow: auto;
clear: both;
}
.attention {
margin-bottom: 1em;
padding: .5em;
font-weight: bold;
}
.select {
font-weight: bold;
margin-top: 1em;
${styles.rightPanel.settingsTab.dropdown_SelectCompiler}
}
.heading {
@ -57,7 +69,6 @@ var css = csjs`
}
.pluginTextArea {
font-family: unset;
margin-top: 5px;
}
.pluginLoad {
vertical-align: top;
@ -65,6 +76,9 @@ var css = csjs`
i.warnIt {
color: ${styles.appProperties.warningText_Color};
}
.icon {
margin-right: .5em;
}
}
`
module.exports = SettingsTab
@ -79,45 +93,57 @@ function SettingsTab (container, appAPI, appEvents, opts) {
var el = yo`
<div class="${css.settingsTabView} "id="settingsView">
<div class="${css.info}">
<div>Your current Solidity version is</div>
<div id="version"></div>
</div>
<div class="${css.crow}">
<select class="${css.select}" id="versionSelector"></select>
</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 class=${css.title}>Solidity version</div>
<span>Current version:</span> <span id="version"></span>
<div class="${css.crow}">
<select class="${css.select}" id="versionSelector"></select>
</div>
</div>
<div class="${css.crow}">
<input class="${css.col1}" name="theme" id="themeDark" type="radio">
<label for="themeDark">Dark Theme</label>
<div class="${css.info}">
<div class=${css.title}>General settings</div>
<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>
<hr>
<div class="${css.crowNoFlex}">
<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.info}">
<div class=${css.title}>Themes</div>
<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>
<textarea rows="4" cols="70" id="plugininput" type="text" class="${css.pluginTextArea}" ></textarea>
<br />
<input onclick=${loadPlugin} type="button" value="Load" class="${css.pluginLoad}">
</div>
<div class="${css.info}">
<div class=${css.title}>Plugin</div>
<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>
`
function loadPlugin () {

@ -4,6 +4,7 @@ var CONFIG_FILE = '.remix.config'
function Config (storage) {
this.items = {}
this.unpersistedItems = {}
// load on instantiation
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

@ -166,7 +166,9 @@ function UniversalDApp (opts = {}) {
self.reset(self.contracts)
})
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 () {})
}
this.txRunner = new TxRunner(this.accounts, {
personalMode: this.personalMode
personalMode: this.personalMode,
config: this._api.config,
detectNetwork: this._api.detectNetwork
})
}

Loading…
Cancel
Save