Merge pull request #1037 from ethereum/refactor/separate_universal_dapp

separate universal-dapp logic from its UI code
pull/1/head
yann300 7 years ago committed by GitHub
commit 99c82922cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 9
      src/app.js
  2. 8
      src/app/tabs/run-tab.js
  3. 133
      src/universal-dapp-styles.js
  4. 153
      src/universal-dapp-ui.js
  5. 358
      src/universal-dapp.js

@ -7,6 +7,7 @@ var remixLib = require('remix-lib')
var EventManager = remixLib.EventManager
var UniversalDApp = require('./universal-dapp.js')
var UniversalDAppUI = require('./universal-dapp-ui.js')
var Remixd = require('./lib/remixd')
var OffsetToLineColumnConverter = require('./lib/offsetToLineColumnConverter')
@ -271,7 +272,11 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
},
opt: { removable: false, removable_instances: true }
})
var udappUI = new UniversalDAppUI(udapp)
udapp.reset({}, transactionContextAPI)
udappUI.reset()
udapp.event.register('debugRequested', this, function (txResult) {
startdebugging(txResult.transactionHash)
})
@ -586,6 +591,9 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
udapp: () => {
return udapp
},
udappUI: () => {
return udappUI
},
switchFile: function (path) {
fileManager.switchFile(path)
},
@ -619,6 +627,7 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
},
resetDapp: (contracts) => {
udapp.reset(contracts, transactionContextAPI)
udappUI.reset()
},
setOptimize: (optimize, runCompilation) => {
compiler.setOptimize(optimize)

@ -379,7 +379,7 @@ function makeRecorder (events, appAPI, appEvents) {
if (txArray.length) {
noInstancesText.style.display = 'none'
recorder.run(txArray, accounts, options, abis, linkReferences, (abi, address, contractName) => {
instanceContainer.appendChild(appAPI.udapp().renderInstanceFromABI(abi, address, contractName))
instanceContainer.appendChild(appAPI.udappUI().renderInstanceFromABI(abi, address, contractName))
})
}
} else {
@ -484,7 +484,7 @@ function contractDropdown (events, appAPI, appEvents, instanceContainer) {
}
noInstancesText.style.display = 'none'
var address = isVM ? txResult.result.createdAddress : txResult.result.contractAddress
instanceContainer.appendChild(appAPI.udapp().renderInstance(selectedContract.contract.object, address, selectContractNames.value))
instanceContainer.appendChild(appAPI.udappUI().renderInstance(selectedContract.contract.object, address, selectContractNames.value))
} else {
appAPI.logMessage(`creation of ${selectedContract.name} errored: ` + error)
}
@ -509,11 +509,11 @@ function contractDropdown (events, appAPI, appEvents, instanceContainer) {
} catch (e) {
return modalDialogCustom.alert('Failed to parse the current file as JSON ABI.')
}
instanceContainer.appendChild(appAPI.udapp().renderInstanceFromABI(abi, address, address))
instanceContainer.appendChild(appAPI.udappUI().renderInstanceFromABI(abi, address, address))
})
} else {
var contract = appAPI.getContract(contractNames.children[contractNames.selectedIndex].innerHTML)
instanceContainer.appendChild(appAPI.udapp().renderInstance(contract.object, address, selectContractNames.value))
instanceContainer.appendChild(appAPI.udappUI().renderInstance(contract.object, address, selectContractNames.value))
}
}

