diff --git a/src/app.js b/src/app.js index 5e49e305cd..7a709392ac 100644 --- a/src/app.js +++ b/src/app.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') @@ -262,6 +263,10 @@ function run () { }, opt: { removable: false, removable_instances: true } }) + + var udappUI = new UniversalDAppUI(udapp) + console.dir(udappUI) + udapp.reset({}, transactionContextAPI) udapp.event.register('debugRequested', this, function (txResult) { startdebugging(txResult.transactionHash) diff --git a/src/universal-dapp-ui.js b/src/universal-dapp-ui.js new file mode 100644 index 0000000000..b2f1e769a8 --- /dev/null +++ b/src/universal-dapp-ui.js @@ -0,0 +1,336 @@ +/* global */ +'use strict' + +var $ = require('jquery') +var ethJSUtil = require('ethereumjs-util') +var remixLib = require('remix-lib') +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') + +// -------------- 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 +*/ +function UniversalDAppUI (udapp, opts = {}) { + var self = this + this.udapp = udapp + + self.el = yo`
` +} + +UniversalDAppUI.prototype.reset = function (contracts, transactionContextAPI) { + this.el.innerHTML = '' +} + +// TODO: has both UI and Model +UniversalDAppUI.prototype.renderInstance = function (contract, address, contractName) { + var abi = txHelper.sortAbiFunction(contract.abi) + 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`
` + var context = executionContext.isVM() ? 'memory' : 'blockchain' + + var shortAddress = helper.shortenAddress(address) + var title = yo`
+
${contractName} at ${shortAddress} (${context})
+ ${copyToClipboard(() => address)} +
` + + if (self.removable_instances) { + var close = yo`
` + 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 + } + // @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 = '' + if (args.funABI.inputs) { + inputs = txHelper.inputParametersDeclarationToString(args.funABI.inputs) + } + var inputField = yo`` + inputField.setAttribute('placeholder', inputs) + inputField.setAttribute('title', inputs) + + var outputOverride = yo`
` + + var title + if (args.funABI.name) { + title = args.funABI.name + } else { + title = '(fallback)' + } + + var button = yo`` + 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}`) + } + } + 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) + } + } else { + self._api.logMessage(`${logMsg} errored: ${error} `) + } + }) + } else { + self._api.logMessage(`${logMsg} errored: ${error} `) + } + }, (msg) => { + self._api.logMessage(msg) + }) + } + + var contractProperty = yo`
` + var contractActions = yo`
` + + 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 diff --git a/src/universal-dapp.js b/src/universal-dapp.js index bf28945711..0ae6552f00 100644 --- a/src/universal-dapp.js +++ b/src/universal-dapp.js @@ -1,165 +1,30 @@ /* 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 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 */ -function UniversalDApp (opts = {}) { +function UniversalDAppModel (opts = {}) { this.event = new EventManager() var self = this self._api = opts.api self.removable = opts.opt.removable self.removable_instances = opts.opt.removable_instances - self.el = yo`
` + self.personalMode = opts.opt.personalMode || false self.contracts self.transactionContextAPI executionContext.event.register('contextChanged', this, function (context) { @@ -168,8 +33,7 @@ function UniversalDApp (opts = {}) { self.txRunner = new TxRunner({}, opts.api) } -UniversalDApp.prototype.reset = function (contracts, transactionContextAPI) { - this.el.innerHTML = '' +UniversalDAppModel.prototype.reset = function (contracts, transactionContextAPI) { this.contracts = contracts if (transactionContextAPI) { this.transactionContextAPI = transactionContextAPI @@ -186,7 +50,7 @@ UniversalDApp.prototype.reset = function (contracts, transactionContextAPI) { this.txRunner = new TxRunner(this.accounts, this._api) } -UniversalDApp.prototype.newAccount = function (password, cb) { +UniversalDAppModel.prototype.newAccount = function (password, cb) { if (!executionContext.isVM()) { if (!this._api.personalMode()) { return cb('Not running in personal mode') @@ -208,7 +72,7 @@ UniversalDApp.prototype.newAccount = function (password, cb) { } } -UniversalDApp.prototype._addAccount = function (privateKey, balance) { +UniversalDAppModel.prototype._addAccount = function (privateKey, balance) { var self = this if (!executionContext.isVM()) { @@ -225,7 +89,7 @@ UniversalDApp.prototype._addAccount = function (privateKey, balance) { } } -UniversalDApp.prototype.getAccounts = function (cb) { +UniversalDAppModel.prototype.getAccounts = function (cb) { var self = this if (!executionContext.isVM()) { @@ -245,7 +109,7 @@ UniversalDApp.prototype.getAccounts = function (cb) { } } -UniversalDApp.prototype.getBalance = function (address, cb) { +UniversalDAppModel.prototype.getBalance = function (address, cb) { var self = this address = ethJSUtil.stripHexPrefix(address) @@ -273,181 +137,7 @@ 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) -} - -// 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`
` - var context = executionContext.isVM() ? 'memory' : 'blockchain' - - var shortAddress = helper.shortenAddress(address) - var title = yo`
-
${contractName} at ${shortAddress} (${context})
- ${copyToClipboard(() => address)} -
` - - if (self.removable_instances) { - var close = yo`
` - 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 - } - // @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`` - inputField.setAttribute('placeholder', inputs) - inputField.setAttribute('title', inputs) - - var outputOverride = yo`
` - - var title - if (args.funABI.name) { - title = args.funABI.name - } else { - title = '(fallback)' - } - - var button = yo`` - 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}`) - } - } - 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) - } - } else { - self._api.logMessage(`${logMsg} errored: ${error} `) - } - }) - } else { - self._api.logMessage(`${logMsg} errored: ${error} `) - } - }, (msg) => { - self._api.logMessage(msg) - }) - } - - var contractProperty = yo`
` - var contractActions = yo`
` - - 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 -} - -UniversalDApp.prototype.pendingTransactions = function () { +UniversalDAppModel.prototype.pendingTransactions = function () { return this.txRunner.pendingTxs } @@ -461,7 +151,7 @@ function execute (pipeline, env, callback) { next(null, env) } -UniversalDApp.prototype.runTx = function (args, cb) { +UniversalDAppModel.prototype.runTx = function (args, cb) { var self = this var tx = { to: args.to, data: args.data.dataHex, useCall: args.useCall, from: args.from, value: args.value } var payLoad = { funAbi: args.data.funAbi, funArgs: args.data.funArgs, contractBytecode: args.data.contractBytecode, contractName: args.data.contractName } // contains decoded parameters @@ -546,4 +236,4 @@ function runTransaction (env, next) { }) } -module.exports = UniversalDApp +module.exports = UniversalDAppModel