parent
ad7edf216c
commit
93c62d9e92
@ -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`<div class=${css.udapp}></div>` |
||||||
|
} |
||||||
|
|
||||||
|
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`<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 |
||||||
|
} |
||||||
|
// @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`<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}`) |
||||||
|
} |
||||||
|
} |
||||||
|
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`<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 |
Loading…
Reference in new issue