@ -0,0 +1,133 @@
var csjs = require('csjs-inject')
var remixLib = require('remix-lib')
var styleGuide = remixLib.ui.themeChooser
var styles = styleGuide.chooser()
var css = csjs`
.instanceTitleContainer {
display: flex;
align-items: center;
}
.title {
${styles.rightPanel.runTab.titlebox_RunTab}
display: flex;
justify-content: end;
align-items: center;
font-size: 11px;
height: 30px;
width: 97%;
overflow: hidden;
word-break: break-word;
line-height: initial;
overflow: visible;
}
.titleLine {
display: flex;
align-items: baseline;
}
.titleText {
margin-right: 1em;
word-break: break-word;
min-width: 230px;
}
.title .copy {
color: ${styles.rightPanel.runTab.icon_AltColor_Instance_CopyToClipboard};
}
.instance {
${styles.rightPanel.runTab.box_Instance};
margin-bottom: 10px;
padding: 10px 15px 15px 15px;
}
.instance .title:before {
content: "\\25BE";
margin-right: 5%;
}
.instance.hidesub .title:before {
content: "\\25B8";
margin-right: 5%;
}
.instance.hidesub > * {
display: none;
}
.instance.hidesub .title {
display: flex;
}
.instance.hidesub .udappClose {
display: flex;
}
.buttonsContainer {
margin-top: 2%;
display: flex;
overflow: hidden;
}
.contractActions {
display: flex;
}
.instanceButton {}
.closeIcon {
font-size: 12px;
cursor: pointer;
}
.udappClose {
display: flex;
justify-content: flex-end;
}
.contractProperty {
overflow: auto;
margin-bottom: 0.4em;
}
.contractProperty.hasArgs input {
width: 75%;
padding: .36em;
}
.contractProperty button {
${styles.rightPanel.runTab.button_Create}
min-width: 100px;
width: 100px;
font-size: 10px;
margin:0;
word-break: inherit;
}
.contractProperty button:disabled {
cursor: not-allowed;
background-color: white;
border-color: lightgray;
}
.contractProperty.constant button {
${styles.rightPanel.runTab.button_Constant}
min-width: 100px;
width: 100px;
font-size: 10px;
margin:0;
word-break: inherit;
outline: none;
width: inherit;
}
.contractProperty input {
display: none;
}
.contractProperty > .value {
box-sizing: border-box;
float: left;
align-self: center;
color: ${styles.appProperties.mainText_Color};
margin-left: 4px;
}
.hasArgs input {
display: block;
border: 1px solid #dddddd;
padding: .36em;
border-left: none;
padding: 8px 8px 8px 10px;
font-size: 10px;
height: 25px;
}
.hasArgs button {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
border-right: 0;
}
`
module.exports = css

@ -0,0 +1,153 @@
/* global */
'use strict'
var $ = require('jquery')
var yo = require('yo-yo')
var helper = require('./lib/helper')
var copyToClipboard = require('./app/ui/copy-to-clipboard')
var css = require('./universal-dapp-styles')
/*
trigger debugRequested
*/
function UniversalDAppUI (udapp, opts = {}) {
var self = this
this.udapp = udapp
self.el = yo`<div class=${css.udapp}></div>`
}
UniversalDAppUI.prototype.reset = function () {
this.el.innerHTML = ''
}
UniversalDAppUI.prototype.renderInstance = function (contract, address, contractName) {
var abi = this.udapp.getABI(contract)
return this.renderInstanceFromABI(abi, address, contractName)
}
// TODO this function was named before "appendChild".
// this will render an instance: contract name, contract address, and all the public functions
// basically this has to be called for the "atAddress" (line 393) and when a contract creation succeed
// this returns a DOM element
UniversalDAppUI.prototype.renderInstanceFromABI = function (contractABI, address, contractName) {
var self = this
function remove () { instance.remove() }
address = (address.slice(0, 2) === '0x' ? '' : '0x') + address.toString('hex')
var instance = yo`<div class="instance ${css.instance}" id="instance${address}"></div>`
var context = self.udapp.context()
var shortAddress = helper.shortenAddress(address)
var title = yo`<div class="${css.title}" onclick=${toggleClass}>
<div class="${css.titleText}"> ${contractName} at ${shortAddress} (${context}) </div>
${copyToClipboard(() => address)}
</div>`
if (self.udapp.removable_instances) {
var close = yo`<div class="${css.udappClose}" onclick=${remove}><i class="${css.closeIcon} fa fa-close" aria-hidden="true"></i></div>`
instance.append(close)
}
function toggleClass () {
$(instance).toggleClass(`${css.hidesub}`)
}
instance.appendChild(title)
// Add the fallback function
var fallback = self.udapp.getFallbackInterface(contractABI)
if (fallback) {
instance.appendChild(this.getCallButton({
funABI: fallback,
address: address,
contractAbi: contractABI,
contractName: contractName
}))
}
$.each(contractABI, (i, funABI) => {
if (funABI.type !== 'function') {
return
}
// @todo getData cannot be used with overloaded functions
instance.appendChild(this.getCallButton({
funABI: funABI,
address: address,
contractAbi: contractABI,
contractName: contractName
}))
})
return instance
}
// TODO this is used by renderInstance when a new instance is displayed.
// this returns a DOM element.
UniversalDAppUI.prototype.getCallButton = function (args) {
var self = this
// args.funABI, args.address [fun only]
// args.contractName [constr only]
var lookupOnly = args.funABI.constant
var inputs = self.udapp.getInputs(args.funABI)
var inputField = yo`<input></input>`
inputField.setAttribute('placeholder', inputs)
inputField.setAttribute('title', inputs)
var outputOverride = yo`<div class=${css.value}></div>`
var title
if (args.funABI.name) {
title = args.funABI.name
} else {
title = '(fallback)'
}
var button = yo`<button onclick=${clickButton} class="${css.instanceButton}"></button>`
button.classList.add(css.call)
button.setAttribute('title', title)
button.innerHTML = title
function clickButton () {
self.udapp.call(true, args, inputField.value, lookupOnly, (decoded) => {
outputOverride.innerHTML = ''
outputOverride.appendChild(decoded)
})
}
var contractProperty = yo`<div class="${css.contractProperty} ${css.buttonsContainer}"></div>`
var contractActions = yo`<div class="${css.contractActions}" ></div>`
contractProperty.appendChild(contractActions)
contractActions.appendChild(button)
if (inputs.length) {
contractActions.appendChild(inputField)
}
if (lookupOnly) {
contractProperty.appendChild(outputOverride)
}
if (lookupOnly) {
contractProperty.classList.add(css.constant)
button.setAttribute('title', (title + ' - call'))
}
if (args.funABI.inputs && args.funABI.inputs.length > 0) {
contractProperty.classList.add(css.hasArgs)
}
if (args.funABI.payable === true) {
contractProperty.classList.add(css.payable)
button.setAttribute('title', (title + ' - transact (payable)'))
}
if (!lookupOnly && args.funABI.payable === false) {
button.setAttribute('title', (title + ' - transact (not payable)'))
}
return contractProperty
}
module.exports = UniversalDAppUI

