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