diff --git a/assets/css/universal-dapp.css b/assets/css/universal-dapp.css index 1c68020991..693c730be8 100644 --- a/assets/css/universal-dapp.css +++ b/assets/css/universal-dapp.css @@ -215,7 +215,7 @@ padding: 0 0.4em; box-sizing: border-box; float: left; - word-wrap: break-word; + min-width: 100%; } #runTabView .contractProperty.hasArgs input { diff --git a/src/app/execution/txListener.js b/src/app/execution/txListener.js index 104300ac2d..49ad15f861 100644 --- a/src/app/execution/txListener.js +++ b/src/app/execution/txListener.js @@ -249,7 +249,12 @@ class TxListener { for (var i = 0; i < abi.inputs.length; i++) { inputTypes.push(abi.inputs[i].type) } - return ethJSABI.rawDecode(inputTypes, data) + var decoded = ethJSABI.rawDecode(inputTypes, data) + var ret = {} + for (var k in abi.inputs) { + ret[abi.inputs[k].type + ' ' + abi.inputs[k].name] = decoded[k] + } + return ret } } diff --git a/src/app/execution/txLogger.js b/src/app/execution/txLogger.js index bc5b0d0468..e847b10d7d 100644 --- a/src/app/execution/txLogger.js +++ b/src/app/execution/txLogger.js @@ -1,12 +1,64 @@ 'use strict' var yo = require('yo-yo') + +// -------------- styling ---------------------- +var csjs = require('csjs-inject') var remix = require('ethereum-remix') +var styleGuide = remix.ui.styleGuide +var styles = styleGuide() + var EventManager = remix.lib.EventManager var helper = require('../../lib/helper') var ethJSUtil = require('ethereumjs-util') var BN = ethJSUtil.BN var executionContext = require('../../execution-context') +var css = csjs` + .log { + display: flex; + align-items: baseline; + justify-content: space-between; + } + .tx { + color: ${styles.colors.violet}; + width: 45%; + } + .txTable, .tr, .td { + border-collapse: collapse; + font-size: 10px; + color: ${styles.colors.grey}; + border: 1px dashed ${styles.colors.black}; + } + #txTable { + margin-top: 1%; + margin-bottom: 5%; + align-self: center; + } + .tr, .td { + padding: 4px; + } + .tableTitle { + width: 25%; + } + .buttons { + display: flex; + } + .debug, .details { + ${styles.button} + min-height: 18px; + max-height: 18px; + width: 45px; + min-width: 45px; + font-size: 10px; + margin-left: 5px; + } + .debug { + background-color: ${styles.colors.lightOrange}; + } + .details { + background-color: ${styles.colors.lightGrey}; + } +` /** * This just export a function that register to `newTransaction` and forward them to the logger. * Emit debugRequested @@ -59,47 +111,131 @@ function log (self, tx, api) { } function renderKnownTransaction (self, data) { - var to = data.tx.to - if (to) to = helper.shortenAddress(data.tx.to) + var from = data.tx.from + var to = data.resolvedData.contractName + '.' + data.resolvedData.fn function debug () { self.event.trigger('debugRequested', [data.tx.hash]) } - function detail () { - // @TODO here should open a modal containing some info (e.g input params, logs, ...) + var tx = yo` + +
+ ${context(self, {from, to, data})} +
+ + +
+
+
+ ` + + var table + function txDetails () { + if (table && table.parentNode) { + tx.removeChild(table) + } else { + table = createTable({ + contractAddress: data.tx.contractAddress, + data: data.tx, + from, + to, + gas: data.tx.gas, + hash: data.tx.hash, + input: data.tx.input, + 'decoded input': data.resolvedData && data.resolvedData.params ? JSON.stringify(value(data.resolvedData.params), null, '\t') : ' - ', + logs: JSON.stringify(data.logs, null, '\t') || '0', + val: data.tx.value + }) + tx.appendChild(table) + } } - return yo`${context(self, data.tx)}: from:${helper.shortenAddress(data.tx.from)}, to:${to}, ${data.resolvedData.contractName}.${data.resolvedData.fn}, value:${value(data.tx.value)} wei, data:${helper.shortenHexData(data.tx.input)}, ${data.logs.length} logs, hash:${helper.shortenHexData((data.tx.hash))}, ` + + return tx } function renderUnknownTransaction (self, data) { + var from = data.tx.from var to = data.tx.to - if (to) to = helper.shortenAddress(data.tx.to) function debug () { self.event.trigger('debugRequested', [data.tx.hash]) } - function detail () { - // @TODO here should open a modal containing some info (e.g input params, logs, ...) + var tx = yo` + +
+ ${context(self, {from, to, data})} +
+ + +
+
+
+ ` + var table + function txDetails () { + if (table && table.parentNode) { + tx.removeChild(table) + } else { + table = createTable({ + data: data.tx, + from, + to, + val: data.tx.value, + input: data.tx.input, + hash: data.tx.hash, + gas: data.tx.gas, + logs: JSON.stringify(data.logs) || '0' + }) + tx.appendChild(table) + } } - return yo`${context(self, data.tx)}: from:${helper.shortenAddress(data.tx.from)}, to:${to}, value:${value(data.tx.value)} wei, data:${helper.shortenHexData((data.tx.input))}, hash:${helper.shortenHexData((data.tx.hash))}, ` + return tx } function renderEmptyBlock (self, data) { return yo`block ${data.block.number} - O transactions` } -function context (self, tx) { +function context (self, opts) { + var data = opts.data || '' + var from = opts.from ? helper.shortenHexData(opts.from) : '' + var to = opts.to + if (data.tx.to) to = to + ' ' + helper.shortenHexData(data.tx.to) + var val = data.tx.value + var hash = data.tx.hash ? helper.shortenHexData(data.tx.hash) : '' + var input = data.tx.input ? helper.shortenHexData(data.tx.input) : '' + var logs = data.logs ? data.logs.length : 0 + var block = data.tx.blockNumber || '' + var i = data.tx.transactionIndex if (executionContext.getProvider() === 'vm') { - return yo`(vm)` + return yo`[vm] from:${from}, to:${to}, value:${value(val)} wei, data:${input}, ${logs} logs, hash:${hash}` + } else if (executionContext.getProvider() !== 'vm' && data.resolvedData) { + return yo`[block:${block} txIndex:${i}] from:${from}, to:${to}, value:${value(val)} wei` } else { - return yo`block:${tx.blockNumber}, txIndex:${tx.transactionIndex}` + to = helper.shortenHexData(to) + hash = helper.shortenHexData(data.tx.blockHash) + return yo`[block:${block} txIndex:${i}] from:${from}, to:${to}, value:${value(val)} wei` } } function value (v) { try { - if (v.indexOf && v.indexOf('0x') === 0) { + if (v instanceof Array) { + var ret = [] + for (var k in v) { + ret.push(value(v[k])) + } + return ret + } else if (BN.isBN(v)) { + return v.toString(10) + } else if (v.indexOf && v.indexOf('0x') === 0) { return (new BN(v.replace('0x', ''), 16)).toString(10) + } else if (typeof v === 'object') { + var retObject = {} + for (var i in v) { + retObject[i] = value(v[i]) + } + return retObject } else { - return v.toString(10) + return v } } catch (e) { console.log(e) @@ -108,3 +244,92 @@ function value (v) { } module.exports = TxLogger + +// helpers + +function createTable (opts) { + var table = yo`
` + + var contractAddress = yo` + + contractAddress + ${opts.contractAddress} + + ` + if (opts.contractAddress) table.appendChild(contractAddress) + + var from = yo` + + from + ${opts.from} + + ` + if (opts.from) table.appendChild(from) + + var toHash + var data = opts.data // opts.data = data.tx + if (data.to) { + toHash = opts.to + ' ' + data.to + } else { + toHash = opts.to + } + var to = yo` + + to + ${toHash} + + ` + if (opts.to) table.appendChild(to) + + var gas = yo` + + gas + ${opts.gas} + + ` + if (opts.gas) table.appendChild(gas) + + var hash = yo` + + hash + ${opts.hash} + + ` + if (opts.hash) table.appendChild(hash) + + var input = yo` + + input + ${opts.input} + + ` + if (opts.input) table.appendChild(input) + + if (opts['decoded input']) { + var inputDecoded = yo` + + decoded input + ${opts['decoded input']} + ` + table.appendChild(inputDecoded) + } + + var logs = yo` + + logs + ${opts.logs || '0'} + + ` + if (opts.logs) table.appendChild(logs) + + var val = value(opts.val) + val = yo` + + value + ${val} wei + + ` + if (opts.val) table.appendChild(val) + + return table +} diff --git a/src/app/tabs/run-tab.js b/src/app/tabs/run-tab.js index 0cf97bb140..1e8e82944b 100644 --- a/src/app/tabs/run-tab.js +++ b/src/app/tabs/run-tab.js @@ -300,7 +300,7 @@ function contractDropdown (appAPI, appEvents, instanceContainer) { var args = createButtonInput.value txFormat.buildData(contract, contracts, true, constructor, args, appAPI.udapp(), (error, data) => { if (!error) { - appAPI.logMessage('transaction added ...') + appAPI.logMessage('Transaction added ...') txExecution.createContract(data, appAPI.udapp(), (error, txResult) => { if (!error) { var isVM = executionContext.isVM() diff --git a/src/lib/helper.js b/src/lib/helper.js index 917a547b3a..5676c06040 100644 --- a/src/lib/helper.js +++ b/src/lib/helper.js @@ -4,6 +4,7 @@ module.exports = { return address.slice(0, 5) + '...' + address.slice(len - 5, len) + (etherBalance ? ' (' + etherBalance.toString() + ' ether)' : '') }, shortenHexData: function (data) { + if (!data) return '' if (data.length < 5) return data var len = data.length return data.slice(0, 5) + '...' + data.slice(len - 5, len) diff --git a/test-browser/tests/compiling.js b/test-browser/tests/compiling.js index 81a7654209..696f12cd45 100644 --- a/test-browser/tests/compiling.js +++ b/test-browser/tests/compiling.js @@ -33,7 +33,7 @@ function runTests (browser) { .waitForElementPresent('.instance button[title="f - transact (not payable)"]') .click('.instance button[title="f - transact (not payable)"]') .waitForElementPresent('#editor-container div[class^="terminal"] span[id="tx0xa178c603400a184ce5fedbcfab392d9b77822f6ffa7facdec693aded214523bc"]') - .assert.containsText('#editor-container div[class^="terminal"] span[id="tx0xa178c603400a184ce5fedbcfab392d9b77822f6ffa7facdec693aded214523bc"]', '(vm): from:0xca3...a733c, to:0x692...77b3a, browser/Untitled.sol:TestContract.f(), value:0 wei, data:0x261...21ff0, 0 logs, hash:0xa17...523bc,DetailsDebug') + .assert.containsText('#editor-container div[class^="terminal"] span[id="tx0xa178c603400a184ce5fedbcfab392d9b77822f6ffa7facdec693aded214523bc"]', '[vm] from:0xca3...a733c, to:browser/Untitled.sol:TestContract.f() 0x692...77b3a, value:0 wei, data:0x261...21ff0, 0 logs, hash:0xa17...523bc') .end() /* @TODO: need to check now the return value of the function