refactor contract dropdown; separate logic from view code; refactor code

pull/1/head
Iuri Matias 6 years ago committed by yann300
parent 7ff7b8e5bf
commit d095b83531
  1. 27
      src/app/tabs/run-tab.js
  2. 227
      src/app/tabs/runTab/contractDropdown.js
  3. 70
      src/app/tabs/runTab/model/dropdownlogic.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`
<div>
${settingsUI.render()}

@ -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`<i title="Contract compilation failed. Please check the compile tab for more information." class="fa fa-times-circle ${css.errorIcon}" ></i>`
var info = yo`<i class="fa fa-info ${css.infoDeployAction}" aria-hidden="true" title="*.sol files allows deploying and accessing contracts. *.abi files only allows accessing contracts."></i>`
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`<option compiler="${compilerFullName}">${contract.name}</option>`)
})
} 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`<option>(abi)</option>`)
this.selectContractNames.setAttribute('disabled', true)
} else if (/.(.sol)$/.exec(currentFile)) {
deployAction('block')
}
})
render () {
this.compFails = yo`<i title="Contract compilation failed. Please check the compile tab for more information." class="fa fa-times-circle ${css.errorIcon}" ></i>`
var info = yo`<i class="fa fa-info ${css.infoDeployAction}" aria-hidden="true" title="*.sol files allows deploying and accessing contracts. *.abi files only allows accessing contracts."></i>`
this.atAddressButtonInput = yo`<input class="${css.input} ataddressinput" placeholder="Load contract from Address" title="atAddress" />`
this.selectContractNames = yo`<select class="${css.contractNames}" disabled></select>`
this.parentSelf._view.createPanel = yo`<div class="${css.button}"></div>`
this.parentSelf._view.orLabel = yo`<div class="${css.orLabel}">or</div>`
this.createPanel = yo`<div class="${css.button}"></div>`
this.orLabel = yo`<div class="${css.orLabel}">or</div>`
var el = yo`
<div class="${css.container}">
<div class="${css.subcontainer}">
${this.selectContractNames} ${compFails} ${info}
${this.selectContractNames} ${this.compFails} ${info}
</div>
<div>
${this.parentSelf._view.createPanel}
${this.parentSelf._view.orLabel}
${this.createPanel}
${this.orLabel}
<div class="${css.button} ${css.atAddressSect}">
<div class="${css.atAddress}" onclick=${function () { this.loadFromAddress() }}>At Address</div>
<div class="${css.atAddress}" onclick=${this.loadFromAddress.bind(this)}>At Address</div>
${this.atAddressButtonInput}
</div>
</div>
@ -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`<option>(abi)</option>`)
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`<option compiler="${compilerFullName}">${contract.name}</option>`)
})
} 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])
}
)
}
}

@ -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
Loading…
Cancel
Save