remix-ide
Version:
Minimalistic browser-based Solidity IDE
202 lines (175 loc) • 8.43 kB
JavaScript
var yo = require('yo-yo')
var css = require('../styles/run-tab-styles')
var modalDialogCustom = require('../../ui/modal-dialog-custom')
var remixLib = require('remix-lib')
var EventManager = remixLib.EventManager
var confirmDialog = require('../../execution/confirmDialog')
var modalDialog = require('../../ui/modaldialog')
var MultiParamManager = require('../../../multiParamManager')
class ContractDropdownUI {
constructor (dropdownLogic, logCallback) {
this.dropdownLogic = dropdownLogic
this.logCallback = logCallback
this.event = new EventManager()
this.listenToEvents()
}
listenToEvents () {
this.dropdownLogic.event.register('newlyCompiled', (success, data, source, compiler, compilerFullName) => {
if (!document.querySelector(`.${css.contractNames.classNames[0]}`)) return
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()
if (success) {
this.compFails.style.display = 'none'
document.querySelector(`.${css.contractNames}`).classList.remove(css.contractNamesError)
} else {
this.compFails.style.display = 'block'
document.querySelector(`.${css.contractNames}`).classList.add(css.contractNamesError)
}
})
this.dropdownLogic.event.register('currentFileChanged', this.changeCurrentFile.bind(this))
}
render () {
this.compFails = yo`<i title="No contract compiled yet or compilation failed. Please check the compile tab for more information." class="fas fa-times-circle ${css.errorIcon}" ></i>`
var info = yo`<i class="fas 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} ${css.ataddressinput} ataddressinput form-control" placeholder="Load contract from Address" title="atAddress" />`
this.selectContractNames = yo`<select class="${css.contractNames} custom-select" disabled></select>`
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} ${this.compFails} ${info}
</div>
<div>
${this.createPanel}
${this.orLabel}
<div class="${css.button} ${css.atAddressSect}">
<div class="${css.atAddress} btn btn-sm btn-info" onclick=${this.loadFromAddress.bind(this)}>At Address</div>
${this.atAddressButtonInput}
</div>
</div>
</div>
`
this.selectContractNames.addEventListener('change', this.setInputParamsPlaceHolder.bind(this))
this.setInputParamsPlaceHolder()
return el
}
changeCurrentFile (currentFile) {
if (!document.querySelector(`.${css.contractNames}`)) return
document.querySelector(`.${css.contractNames}`).classList.remove(css.contractNamesError)
var contractNames = document.querySelector(`.${css.contractNames.classNames[0]}`)
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.createPanel.innerHTML = ''
if (this.selectContractNames.selectedIndex < 0 || this.selectContractNames.children.length <= 0) {
this.createPanel.innerHTML = 'No compiled contracts'
return
}
var selectedContract = this.getSelectedContract()
var createConstructorInstance = new MultiParamManager(0, selectedContract.getConstructorInterface(), (valArray, inputsValues) => {
this.createInstance(inputsValues)
}, selectedContract.getConstructorInputs(), 'Deploy', selectedContract.bytecodeObject)
this.createPanel.appendChild(createConstructorInstance.render())
}
getSelectedContract () {
var contract = this.selectContractNames.children[this.selectContractNames.selectedIndex]
var contractName = contract.innerHTML
var compilerAtributeName = contract.getAttribute('compiler')
return this.dropdownLogic.getSelectedContract(contractName, compilerAtributeName)
}
createInstance (args) {
var selectedContract = this.getSelectedContract()
if (selectedContract.bytecodeObject.length === 0) {
return modalDialogCustom.alert('This contract may be abstract, not implement an abstract parent\'s methods completely or not invoke an inherited contract\'s constructor correctly.')
}
var continueCb = (error, continueTxExecution, cancelCb) => {
if (error) {
var msg = typeof error !== 'string' ? error.message : error
modalDialog('Gas estimation failed', yo`<div>Gas estimation errored with the following message (see below).
The transaction execution will likely fail. Do you want to force sending? <br>
${msg}
</div>`,
{
label: 'Send Transaction',
fn: () => {
continueTxExecution()
}}, {
label: 'Cancel Transaction',
fn: () => {
cancelCb()
}
})
} else {
continueTxExecution()
}
}
var promptCb = (okCb, cancelCb) => {
modalDialogCustom.promptPassphrase('Passphrase requested', 'Personal mode is enabled. Please provide passphrase of account', '', okCb, cancelCb)
}
var statusCb = (msg) => {
return this.logCallback(msg)
}
var finalCb = (error, contractObject, address) => {
this.event.trigger('clearInstance')
if (error) {
return this.logCallback(error)
}
this.event.trigger('newContractInstanceAdded', [contractObject, address, contractObject.name])
}
if (selectedContract.isOverSizeLimit()) {
return modalDialog('Contract code size over limit', yo`<div>Contract creation initialization returns data with length of more than 24576 bytes. The deployment will likely fails. <br>
More info: <a href="https://github.com/ethereum/EIPs/blob/master/EIPS/eip-170.md" target="_blank">eip-170</a>
</div>`,
{
label: 'Force Send',
fn: () => {
this.dropdownLogic.forceSend(selectedContract, args, continueCb, promptCb, modalDialog, confirmDialog, statusCb, finalCb)
}}, {
label: 'Cancel',
fn: () => {
this.logCallback(`creation of ${selectedContract.name} canceled by user.`)
}
})
}
this.dropdownLogic.forceSend(selectedContract, args, continueCb, promptCb, modalDialog, confirmDialog, statusCb, finalCb)
}
loadFromAddress () {
this.event.trigger('clearInstance')
var address = this.atAddressButtonInput.value
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)
}
if (loadType === 'abi') {
return this.event.trigger('newContractABIAdded', [abi, address])
}
var selectedContract = this.getSelectedContract()
this.event.trigger('newContractInstanceAdded', [selectedContract.object, address, this.selectContractNames.value])
}
)
}
}
module.exports = ContractDropdownUI