@ -1,154 +1,18 @@
/* global */
'use strict'
var $ = require('jquery')
var ethJSUtil = require('ethereumjs-util')
var BN = ethJSUtil.BN
var remixLib = require('remix-lib')
var EventManager = remixLib.EventManager
var crypto = require('crypto')
var TxRunner = require('./app/execution/txRunner')
var yo = require('yo-yo')
var txFormat = require('./app/execution/txFormat')
var txHelper = require('./app/execution/txHelper')
var txExecution = require('./app/execution/txExecution')
var helper = require('./lib/helper')
var executionContext = require('./execution-context')
var copyToClipboard = require('./app/ui/copy-to-clipboard')
var modalCustom = require('./app/ui/modal-dialog-custom')
// -------------- styling ----------------------
var csjs = require('csjs-inject')
var styleGuide = remixLib.ui.themeChooser
var styles = styleGuide.chooser()
var css = csjs`
.instanceTitleContainer {
display: flex;
align-items: center;
}
.title {
${styles.rightPanel.runTab.titlebox_RunTab}
display: flex;
justify-content: end;
align-items: center;
font-size: 11px;
height: 30px;
width: 97%;
overflow: hidden;
word-break: break-word;
line-height: initial;
overflow: visible;
}
.titleLine {
display: flex;
align-items: baseline;
}
.titleText {
margin-right: 1em;
word-break: break-word;
min-width: 230px;
}
.title .copy {
color: ${styles.rightPanel.runTab.icon_AltColor_Instance_CopyToClipboard};
}
.instance {
${styles.rightPanel.runTab.box_Instance};
margin-bottom: 10px;
padding: 10px 15px 15px 15px;
}
.instance .title:before {
content: "\\25BE";
margin-right: 5%;
}
.instance.hidesub .title:before {
content: "\\25B8";
margin-right: 5%;
}
.instance.hidesub > * {
display: none;
}
.instance.hidesub .title {
display: flex;
}
.instance.hidesub .udappClose {
display: flex;
}
.buttonsContainer {
margin-top: 2%;
display: flex;
overflow: hidden;
}
.contractActions {
display: flex;
}
.instanceButton {}
.closeIcon {
font-size: 12px;
cursor: pointer;
}
.udappClose {
display: flex;
justify-content: flex-end;
}
.contractProperty {
overflow: auto;
margin-bottom: 0.4em;
}
.contractProperty.hasArgs input {
width: 75%;
padding: .36em;
}
.contractProperty button {
${styles.rightPanel.runTab.button_Create}
min-width: 100px;
width: 100px;
font-size: 10px;
margin:0;
word-break: inherit;
}
.contractProperty button:disabled {
cursor: not-allowed;
background-color: white;
border-color: lightgray;
}
.contractProperty.constant button {
${styles.rightPanel.runTab.button_Constant}
min-width: 100px;
width: 100px;
font-size: 10px;
margin:0;
word-break: inherit;
outline: none;
width: inherit;
}
.contractProperty input {
display: none;
}
.contractProperty > .value {
box-sizing: border-box;
float: left;
align-self: center;
color: ${styles.appProperties.mainText_Color};
margin-left: 4px;
}
.hasArgs input {
display: block;
border: 1px solid #dddddd;
padding: .36em;
border-left: none;
padding: 8px 8px 8px 10px;
font-size: 10px;
height: 25px;
}
.hasArgs button {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
border-right: 0;
}
`
/*
trigger debugRequested
*/
@ -159,9 +23,6 @@ function UniversalDApp (opts = {}) {
self._api = opts.api
self.removable = opts.opt.removable
self.removable_instances = opts.opt.removable_instances
self.el = yo`<div class=${css.udapp}></div>`
self.contracts
self.transactionContextAPI
executionContext.event.register('contextChanged', this, function (context) {
self.reset(self.contracts)
})
@ -169,7 +30,6 @@ function UniversalDApp (opts = {}) {
}
UniversalDApp.prototype.reset = function (contracts, transactionContextAPI) {
this.el.innerHTML = ''
this.contracts = contracts
if (transactionContextAPI) {
this.transactionContextAPI = transactionContextAPI
@ -273,182 +133,72 @@ UniversalDApp.prototype.getBalance = function (address, cb) {
}
}
UniversalDApp.prototype.renderInstance = function (contract, address, contractName) {
var abi = txHelper.sortAbiFunction(contract.abi)
return this.renderInstanceFromABI(abi, address, contractName)
UniversalDApp.prototype.pendingTransactions = function () {
return this.txRunner.pendingTxs
}
// TODO this function was named before "appendChild".
// this will render an instance: contract name, contract address, and all the public functions
// basically this has to be called for the "atAddress" (line 393) and when a contract creation succeed
// this returns a DOM element
UniversalDApp.prototype.renderInstanceFromABI = function (contractABI, address, contractName) {
var self = this
function remove () { instance.remove() }
address = (address.slice(0, 2) === '0x' ? '' : '0x') + address.toString('hex')
var instance = yo`<div class="instance ${css.instance}" id="instance${address}"></div>`
var context = executionContext.isVM() ? 'memory' : 'blockchain'
var shortAddress = helper.shortenAddress(address)
var title = yo`<div class="${css.title}" onclick=${toggleClass}>
<div class="${css.titleText}"> ${contractName} at ${shortAddress} (${context}) </div>
${copyToClipboard(() => address)}
</div>`
if (self.removable_instances) {
var close = yo`<div class="${css.udappClose}" onclick=${remove}><i class="${css.closeIcon} fa fa-close" aria-hidden="true"></i></div>`
instance.append(close)
}
function toggleClass () {
$(instance).toggleClass(`${css.hidesub}`)
}
instance.appendChild(title)
// Add the fallback function
var fallback = txHelper.getFallbackInterface(contractABI)
if (fallback) {
instance.appendChild(this.getCallButton({
funABI: fallback,
address: address,
contractAbi: contractABI,
contractName: contractName
}))
}
$.each(contractABI, (i, funABI) => {
if (funABI.type !== 'function') {
return
UniversalDApp.prototype.call = function (isUserAction, args, value, lookupOnly, outputCb) {
const self = this
var logMsg
if (isUserAction) {
if (!args.funABI.constant) {
logMsg = `transact to ${args.contractName}.${(args.funABI.name) ? args.funABI.name : '(fallback)'}`
} else {
logMsg = `call to ${args.contractName}.${(args.funABI.name) ? args.funABI.name : '(fallback)'}`
}
// @todo getData cannot be used with overloaded functions
instance.appendChild(this.getCallButton({
funABI: funABI,
address: address,
contractAbi: contractABI,
contractName: contractName
}))
})
return instance
}
// TODO this is used by renderInstance when a new instance is displayed.
// this returns a DOM element.
UniversalDApp.prototype.getCallButton = function (args) {
var self = this
// args.funABI, args.address [fun only]
// args.contractName [constr only]
var lookupOnly = args.funABI.constant
var inputs = ''
if (args.funABI.inputs) {
inputs = txHelper.inputParametersDeclarationToString(args.funABI.inputs)
}
var inputField = yo`<input></input>`
inputField.setAttribute('placeholder', inputs)
inputField.setAttribute('title', inputs)
var outputOverride = yo`<div class=${css.value}></div>`
var title
if (args.funABI.name) {
title = args.funABI.name
} else {
title = '(fallback)'
}
var button = yo`<button onclick=${clickButton} class="${css.instanceButton}"></button>`
button.classList.add(css.call)
button.setAttribute('title', title)
button.innerHTML = title
function clickButton () {
call(true)
}
function call (isUserAction) {
var logMsg
if (isUserAction) {
if (!args.funABI.constant) {
logMsg = `transact to ${args.contractName}.${(args.funABI.name) ? args.funABI.name : '(fallback)'}`
} else {
logMsg = `call to ${args.contractName}.${(args.funABI.name) ? args.funABI.name : '(fallback)'}`
}
}
txFormat.buildData(args.contractName, args.contractAbi, self.contracts, false, args.funABI, inputField.value, self, (error, data) => {
if (!error) {
if (isUserAction) {
if (!args.funABI.constant) {
self._api.logMessage(`${logMsg} pending ... `)
} else {
self._api.logMessage(`${logMsg}`)
}
txFormat.buildData(args.contractName, args.contractAbi, self.contracts, false, args.funABI, value, self, (error, data) => {
if (!error) {
if (isUserAction) {
if (!args.funABI.constant) {
self._api.logMessage(`${logMsg} pending ... `)
} else {
self._api.logMessage(`${logMsg}`)
}
txExecution.callFunction(args.address, data, args.funABI, self, (error, txResult) => {
if (!error) {
var isVM = executionContext.isVM()
if (isVM) {
var vmError = txExecution.checkVMError(txResult)
if (vmError.error) {
self._api.logMessage(`${logMsg} errored: ${vmError.message} `)
return
}
}
if (lookupOnly) {
var decoded = txFormat.decodeResponseToTreeView(executionContext.isVM() ? txResult.result.vm.return : ethJSUtil.toBuffer(txResult.result), args.funABI)
outputOverride.innerHTML = ''
outputOverride.appendChild(decoded)
}
txExecution.callFunction(args.address, data, args.funABI, self, (error, txResult) => {
if (!error) {
var isVM = executionContext.isVM()
if (isVM) {
var vmError = txExecution.checkVMError(txResult)
if (vmError.error) {
self._api.logMessage(`${logMsg} errored: ${vmError.message} `)
return
}
} else {
self._api.logMessage(`${logMsg} errored: ${error} `)
}
})
} else {
self._api.logMessage(`${logMsg} errored: ${error} `)
}
}, (msg) => {
self._api.logMessage(msg)
})
}
var contractProperty = yo`<div class="${css.contractProperty} ${css.buttonsContainer}"></div>`
var contractActions = yo`<div class="${css.contractActions}" ></div>`
contractProperty.appendChild(contractActions)
contractActions.appendChild(button)
if (inputs.length) {
contractActions.appendChild(inputField)
}
if (lookupOnly) {
contractProperty.appendChild(outputOverride)
}
if (lookupOnly) {
contractProperty.classList.add(css.constant)
button.setAttribute('title', (title + ' - call'))
}
if (args.funABI.inputs && args.funABI.inputs.length > 0) {
contractProperty.classList.add(css.hasArgs)
}
if (lookupOnly) {
var decoded = txFormat.decodeResponseToTreeView(executionContext.isVM() ? txResult.result.vm.return : ethJSUtil.toBuffer(txResult.result), args.funABI)
outputCb(decoded)
}
} else {
self._api.logMessage(`${logMsg} errored: ${error} `)
}
})
} else {
self._api.logMessage(`${logMsg} errored: ${error} `)
}
}, (msg) => {
self._api.logMessage(msg)
})
}
if (args.funABI.payable === true) {
contractProperty.classList.add(css.payable)
button.setAttribute('title', (title + ' - transact (payable)'))
}
UniversalDApp.prototype.context = function () {
return (executionContext.isVM() ? 'memory' : 'blockchain')
}
if (!lookupOnly && args.funABI.payable === false) {
button.setAttribute('title', (title + ' - transact (not payable)'))
}
UniversalDApp.prototype.getABI = function (contract) {
return txHelper.sortAbiFunction(contract.abi)
}
return contractProperty
UniversalDApp.prototype.getFallbackInterface = function (contractABI) {
return txHelper.getFallbackInterface(contractABI)
}
UniversalDApp.prototype.pendingTransactions = function () {
return this.txRunner.pendingTxs
UniversalDApp.prototype.getInputs = function (funABI) {
if (!funABI.inputs) {
return ''
}
return txHelper.inputParametersDeclarationToString(funABI.inputs)
}
function execute (pipeline, env, callback) {

Loading…
Cancel
Save