UNPKG

remix-ide

Version:
264 lines (237 loc) 10 kB
/* global */ 'use strict' var $ = require('jquery') var yo = require('yo-yo') var ethJSUtil = require('ethereumjs-util') var BN = ethJSUtil.BN var helper = require('../../lib/helper') var copyToClipboard = require('./copy-to-clipboard') var css = require('../../universal-dapp-styles') var MultiParamManager = require('./multiParamManager') var remixLib = require('remix-lib') var txFormat = remixLib.execution.txFormat const txHelper = remixLib.execution.txHelper var TreeView = require('./TreeView') var txCallBacks = require('./sendTxCallbacks') function UniversalDAppUI (blockchain, logCallback) { this.blockchain = blockchain this.logCallback = logCallback this.compilerData = {contractsDetails: {}} } function decodeResponseToTreeView (response, fnabi) { var treeView = new TreeView({ extractData: (item, parent, key) => { var ret = {} if (BN.isBN(item)) { ret.self = item.toString(10) ret.children = [] } else { ret = treeView.extractDataDefault(item, parent, key) } return ret } }) return treeView.render(txFormat.decodeResponse(response, fnabi)) } UniversalDAppUI.prototype.renderInstance = function (contract, address, contractName) { var noInstances = document.querySelector('[data-id="deployAndRunNoInstanceText"]') if (noInstances) { noInstances.parentNode.removeChild(noInstances) } const 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) { let self = this address = (address.slice(0, 2) === '0x' ? '' : '0x') + address.toString('hex') address = ethJSUtil.toChecksumAddress(address) var instance = yo`<div class="instance run-instance border-dark ${css.instance} ${css.hidesub}" id="instance${address}" data-shared="universalDappUiInstance"></div>` const context = this.blockchain.context() var shortAddress = helper.shortenAddress(address) var title = yo` <div class="${css.title} alert alert-secondary"> <button data-id="universalDappUiTitleExpander" class="btn ${css.titleExpander}" onclick="${(e) => { toggleClass(e) }}"> <i class="fas fa-angle-right" aria-hidden="true"></i> </button> <div class="input-group ${css.nameNbuts}"> <div class="${css.titleText} input-group-prepend"> <span class="input-group-text ${css.spanTitleText}"> ${contractName} at ${shortAddress} (${context}) </span> </div> <div class="btn-group"> <button class="btn p-1 btn-secondary">${copyToClipboard(() => address)}</button> </div> </div> </div> ` var close = yo` <button class="${css.udappClose} mr-2 p-1 btn btn-secondary" data-id="universalDappUiUdappClose" onclick=${remove} title="Remove from the list" > <i class="${css.closeIcon} fas fa-times" aria-hidden="true"></i> </button>` title.querySelector('.btn-group').appendChild(close) var contractActionsWrapper = yo` <div class="${css.cActionsWrapper}" data-id="universalDappUiContractActionWrapper"> </div> ` function remove () { instance.remove() // @TODO perhaps add a callack here to warn the caller that the instance has been removed } function toggleClass (e) { $(instance).toggleClass(`${css.hidesub} bg-light`) // e.currentTarget.querySelector('i') e.currentTarget.querySelector('i').classList.toggle(`fa-angle-right`) e.currentTarget.querySelector('i').classList.toggle(`fa-angle-down`) } instance.appendChild(title) instance.appendChild(contractActionsWrapper) $.each(contractABI, (i, funABI) => { if (funABI.type !== 'function') { return } // @todo getData cannot be used with overloaded functions contractActionsWrapper.appendChild(this.getCallButton({ funABI: funABI, address: address, contractABI: contractABI, contractName: contractName })) }) const calldataInput = yo` <input id="deployAndRunLLTxCalldata" class="${css.calldataInput} form-control" title="The Calldata to send to fallback function of the contract."> ` const llIError = yo` <label id="deployAndRunLLTxError" class="text-danger my-2"></label> ` // constract LLInteractions elements const lowLevelInteracions = yo` <div class="d-flex flex-column"> <div class="d-flex flex-row justify-content-between mt-2"> <div class="py-2 border-top d-flex justify-content-start flex-grow-1"> Low level interactions </div> <a href="https://solidity.readthedocs.io/en/v0.6.2/contracts.html#receive-ether-function" title="check out docs for using 'receive'/'fallback'" target="_blank" > <i aria-hidden="true" class="fas fa-info my-2 mr-1"></i> </a> </div> <div class="d-flex flex-column align-items-start"> <label class="">CALLDATA</label> <div class="d-flex justify-content-end w-100 align-items-center"> ${calldataInput} <button id="deployAndRunLLTxSendTransaction" data-id="pluginManagerSettingsDeployAndRunLLTxSendTransaction" class="${css.instanceButton} p-0 w-50 btn border-warning text-warning" title="Send data to contract." onclick=${() => sendData()}>Transact</button> </div> </div> <div> ${llIError} </div> </div> ` function sendData () { function setLLIError (text) { llIError.innerText = text } setLLIError('') const fallback = txHelper.getFallbackInterface(contractABI) const receive = txHelper.getReceiveInterface(contractABI) const args = { funABI: fallback || receive, address: address, contractName: contractName, contractABI: contractABI } const amount = document.querySelector('#value').value if (amount !== '0') { // check for numeric and receive/fallback if (!helper.isNumeric(amount)) { return setLLIError('Value to send should be a number') } else if (!receive && !(fallback && fallback.stateMutability === 'payable')) { return setLLIError("In order to receive Ether transfer the contract should have either 'receive' or payable 'fallback' function") } } let calldata = calldataInput.value if (calldata) { if (calldata.length < 2 || calldata.length < 4 && helper.is0XPrefixed(calldata)) { return setLLIError('The calldata should be a valid hexadecimal value with size of at least one byte.') } else { if (helper.is0XPrefixed(calldata)) { calldata = calldata.substr(2, calldata.length) } if (!helper.isHexadecimal(calldata)) { return setLLIError('The calldata should be a valid hexadecimal value.') } } if (!fallback) { return setLLIError("'Fallback' function is not defined") } } if (!receive && !fallback) return setLLIError(`Both 'receive' and 'fallback' functions are not defined`) // we have to put the right function ABI: // if receive is defined and that there is no calldata => receive function is called // if fallback is defined => fallback function is called if (receive && !calldata) args.funABI = receive else if (fallback) args.funABI = fallback if (!args.funABI) return setLLIError(`Please define a 'Fallback' function to send calldata and a either 'Receive' or payable 'Fallback' to send ethers`) self.runTransaction(false, args, null, calldataInput.value, null) } contractActionsWrapper.appendChild(lowLevelInteracions) return instance } // TODO this is used by renderInstance when a new instance is displayed. // this returns a DOM element. UniversalDAppUI.prototype.getCallButton = function (args) { let self = this var outputOverride = yo`<div class=${css.value}></div>` // show return value const isConstant = args.funABI.constant !== undefined ? args.funABI.constant : false const lookupOnly = args.funABI.stateMutability === 'view' || args.funABI.stateMutability === 'pure' || isConstant const multiParamManager = new MultiParamManager( lookupOnly, args.funABI, (valArray, inputsValues) => self.runTransaction(lookupOnly, args, valArray, inputsValues, outputOverride), self.blockchain.getInputs(args.funABI) ) const contractActionsContainer = yo`<div class="${css.contractActionsContainer}" >${multiParamManager.render()}</div>` contractActionsContainer.appendChild(outputOverride) return contractActionsContainer } UniversalDAppUI.prototype.runTransaction = function (lookupOnly, args, valArr, inputsValues, outputOverride) { const functionName = args.funABI.type === 'function' ? args.funABI.name : `(${args.funABI.type})` const logMsg = `${lookupOnly ? 'call' : 'transact'} to ${args.contractName}.${functionName}` const callbacksInContext = txCallBacks.getCallBacksWithContext(this, this.blockchain) const outputCb = (returnValue) => { if (outputOverride) { const decoded = decodeResponseToTreeView(returnValue, args.funABI) outputOverride.innerHTML = '' outputOverride.appendChild(decoded) } } const params = args.funABI.type !== 'fallback' ? inputsValues : '' this.blockchain.runOrCallContractMethod( args.contractName, args.contractAbi, args.funABI, inputsValues, args.address, params, lookupOnly, logMsg, this.logCallback, outputCb, callbacksInContext.confirmationCb.bind(callbacksInContext), callbacksInContext.continueCb.bind(callbacksInContext), callbacksInContext.promptCb.bind(callbacksInContext)) } module.exports = UniversalDAppUI