From d095b8353114cbed83f05b725adccc113e5125ac Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Fri, 28 Dec 2018 13:33:50 -0500 Subject: [PATCH] refactor contract dropdown; separate logic from view code; refactor code --- src/app/tabs/run-tab.js | 27 ++- src/app/tabs/runTab/contractDropdown.js | 227 +++++++++------------ src/app/tabs/runTab/model/dropdownlogic.js | 70 +++++++ 3 files changed, 189 insertions(+), 135 deletions(-) create mode 100644 src/app/tabs/runTab/model/dropdownlogic.js diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index 48999f914e..d3b90c34db 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -9,6 +9,7 @@ var css = require('./styles/run-tab-styles') var Settings = require('./runTab/model/settings.js') var SettingsUI = require('./runTab/settings.js') +var DropdownLogic = require('./runTab/model/dropdownLogic.js') var ContractDropdownUI = require('./runTab/contractDropdown.js') var Recorder = require('./runTab/model/recorder.js') @@ -133,17 +134,31 @@ function runTab (opts, localRegistry) { 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) + this._view.instanceContainer.innerHTML = '' // clear the instances list + this._view.instanceContainer.appendChild(self._view.instanceContainerTitle) + this._view.instanceContainer.appendChild(self._view.noInstancesText) }) settingsUI.event.register('clearInstance', () => { this.event.trigger('clearInstance', []) }) - var contractDropdownUI = new ContractDropdownUI(self) + var dropdownLogic = new DropdownLogic(self) + var contractDropdownUI = new ContractDropdownUI(dropdownLogic, self) + + contractDropdownUI.event.register('clearInstance', () => { + var noInstancesText = this._view.noInstancesText + if (noInstancesText.parentNode) { noInstancesText.parentNode.removeChild(noInstancesText) } + }) + contractDropdownUI.event.register('newContractABIAdded', (abi, address) => { + this._view.instanceContainer.appendChild(this._deps.udappUI.renderInstanceFromABI(abi, address, address)) + }) + contractDropdownUI.event.register('newContractInstanceAdded', (contractObject, address, value) => { + this._view.instanceContainer.appendChild(this._deps.udappUI.renderInstance(contractObject, address, value)) + }) + + this._view.instanceContainer.appendChild(this._view.instanceContainerTitle) + this._view.instanceContainer.appendChild(this._view.noInstancesText) + var el = yo`
${settingsUI.render()} diff --git a/src/app/tabs/runTab/contractDropdown.js b/src/app/tabs/runTab/contractDropdown.js index 6e9fd461a4..f1157aaa04 100644 --- a/src/app/tabs/runTab/contractDropdown.js +++ b/src/app/tabs/runTab/contractDropdown.js @@ -1,93 +1,71 @@ var yo = require('yo-yo') -var ethJSUtil = require('ethereumjs-util') var css = require('../styles/run-tab-styles') var executionContext = require('../../../execution-context') var modalDialogCustom = require('../../ui/modal-dialog-custom') -var modalCustom = require('../../ui/modal-dialog-custom') -var CompilerAbstract = require('../../compiler/compiler-abstract') var remixLib = require('remix-lib') var txExecution = remixLib.execution.txExecution var txFormat = remixLib.execution.txFormat var txHelper = remixLib.execution.txHelper +var EventManager = remixLib.EventManager var typeConversion = remixLib.execution.typeConversion var confirmDialog = require('../../execution/confirmDialog') var modalDialog = require('../../ui/modaldialog') var MultiParamManager = require('../../../multiParamManager') class ContractDropdownUI { - constructor (parentSelf) { + constructor (dropdownLogic, parentSelf) { this.parentSelf = parentSelf + this.dropdownLogic = dropdownLogic + this.event = new EventManager() + + this.listenToEvents() } - render () { - this.instanceContainer = this.parentSelf._view.instanceContainer - var instanceContainerTitle = this.parentSelf._view.instanceContainerTitle - this.instanceContainer.appendChild(instanceContainerTitle) - this.instanceContainer.appendChild(this.parentSelf._view.noInstancesText) - var compFails = yo`` - var info = yo`` + listenToEvents () { + this.dropdownLogic.event.register('newlyCompiled', (success, data, source, compiler, compilerFullName) => { + var contractNames = document.querySelector(`.${css.contractNames.classNames[0]}`) + contractNames.innerHTML = '' + if (success) { + this.selectContractNames.removeAttribute('disabled') + this.dropdownLogic.getCompiledContracts(compiler, compilerFullName).forEach((contract) => { + contractNames.appendChild(yo``) + }) + } else { + this.selectContractNames.setAttribute('disabled', true) + } + this.setInputParamsPlaceHolder() - var newlyCompiled = (success, data, source, compiler, compilerFullName) => { - this.getContractNames(success, data, compiler, compilerFullName) if (success) { - compFails.style.display = 'none' + this.compFails.style.display = 'none' document.querySelector(`.${css.contractNames}`).classList.remove(css.contractNamesError) } else { - compFails.style.display = 'block' + this.compFails.style.display = 'block' document.querySelector(`.${css.contractNames}`).classList.add(css.contractNamesError) } - } - - this.parentSelf._deps.pluginManager.event.register('sendCompilationResult', (file, source, languageVersion, data) => { - // TODO check whether the tab is configured - let compiler = new CompilerAbstract(languageVersion, data) - this.parentSelf._deps.compilersArtefacts[languageVersion] = compiler - this.parentSelf._deps.compilersArtefacts['__last'] = compiler - newlyCompiled(true, data, source, compiler, languageVersion) }) - this.parentSelf._deps.compiler.event.register('compilationFinished', (success, data, source) => { - var name = 'solidity' - let compiler = new CompilerAbstract(name, data) - this.parentSelf._deps.compilersArtefacts[name] = compiler - this.parentSelf._deps.compilersArtefacts['__last'] = compiler - newlyCompiled(success, data, source, this.parentSelf._deps.compiler, name) - }) - - var deployAction = (value) => { - this.parentSelf._view.createPanel.style.display = value - this.parentSelf._view.orLabel.style.display = value - } + this.dropdownLogic.event.register('currentFileChanged', this.changeCurrentFile.bind(this)) + } - this.parentSelf._deps.fileManager.event.register('currentFileChanged', (currentFile) => { - document.querySelector(`.${css.contractNames}`).classList.remove(css.contractNamesError) - var contractNames = document.querySelector(`.${css.contractNames.classNames[0]}`) - contractNames.innerHTML = '' - if (/.(.abi)$/.exec(currentFile)) { - deployAction('none') - compFails.style.display = 'none' - contractNames.appendChild(yo``) - this.selectContractNames.setAttribute('disabled', true) - } else if (/.(.sol)$/.exec(currentFile)) { - deployAction('block') - } - }) + render () { + this.compFails = yo`` + var info = yo`` this.atAddressButtonInput = yo`` this.selectContractNames = yo`` - this.parentSelf._view.createPanel = yo`
` - this.parentSelf._view.orLabel = yo`
or
` + this.createPanel = yo`
` + this.orLabel = yo`
or
` var el = yo`
- ${this.selectContractNames} ${compFails} ${info} + ${this.selectContractNames} ${this.compFails} ${info}
- ${this.parentSelf._view.createPanel} - ${this.parentSelf._view.orLabel} + ${this.createPanel} + ${this.orLabel}
-
At Address
+
At Address
${this.atAddressButtonInput}
@@ -98,36 +76,50 @@ class ContractDropdownUI { return el } + changeCurrentFile (currentFile) { + document.querySelector(`.${css.contractNames}`).classList.remove(css.contractNamesError) + var contractNames = document.querySelector(`.${css.contractNames.classNames[0]}`) + contractNames.innerHTML = '' + if (/.(.abi)$/.exec(currentFile)) { + this.createPanel.style.display = 'none' + this.orLabel.style.display = 'none' + this.compFails.style.display = 'none' + contractNames.appendChild(yo``) + this.selectContractNames.setAttribute('disabled', true) + } else if (/.(.sol)$/.exec(currentFile)) { + this.createPanel.style.display = 'block' + this.orLabel.style.display = 'block' + } + } + setInputParamsPlaceHolder () { - this.parentSelf._view.createPanel.innerHTML = '' - if (this.selectContractNames.selectedIndex >= 0 && this.selectContractNames.children.length > 0) { - var selectedContract = this.getSelectedContract() - var ctrabi = txHelper.getConstructorInterface(selectedContract.contract.object.abi) - var ctrEVMbc = selectedContract.contract.object.evm.bytecode.object - var createConstructorInstance = new MultiParamManager(0, ctrabi, (valArray, inputsValues) => { - this.createInstance(inputsValues, selectedContract.compiler) - }, txHelper.inputParametersDeclarationToString(ctrabi.inputs), 'Deploy', ctrEVMbc) - this.parentSelf._view.createPanel.appendChild(createConstructorInstance.render()) + this.createPanel.innerHTML = '' + if (this.selectContractNames.selectedIndex < 0 || this.selectContractNames.children.length <= 0) { + this.createPanel.innerHTML = 'No compiled contracts' return - } else { - this.parentSelf._view.createPanel.innerHTML = 'No compiled contracts' } + + var selectedContract = this.getSelectedContract() + var ctrabi = txHelper.getConstructorInterface(selectedContract.contract.object.abi) + var ctrEVMbc = selectedContract.contract.object.evm.bytecode.object + var createConstructorInstance = new MultiParamManager(0, ctrabi, (valArray, inputsValues) => { + this.createInstance(inputsValues, selectedContract.compiler) + }, txHelper.inputParametersDeclarationToString(ctrabi.inputs), 'Deploy', ctrEVMbc) + this.createPanel.appendChild(createConstructorInstance.render()) } getSelectedContract () { var contract = this.selectContractNames.children[this.selectContractNames.selectedIndex] var contractName = contract.innerHTML - var compiler = this.parentSelf._deps.compilersArtefacts[contract.getAttribute('compiler')] + var compiler = this.dropdownLogic.getContractCompiler(contract.getAttribute('compiler')) if (!compiler) return null - if (contractName) { - return { - name: contractName, - contract: compiler.getContract(contractName), - compiler - } + if (!contractName) return null + return { + name: contractName, + contract: compiler.getContract(contractName), + compiler } - return null } createInstanceCallback (selectedContract, data) { @@ -214,29 +206,27 @@ class ContractDropdownUI { } }, function (okCb, cancelCb) { - modalCustom.promptPassphrase(null, 'Personal mode is enabled. Please provide passphrase of account', '', okCb, cancelCb) + modalDialogCustom.promptPassphrase(null, 'Personal mode is enabled. Please provide passphrase of account', '', okCb, cancelCb) }, (error, txResult) => { - if (!error) { - var isVM = executionContext.isVM() - if (isVM) { - var vmError = txExecution.checkVMError(txResult) - if (vmError.error) { - this.parentSelf._deps.logCallback(vmError.message) - return - } - } - if (txResult.result.status && txResult.result.status === '0x0') { - this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} errored: transaction execution failed`) + if (error) { + return this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} errored: ${error}`) + } + var isVM = executionContext.isVM() + if (isVM) { + var vmError = txExecution.checkVMError(txResult) + if (vmError.error) { + this.parentSelf._deps.logCallback(vmError.message) return } - var noInstancesText = this.parentSelf._view.noInstancesText - if (noInstancesText.parentNode) { noInstancesText.parentNode.removeChild(noInstancesText) } - var address = isVM ? txResult.result.createdAddress : txResult.result.contractAddress - this.instanceContainer.appendChild(this.parentSelf._deps.udappUI.renderInstance(selectedContract.contract.object, address, this.selectContractNames.value)) - } else { - this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} errored: ${error}`) } + if (txResult.result.status && txResult.result.status === '0x0') { + this.parentSelf._deps.logCallback(`creation of ${selectedContract.name} errored: transaction execution failed`) + return + } + this.event.trigger('clearInstance') + var address = isVM ? txResult.result.createdAddress : txResult.result.contractAddress + this.event.trigger('newContractInstanceAdded', [selectedContract.contract.object, address, this.selectContractNames.value]) } ) } @@ -317,7 +307,7 @@ class ContractDropdownUI { }) }, function (okCb, cancelCb) { - modalCustom.promptPassphrase(null, 'Personal mode is enabled. Please provide passphrase of account', '', okCb, cancelCb) + modalDialogCustom.promptPassphrase(null, 'Personal mode is enabled. Please provide passphrase of account', '', okCb, cancelCb) }, runTxCallback) }) @@ -350,46 +340,25 @@ class ContractDropdownUI { } } - // ACCESS DEPLOYED INSTANCE loadFromAddress () { - var noInstancesText = this.parentSelf._view.noInstancesText - if (noInstancesText.parentNode) { noInstancesText.parentNode.removeChild(noInstancesText) } + this.event.trigger('clearInstance') + var address = this.atAddressButtonInput.value - if (!ethJSUtil.isValidAddress(address)) { - return modalDialogCustom.alert('Invalid address.') - } - if (/[a-f]/.test(address) && /[A-F]/.test(address) && !ethJSUtil.isValidChecksumAddress(address)) { - return modalDialogCustom.alert('Invalid checksum address.') - } - if (/.(.abi)$/.exec(this.parentSelf._deps.config.get('currentFile'))) { - modalDialogCustom.confirm(null, 'Do you really want to interact with ' + address + ' using the current ABI definition ?', () => { - var abi - try { - abi = JSON.parse(this.parentSelf._deps.editor.currentContent()) - } catch (e) { - return modalDialogCustom.alert('Failed to parse the current file as JSON ABI.') + this.dropdownLogic.loadContractFromAddress(address, + (cb) => { + modalDialogCustom.confirm(null, 'Do you really want to interact with ' + address + ' using the current ABI definition ?', cb) + }, + (error, loadType, abi) => { + if (error) { + return modalDialogCustom.alert(error) } - this.instanceContainer.appendChild(this.parentSelf._deps.udappUI.renderInstanceFromABI(abi, address, address)) - }) - } else { - var selectedContract = this.getSelectedContract() - this.instanceContainer.appendChild(this.parentSelf._deps.udappUI.renderInstance(selectedContract.contract.object, address, this.selectContractNames.value)) - } - } - - // GET NAMES OF ALL THE CONTRACTS - getContractNames (success, data, compiler, compilerFullName) { - var contractNames = document.querySelector(`.${css.contractNames.classNames[0]}`) - contractNames.innerHTML = '' - if (success) { - this.selectContractNames.removeAttribute('disabled') - compiler.visitContracts((contract) => { - contractNames.appendChild(yo``) - }) - } else { - this.selectContractNames.setAttribute('disabled', true) - } - this.setInputParamsPlaceHolder() + if (loadType === 'abi') { + return this.event.trigger('newContractABIAdded', [abi, address]) + } + var selectedContract = this.getSelectedContract() + this.event.trigger('newContractInstanceAdded', [selectedContract.contract.object, address, this.selectContractNames.value]) + } + ) } } diff --git a/src/app/tabs/runTab/model/dropdownlogic.js b/src/app/tabs/runTab/model/dropdownlogic.js new file mode 100644 index 0000000000..fce17512d7 --- /dev/null +++ b/src/app/tabs/runTab/model/dropdownlogic.js @@ -0,0 +1,70 @@ +var ethJSUtil = require('ethereumjs-util') +var remixLib = require('remix-lib') +var CompilerAbstract = require('../../../compiler/compiler-abstract') +var EventManager = remixLib.EventManager + +class DropdownLogic { + constructor (parentSelf) { + this.parentSelf = parentSelf + this.event = new EventManager() + + this.listenToCompilationEvents() + + this.parentSelf._deps.fileManager.event.register('currentFileChanged', (currentFile) => { + this.event.trigger('currentFileChanged', [currentFile]) + }) + } + + listenToCompilationEvents () { + this.parentSelf._deps.pluginManager.event.register('sendCompilationResult', (file, source, languageVersion, data) => { + // TODO check whether the tab is configured + let compiler = new CompilerAbstract(languageVersion, data) + this.parentSelf._deps.compilersArtefacts[languageVersion] = compiler + this.parentSelf._deps.compilersArtefacts['__last'] = compiler + this.event.trigger('newlyCompiled', [true, data, source, compiler, languageVersion]) + }) + + this.parentSelf._deps.compiler.event.register('compilationFinished', (success, data, source) => { + var name = 'solidity' + let compiler = new CompilerAbstract(name, data) + this.parentSelf._deps.compilersArtefacts[name] = compiler + this.parentSelf._deps.compilersArtefacts['__last'] = compiler + this.event.trigger('newlyCompiled', [success, data, source, this.parentSelf._deps.compiler, name]) + }) + } + + loadContractFromAddress (address, confirmCb, cb) { + if (!ethJSUtil.isValidAddress(address)) { + return cb('Invalid address.') + } + if (/[a-f]/.test(address) && /[A-F]/.test(address) && !ethJSUtil.isValidChecksumAddress(address)) { + return cb('Invalid checksum address.') + } + if (/.(.abi)$/.exec(this.parentSelf._deps.config.get('currentFile'))) { + confirmCb(() => { + var abi + try { + abi = JSON.parse(this.parentSelf._deps.editor.currentContent()) + } catch (e) { + return cb('Failed to parse the current file as JSON ABI.') + } + cb(null, 'abi', abi) + }) + } + cb(null, 'instance') + } + + getCompiledContracts (compiler, compilerFullName) { + var contracts = [] + compiler.visitContracts((contract) => { + contracts.push(contract) + }) + return contracts + } + + getContractCompiler (name) { + return this.parentSelf._deps.compilersArtefacts[name] + } +} + +module.exports = DropdownLogic