/* 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