|
|
|
@ -1,13 +1,9 @@ |
|
|
|
|
import React, { useState, useEffect, useReducer, useRef, SyntheticEvent, MouseEvent } from 'react' // eslint-disable-line
|
|
|
|
|
import { useKeyPress } from './custom-hooks/useKeyPress' // eslint-disable-line
|
|
|
|
|
import { useWindowResize } from 'beautiful-react-hooks' |
|
|
|
|
import { registerCommandAction, registerLogScriptRunnerAction, registerInfoScriptRunnerAction, registerErrorScriptRunnerAction, registerWarnScriptRunnerAction, listenOnNetworkAction, initListeningOnNetwork } from './actions/terminalAction' |
|
|
|
|
import { initialState, registerCommandReducer, addCommandHistoryReducer, registerScriptRunnerReducer, remixWelcomeTextReducer } from './reducers/terminalReducer' |
|
|
|
|
import { remixWelcome } from './reducers/remixWelcom' // eslint-disable-line
|
|
|
|
|
import { initialState, registerCommandReducer, addCommandHistoryReducer, registerScriptRunnerReducer } from './reducers/terminalReducer' |
|
|
|
|
import { getKeyOf, getValueOf, Objectfilter, matched } from './utils/utils' |
|
|
|
|
import {allCommands, allPrograms} from './commands' // eslint-disable-line
|
|
|
|
|
import { CopyToClipboard } from '@remix-ui/clipboard' // eslint-disable-line
|
|
|
|
|
import { ModalDialog } from '@remix-ui/modal-dialog' // eslint-disable-line
|
|
|
|
|
import TerminalWelcomeMessage from './terminalWelcome' // eslint-disable-line
|
|
|
|
|
|
|
|
|
|
import './remix-ui-terminal.css' |
|
|
|
@ -22,31 +18,6 @@ import { RemixUiTerminalProps } from './types/terminalTypes' |
|
|
|
|
import { wrapScript } from './utils/wrapScript' |
|
|
|
|
|
|
|
|
|
/* eslint-disable-next-line */ |
|
|
|
|
export interface RemixUiTerminalProps { |
|
|
|
|
propterties: any |
|
|
|
|
event: any |
|
|
|
|
autoCompletePopupEvent: any |
|
|
|
|
autoCompletePopup: any |
|
|
|
|
blockchain: any |
|
|
|
|
api: any |
|
|
|
|
options: any |
|
|
|
|
data: any |
|
|
|
|
cmdInterpreter: any |
|
|
|
|
command: any |
|
|
|
|
version: any |
|
|
|
|
config: any |
|
|
|
|
thisState: any |
|
|
|
|
vm: any |
|
|
|
|
commandHelp: any, |
|
|
|
|
_deps: any, |
|
|
|
|
fileImport: any, |
|
|
|
|
gistHandler: any, |
|
|
|
|
sourceHighlighter: any, |
|
|
|
|
registry: any, |
|
|
|
|
commands: any, |
|
|
|
|
txListener: any, |
|
|
|
|
eventsDecoder: any |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
export interface ClipboardEvent<T = Element> extends SyntheticEvent<T, any> { |
|
|
|
|
clipboardData: DataTransfer; |
|
|
|
@ -177,55 +148,6 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => { |
|
|
|
|
if (cb) cb() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const domTerminalFeatures = () => { |
|
|
|
|
return { |
|
|
|
|
remix: props.cmdInterpreter |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function exeCurrent (cb) { |
|
|
|
|
return execute(undefined, cb) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function execute (file, cb) { |
|
|
|
|
console.log('called execute scriptRunner') |
|
|
|
|
function _execute (content, cb) { |
|
|
|
|
if (!content) { |
|
|
|
|
// toolTip('no content to execute')
|
|
|
|
|
if (cb) cb() |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
newstate.commands.script(content) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (typeof file === 'undefined') { |
|
|
|
|
var content = props._deps.editor.currentContent() |
|
|
|
|
_execute(content, cb) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var provider = props._deps.fileManager.fileProviderOf(file) |
|
|
|
|
|
|
|
|
|
if (!provider) { |
|
|
|
|
// toolTip(`provider for path ${file} not found`)
|
|
|
|
|
if (cb) cb() |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
provider.get(file, (error, content) => { |
|
|
|
|
if (error) { |
|
|
|
|
// toolTip(error)
|
|
|
|
|
// TODO: pop up
|
|
|
|
|
if (cb) cb() |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
_execute(content, cb) |
|
|
|
|
}) |
|
|
|
|
return '' |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const _shell = async (script, scopedCommands, done) => { // default shell
|
|
|
|
|
if (script.indexOf('remix:') === 0) { |
|
|
|
|
return done(null, 'This type of command has been deprecated and is not functionning anymore. Please run remix.help() to list available commands.') |
|
|
|
@ -255,61 +177,6 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// events
|
|
|
|
|
useEffect(() => { |
|
|
|
|
registerRemixWelcomeTextAction(remixWelcome, welcomTextDispath) |
|
|
|
|
registerLogScriptRunnerAction(props.thisState, 'log', newstate.commands, scriptRunnerDispatch) |
|
|
|
|
registerInfoScriptRunnerAction(props.thisState, 'info', newstate.commands, scriptRunnerDispatch) |
|
|
|
|
registerWarnScriptRunnerAction(props.thisState, 'warn', newstate.commands, scriptRunnerDispatch) |
|
|
|
|
registerErrorScriptRunnerAction(props.thisState, 'error', newstate.commands, scriptRunnerDispatch) |
|
|
|
|
registerCommandAction('html', _blocksRenderer('html'), { activate: true }, dispatch) |
|
|
|
|
registerCommandAction('log', _blocksRenderer('log'), { activate: true }, dispatch) |
|
|
|
|
registerCommandAction('info', _blocksRenderer('info'), { activate: true }, dispatch) |
|
|
|
|
registerCommandAction('warn', _blocksRenderer('warn'), { activate: true }, dispatch) |
|
|
|
|
registerCommandAction('error', _blocksRenderer('error'), { activate: true }, dispatch) |
|
|
|
|
registerCommandAction('script', function execute (args, scopedCommands, append) { |
|
|
|
|
var script = String(args[0]) |
|
|
|
|
_shell(script, scopedCommands, function (error, output) { |
|
|
|
|
console.log({ error }, 'registerCommand scrpt') |
|
|
|
|
console.log({ output }, 'registerCommand scrpt 2') |
|
|
|
|
if (error) scriptRunnerDispatch({ type: 'error', payload: { message: error } }) |
|
|
|
|
else if (output) scriptRunnerDispatch({ type: 'error', payload: { message: output } }) |
|
|
|
|
}) |
|
|
|
|
}, { activate: true }, dispatch) |
|
|
|
|
filterFnAction('log', basicFilter, filterDispatch) |
|
|
|
|
filterFnAction('info', basicFilter, filterDispatch) |
|
|
|
|
filterFnAction('warn', basicFilter, filterDispatch) |
|
|
|
|
filterFnAction('error', basicFilter, filterDispatch) |
|
|
|
|
filterFnAction('script', basicFilter, filterDispatch) |
|
|
|
|
registerLogScriptRunnerAction(props.thisState, 'log', newstate.commands, scriptRunnerDispatch) |
|
|
|
|
registerInfoScriptRunnerAction(props.thisState, 'info', newstate.commands, scriptRunnerDispatch) |
|
|
|
|
registerWarnScriptRunnerAction(props.thisState, 'warn', newstate.commands, scriptRunnerDispatch) |
|
|
|
|
registerErrorScriptRunnerAction(props.thisState, 'error', newstate.commands, scriptRunnerDispatch) |
|
|
|
|
// console.log({ htmlresullt }, { logresult })
|
|
|
|
|
// dispatch({ type: 'html', payload: { commands: htmlresullt.commands } })
|
|
|
|
|
// dispatch({ type: 'log', payload: { _commands: logresult._commands } })
|
|
|
|
|
// registerCommand('log', _blocksRenderer('log'), { activate: true })
|
|
|
|
|
}, [newstate.journalBlocks, props.thisState.autoCompletePopup, autoCompletState.text]) |
|
|
|
|
|
|
|
|
|
// handle events
|
|
|
|
|
const handlePaste = (event: ClipboardEvent<HTMLInputElement>) => { |
|
|
|
|
// Do something
|
|
|
|
|
const selection = window.getSelection() |
|
|
|
|
if (!selection.rangeCount) return false |
|
|
|
|
event.preventDefault() |
|
|
|
|
event.stopPropagation() |
|
|
|
|
var clipboard = (event.clipboardData) // || window.clipboardData
|
|
|
|
|
var text = clipboard.getData('text/plain') |
|
|
|
|
text = text.replace(/[^\x20-\xFF]/gi, '') // remove non-UTF-8 characters
|
|
|
|
|
var temp = document.createElement('div') |
|
|
|
|
temp.innerHTML = text |
|
|
|
|
var textnode = document.createTextNode(temp.textContent) |
|
|
|
|
selection.getRangeAt(0).insertNode(textnode) |
|
|
|
|
selection.empty() |
|
|
|
|
// self.scroll2bottom()
|
|
|
|
|
// placeCaretAtEnd(event.currentTarget)
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const handleMinimizeTerminal = (event) => { |
|
|
|
|
event.preventDefault() |
|
|
|
|
event.stopPropagation() |
|
|
|
@ -395,8 +262,6 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => { |
|
|
|
|
} else { |
|
|
|
|
setCmdTemp(inputEl.current.innerText) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return commands[name] |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* start of mouse events */ |
|
|
|
@ -582,460 +447,6 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => { |
|
|
|
|
setAutoCompleteState(prevState => ({ ...prevState, activeSuggestion: 0, showSuggestions: false })) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* block contents that gets rendered from scriptRunner */ |
|
|
|
|
|
|
|
|
|
const _blocksRenderer = (mode) => { |
|
|
|
|
if (mode === 'html') { |
|
|
|
|
return function logger (args) { |
|
|
|
|
console.log({ args }) |
|
|
|
|
if (args.length) { |
|
|
|
|
return args[0] |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
mode = { |
|
|
|
|
log: 'text-log', |
|
|
|
|
info: 'text-info', |
|
|
|
|
warn: 'text-warning', |
|
|
|
|
error: 'text-danger' |
|
|
|
|
}[mode] // defaults
|
|
|
|
|
|
|
|
|
|
if (mode) { |
|
|
|
|
const filterUndefined = (el) => el !== undefined && el !== null |
|
|
|
|
return function logger (args) { |
|
|
|
|
var types = args.filter(filterUndefined).map(type => type) |
|
|
|
|
var values = javascriptserialize.apply(null, args.filter(filterUndefined)).map(function (val, idx) { |
|
|
|
|
if (typeof args[idx] === 'string') { |
|
|
|
|
const el = document.createElement('div') |
|
|
|
|
el.innerHTML = args[idx].replace(/(\r\n|\n|\r)/gm, '<br>') |
|
|
|
|
val = el.children.length === 0 ? el.firstChild : el |
|
|
|
|
} |
|
|
|
|
if (types[idx] === 'element') val = jsbeautify.html(val) |
|
|
|
|
return val |
|
|
|
|
}) |
|
|
|
|
if (values.length) { |
|
|
|
|
console.log({ values }) |
|
|
|
|
return `<span class="${mode}" >${values}</span>` |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
throw new Error('mode is not supported') |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function basicFilter (value, query) { try { return value.indexOf(query) !== -1 } catch (e) { return false } } |
|
|
|
|
|
|
|
|
|
const registerCommand = (name, command, opts) => { |
|
|
|
|
// setState((prevState) => ({ ...prevState, _commands[name]: command }))
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* end of block content that gets rendered from script Runner */ |
|
|
|
|
|
|
|
|
|
const handleClearConsole = () => { |
|
|
|
|
dispatch({ type: 'clearconsole', payload: [] }) |
|
|
|
|
inputEl.current.focus() |
|
|
|
|
} |
|
|
|
|
/* start of autoComplete */ |
|
|
|
|
|
|
|
|
|
const listenOnNetwork = (event: any) => { |
|
|
|
|
const isListening = event.target.checked |
|
|
|
|
setIsListeningOnNetwork(isListening) |
|
|
|
|
listenOnNetworkAction(props, isListening) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const onChange = (event: any) => { |
|
|
|
|
event.preventDefault() |
|
|
|
|
const inputString = event.target.value |
|
|
|
|
if (matched(allPrograms, inputString) || inputString.includes('.')) { |
|
|
|
|
setAutoCompleteState(prevState => ({ ...prevState, showSuggestions: true, userInput: inputString })) |
|
|
|
|
const textList = inputString.split('.') |
|
|
|
|
if (textList.length === 1) { |
|
|
|
|
setAutoCompleteState(prevState => ({ ...prevState, data: { _options: [] } })) |
|
|
|
|
const result = Objectfilter(allPrograms, autoCompletState.userInput) |
|
|
|
|
setAutoCompleteState(prevState => ({ ...prevState, data: { _options: result } })) |
|
|
|
|
} else { |
|
|
|
|
setAutoCompleteState(prevState => ({ ...prevState, data: { _options: [] } })) |
|
|
|
|
const result = Objectfilter(allCommands, autoCompletState.userInput) |
|
|
|
|
setAutoCompleteState(prevState => ({ ...prevState, data: { _options: result } })) |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
setAutoCompleteState(prevState => ({ ...prevState, showSuggestions: false, userInput: inputString })) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const handleSelect = (event) => { |
|
|
|
|
const suggestionCount = autoCompletState.activeSuggestion |
|
|
|
|
if (event.keyCode === 38) { |
|
|
|
|
if (autoCompletState.activeSuggestion === 0) { |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
setAutoCompleteState(prevState => ({ ...prevState, activeSuggestion: suggestionCount - 1 })) |
|
|
|
|
} else if (event.keyCode === 40) { |
|
|
|
|
if (autoCompletState.activeSuggestion - 1 === autoCompletState.data._options.length) { |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
setAutoCompleteState(prevState => ({ ...prevState, activeSuggestion: suggestionCount + 1 })) |
|
|
|
|
} |
|
|
|
|
// props.thisState.event.trigger('handleSelect', [text])
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const checkTxStatus = (tx, type) => { |
|
|
|
|
if (tx.status === '0x1' || tx.status === true) { |
|
|
|
|
return (<i className='txStatus succeeded fas fa-check-circle'></i>) |
|
|
|
|
} |
|
|
|
|
if (type === 'call' || type === 'unknownCall' || type === 'unknown') { |
|
|
|
|
return (<i className='txStatus call'>call</i>) |
|
|
|
|
} else if (tx.status === '0x0' || tx.status === false) { |
|
|
|
|
return (<i className='txStatus failed fas fa-times-circle'></i>) |
|
|
|
|
} else { |
|
|
|
|
return (<i className='txStatus notavailable fas fa-circle-thin' title='Status not available' ></i>) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const context = (opts, blockchain) => { |
|
|
|
|
const data = opts.tx || '' |
|
|
|
|
const from = opts.from ? helper.shortenHexData(opts.from) : '' |
|
|
|
|
let to = opts.to |
|
|
|
|
if (data.to) to = to + ' ' + helper.shortenHexData(data.to) |
|
|
|
|
const val = data.value |
|
|
|
|
let hash = data.hash ? helper.shortenHexData(data.hash) : '' |
|
|
|
|
const input = data.input ? helper.shortenHexData(data.input) : '' |
|
|
|
|
const logs = data.logs && data.logs.decoded && data.logs.decoded.length ? data.logs.decoded.length : 0 |
|
|
|
|
const block = data.receipt ? data.receipt.blockNumber : data.blockNumber || '' |
|
|
|
|
const i = data ? data.transactionIndex : data.transactionIndex |
|
|
|
|
const value = val ? typeConversion.toInt(val) : 0 |
|
|
|
|
|
|
|
|
|
if (blockchain.getProvider() === 'vm') { |
|
|
|
|
return ( |
|
|
|
|
<div> |
|
|
|
|
<span className='txLog_7Xiho'> |
|
|
|
|
<span className='tx'>[{vm}]</span> |
|
|
|
|
<div className='txItem'><span className='txItemTitle'>from:</span> {from}</div> |
|
|
|
|
<div className='txItem'><span className='txItemTitle'>to:</span> {to}</div> |
|
|
|
|
<div className='txItem'><span className='txItemTitle'>value:</span> {value} wei</div> |
|
|
|
|
<div className='txItem'><span className='txItemTitle'>data:</span> {input}</div> |
|
|
|
|
<div className='txItem'><span className='txItemTitle'>logs:</span> {logs}</div> |
|
|
|
|
<div className='txItem'><span className='txItemTitle'>hash:</span> {hash}</div> |
|
|
|
|
</span> |
|
|
|
|
</div>) |
|
|
|
|
} else if (blockchain.getProvider() !== 'vm' && data.resolvedData) { |
|
|
|
|
return ( |
|
|
|
|
<div> |
|
|
|
|
<span className='txLog_7Xiho'> |
|
|
|
|
<span className='tx'>[block:${block} txIndex:${i}]</span> |
|
|
|
|
<div className='txItem'><span className='txItemTitle'>from:</span> {from}</div> |
|
|
|
|
<div className='txItem'><span className='txItemTitle'>to:</span> {to}</div> |
|
|
|
|
<div className='txItem'><span className='txItemTitle'>value:</span> {value} wei</div> |
|
|
|
|
<div className='txItem'><span className='txItemTitle'>data:</span> {input}</div> |
|
|
|
|
<div className='txItem'><span className='txItemTitle'>logs:</span> {logs}</div> |
|
|
|
|
<div className='txItem'><span className='txItemTitle'>hash:</span> {hash}</div> |
|
|
|
|
</span> |
|
|
|
|
</div>) |
|
|
|
|
} else { |
|
|
|
|
to = helper.shortenHexData(to) |
|
|
|
|
hash = helper.shortenHexData(data.blockHash) |
|
|
|
|
return ( |
|
|
|
|
<div> |
|
|
|
|
<span className='txLog'> |
|
|
|
|
<span className='tx'>[block:${block} txIndex:${i}]</span> |
|
|
|
|
<div className='txItem'><span className='txItemTitle'>from:</span> {from}</div> |
|
|
|
|
<div className='txItem'><span className='txItemTitle'>to:</span> {to}</div> |
|
|
|
|
<div className='txItem'><span className='txItemTitle'>value:</span> {value} wei</div> |
|
|
|
|
</span> |
|
|
|
|
</div>) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const txDetails = (event, tx, obj) => { |
|
|
|
|
if (showTableDetails === null) { |
|
|
|
|
setShowTableDetails(true) |
|
|
|
|
} else { |
|
|
|
|
setShowTableDetails(null) |
|
|
|
|
} |
|
|
|
|
// if (showTableDetails.length === 0) {
|
|
|
|
|
// setShowTableDetails([{ hash: tx.hash, show: true }])
|
|
|
|
|
// }
|
|
|
|
|
// const id = showTableDetails.filter(x => x.hash !== tx.hash)
|
|
|
|
|
// if ((showTableDetails.length !== 0) && (id[0] === tx.hash)) {
|
|
|
|
|
// setShowTableDetails(currentState => ([...currentState, { hash: tx.hash, show: false }]))
|
|
|
|
|
// }
|
|
|
|
|
// console.log((showTableDetails.length !== 0) && (id[0] === tx.hash))
|
|
|
|
|
// console.log({ showTableDetails }, ' clicked button')
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const showTable = (opts) => { |
|
|
|
|
let msg = '' |
|
|
|
|
let toHash |
|
|
|
|
const data = opts.data // opts.data = data.tx
|
|
|
|
|
if (data.to) { |
|
|
|
|
toHash = opts.to + ' ' + data.to |
|
|
|
|
} else { |
|
|
|
|
toHash = opts.to |
|
|
|
|
} |
|
|
|
|
let callWarning = '' |
|
|
|
|
if (opts.isCall) { |
|
|
|
|
callWarning = '(Cost only applies when called by a contract)' |
|
|
|
|
} |
|
|
|
|
if (!opts.isCall) { |
|
|
|
|
if (opts.status !== undefined && opts.status !== null) { |
|
|
|
|
if (opts.status === '0x0' || opts.status === false) { |
|
|
|
|
msg = ' Transaction mined but execution failed' |
|
|
|
|
} else if (opts.status === '0x1' || opts.status === true) { |
|
|
|
|
msg = ' Transaction mined and execution succeed' |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
msg = ' Status not available at the moment' |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let stringified = ' - ' |
|
|
|
|
if (opts.logs && opts.logs.decoded) { |
|
|
|
|
stringified = typeConversion.stringify(opts.logs.decoded) |
|
|
|
|
} |
|
|
|
|
const val = opts.val != null ? typeConversion.toInt(opts.val) : 0 |
|
|
|
|
return ( |
|
|
|
|
<table className='txTable' id='txTable' data-id={`txLoggerTable${opts.hash}`}> |
|
|
|
|
<tr className='tr'> |
|
|
|
|
<td className='td' data-shared={`key_${opts.hash}`}> status </td> |
|
|
|
|
<td className='td' data-id={`txLoggerTableStatus${opts.hash}`} data-shared={`pair_${opts.hash}`}>{opts.status}{msg}</td> |
|
|
|
|
</tr> |
|
|
|
|
<tr className='tr'> |
|
|
|
|
<td className='td' data-shared={`key_${opts.hash}`}> transaction hash </td> |
|
|
|
|
<td className='td' data-id={`txLoggerTableHash${opts.hash}`} data-shared={`pair_${opts.hash}`}>{opts.hash} |
|
|
|
|
<CopyToClipboard content={opts.hash}/> |
|
|
|
|
</td> |
|
|
|
|
</tr> |
|
|
|
|
{ |
|
|
|
|
opts.contractAddress && ( |
|
|
|
|
<tr className='tr'> |
|
|
|
|
<td className='td' data-shared={`key_${opts.hash}`}> contract address </td> |
|
|
|
|
<td className='td' data-id={`txLoggerTableContractAddress${opts.hash}`} data-shared={`pair_${opts.hash}`}>{opts.contractAddress} |
|
|
|
|
<CopyToClipboard content={opts.contractAddress}/> |
|
|
|
|
</td> |
|
|
|
|
</tr> |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
{ |
|
|
|
|
opts.from && ( |
|
|
|
|
<tr className='tr'> |
|
|
|
|
<td className='td tableTitle' data-shared={`key_${opts.hash}`}> from </td> |
|
|
|
|
<td className='td' data-id={`txLoggerTableFrom${opts.hash}`} data-shared={`pair_${opts.hash}`}>{opts.from} |
|
|
|
|
<CopyToClipboard content={opts.from}/> |
|
|
|
|
</td> |
|
|
|
|
</tr> |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
{ |
|
|
|
|
opts.to && ( |
|
|
|
|
<tr className='tr'> |
|
|
|
|
<td className='td' data-shared={`key_${opts.hash}`}> to </td> |
|
|
|
|
<td className='td' data-id={`txLoggerTableTo${opts.hash}`} data-shared={`pair_${opts.hash}`}>{toHash} |
|
|
|
|
<CopyToClipboard content={data.to ? data.to : toHash}/> |
|
|
|
|
</td> |
|
|
|
|
</tr> |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
{ |
|
|
|
|
opts.gas && ( |
|
|
|
|
<tr className='tr'> |
|
|
|
|
<td className='td' data-shared={`key_${opts.hash}`}> gas </td> |
|
|
|
|
<td className='td' data-id={`txLoggerTableGas${opts.hash}`} data-shared={`pair_${opts.hash}`}>{opts.gas} gas |
|
|
|
|
<CopyToClipboard content={opts.gas}/> |
|
|
|
|
</td> |
|
|
|
|
</tr> |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
{ |
|
|
|
|
opts.transactionCost && ( |
|
|
|
|
<tr className='tr'> |
|
|
|
|
<td className='td' data-shared={`key_${opts.hash}`}> transaction cost </td> |
|
|
|
|
<td className='td' data-id={`txLoggerTableTransactionCost${opts.hash}`} data-shared={`pair_${opts.hash}`}>{opts.transactionCost} gas {callWarning} |
|
|
|
|
<CopyToClipboard content={opts.transactionCost}/> |
|
|
|
|
</td> |
|
|
|
|
</tr> |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
{ |
|
|
|
|
opts.executionCost && ( |
|
|
|
|
<tr className='tr'> |
|
|
|
|
<td className='td' data-shared={`key_${opts.hash}`}> execution cost </td> |
|
|
|
|
<td className='td' data-id={`txLoggerTableExecutionHash${opts.hash}`} data-shared={`pair_${opts.hash}`}>{opts.executionCost} gas {callWarning} |
|
|
|
|
<CopyToClipboard content={opts.executionCost}/> |
|
|
|
|
</td> |
|
|
|
|
</tr> |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
{opts.hash && ( |
|
|
|
|
<tr className='tr'> |
|
|
|
|
<td className='td' data-shared={`key_${opts.hash}`}> hash </td> |
|
|
|
|
<td className='td' data-id={`txLoggerTableHash${opts.hash}`} data-shared={`pair_${opts.hash}`}>{opts.hash} |
|
|
|
|
<CopyToClipboard content={opts.hash}/> |
|
|
|
|
</td> |
|
|
|
|
</tr> |
|
|
|
|
)} |
|
|
|
|
{opts.input && ( |
|
|
|
|
<tr className='tr'> |
|
|
|
|
<td className='td' data-shared={`key_${opts.hash}`}> input </td> |
|
|
|
|
<td className='td' data-id={`txLoggerTableHash${opts.hash}`} data-shared={`pair_${opts.hash}`}>{helper.shortenHexData(opts.input)} |
|
|
|
|
<CopyToClipboard content={opts.input}/> |
|
|
|
|
</td> |
|
|
|
|
</tr> |
|
|
|
|
)} |
|
|
|
|
{opts['decoded input'] && ( |
|
|
|
|
<tr className='tr'> |
|
|
|
|
<td className='td' data-shared={`key_${opts.hash}`}> decode input </td> |
|
|
|
|
<td className='td' data-id={`txLoggerTableHash${opts.hash}`} data-shared={`pair_${opts.hash}`}>{opts['decoded input']} |
|
|
|
|
<CopyToClipboard content={opts['decoded input']}/> |
|
|
|
|
</td> |
|
|
|
|
</tr> |
|
|
|
|
)} |
|
|
|
|
{opts['decoded output'] && ( |
|
|
|
|
<tr className='tr'> |
|
|
|
|
<td className='td' data-shared={`key_${opts.hash}`}> decode output </td> |
|
|
|
|
<td className='td' data-id={`txLoggerTableHash${opts.hash}`} data-shared={`pair_${opts.hash}`}>{opts['decoded output']} |
|
|
|
|
<CopyToClipboard content={opts['decoded output']}/> |
|
|
|
|
</td> |
|
|
|
|
</tr> |
|
|
|
|
)} |
|
|
|
|
{opts.logs && ( |
|
|
|
|
<tr className='tr'> |
|
|
|
|
<td className='td' data-shared={`key_${opts.hash}`}> logs </td> |
|
|
|
|
<td className='td' data-id={`txLoggerTableHash${opts.hash}`} data-shared={`pair_${opts.hash}`}> |
|
|
|
|
{JSON.stringify(stringified, null, '\t')} |
|
|
|
|
<CopyToClipboard content={JSON.stringify(stringified, null, '\t')}/> |
|
|
|
|
<CopyToClipboard content={JSON.stringify(opts.logs.raw || '0')}/> |
|
|
|
|
</td> |
|
|
|
|
</tr> |
|
|
|
|
)} |
|
|
|
|
{opts.val && ( |
|
|
|
|
<tr className='tr'> |
|
|
|
|
<td className='td' data-shared={`key_${opts.hash}`}> val </td> |
|
|
|
|
<td className='td' data-id={`txLoggerTableHash${opts.hash}`} data-shared={`pair_${opts.hash}`}>{val} wei |
|
|
|
|
<CopyToClipboard content={`${val} wei`}/> |
|
|
|
|
</td> |
|
|
|
|
</tr> |
|
|
|
|
)} |
|
|
|
|
</table> |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const debug = (event, tx) => { |
|
|
|
|
event.stopPropagation() |
|
|
|
|
if (tx.isCall && tx.envMode !== 'vm') { |
|
|
|
|
console.log('start debugging') |
|
|
|
|
return (<ModalDialog |
|
|
|
|
hide={false} |
|
|
|
|
handleHide={() => {} } |
|
|
|
|
message="Cannot debug this call. Debugging calls is only possible in JavaScript VM mode." |
|
|
|
|
/>) |
|
|
|
|
} else { |
|
|
|
|
props.event.trigger('debuggingRequested', [tx.hash]) |
|
|
|
|
console.log('trigger ', { tx: props.event.trigger }) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const renderUnKnownTransactions = (tx, receipt, index) => { |
|
|
|
|
const from = tx.from |
|
|
|
|
const to = tx.to |
|
|
|
|
const obj = { from, to } |
|
|
|
|
const showDetails = showTableDetails === tx.from |
|
|
|
|
const txType = 'unknown' + (tx.isCall ? 'Call' : 'Tx') |
|
|
|
|
return ( |
|
|
|
|
<span id={`tx${tx.hash}`} key={index}> |
|
|
|
|
<div className="log" onClick={(event) => txDetails(event, tx, obj)}> |
|
|
|
|
{/* onClick={e => txDetails(e, tx, data, obj)} */} |
|
|
|
|
{checkTxStatus(receipt || tx, txType)} |
|
|
|
|
{context({ from, to, tx }, props.blockchain)} |
|
|
|
|
{ console.log('under context and checkTxStatus')} |
|
|
|
|
<div className='buttons'> |
|
|
|
|
<div className='debug btn btn-primary btn-sm' data-shared='txLoggerDebugButton' data-id={`txLoggerDebugButton${tx.hash}`} onClick={(event) => debug(event, tx)}>Debug</div> |
|
|
|
|
</div> |
|
|
|
|
<i className = {`arrow fas ${(showTableHash.includes(tx.hash)) ? 'fa-angle-up' : 'fa-angle-down'}`}></i> |
|
|
|
|
</div> |
|
|
|
|
{showTableHash.includes(tx.hash) ? showTable({ |
|
|
|
|
hash: tx.hash, |
|
|
|
|
status: receipt !== null ? receipt.status : null, |
|
|
|
|
isCall: tx.isCall, |
|
|
|
|
contractAddress: tx.contractAddress, |
|
|
|
|
data: tx, |
|
|
|
|
from, |
|
|
|
|
to, |
|
|
|
|
gas: tx.gas, |
|
|
|
|
input: tx.input, |
|
|
|
|
'decoded output': ' - ', |
|
|
|
|
val: tx.value, |
|
|
|
|
transactionCost: tx.transactionCost, |
|
|
|
|
executionCost: tx.executionCost |
|
|
|
|
}) : null} |
|
|
|
|
{ console.log('end')} |
|
|
|
|
</span> |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const renderKnownTransactions = (tx, receipt, resolvedData, logs, index) => { |
|
|
|
|
const from = tx.from |
|
|
|
|
const to = resolvedData.contractName + '.' + resolvedData.fn |
|
|
|
|
const obj = { from, to } |
|
|
|
|
const txType = 'knownTx' |
|
|
|
|
console.log('render unknown transaction ') |
|
|
|
|
return ( |
|
|
|
|
<span id={`tx${tx.hash}`} key={index}> |
|
|
|
|
<div className="log" onClick={(event) => txDetails(event, tx, obj)}> |
|
|
|
|
{/* onClick={e => txDetails(e, tx, data, obj)} */} |
|
|
|
|
{checkTxStatus(receipt, txType)} |
|
|
|
|
{context({ from, to, tx }, props.blockchain)} |
|
|
|
|
<div className='buttons'> |
|
|
|
|
<div className='debug btn btn-primary btn-sm' data-shared='txLoggerDebugButton' data-id={`txLoggerDebugButton${tx.hash}`} onClick={(event) => debug(event, tx)}>Debug</div> |
|
|
|
|
</div> |
|
|
|
|
<i className = {`arrow fas ${(showDetails) ? 'fa-angle-up' : 'fa-angle-down'}`}></i> |
|
|
|
|
</div> |
|
|
|
|
{showTableDetails ? showTable({ |
|
|
|
|
hash: tx.hash, |
|
|
|
|
status: receipt !== null ? receipt.status : null, |
|
|
|
|
isCall: tx.isCall, |
|
|
|
|
contractAddress: tx.contractAddress, |
|
|
|
|
data: tx, |
|
|
|
|
from, |
|
|
|
|
to, |
|
|
|
|
gas: tx.gas, |
|
|
|
|
input: tx.input, |
|
|
|
|
'decoded input': resolvedData && resolvedData.params ? JSON.stringify(typeConversion.stringify(resolvedData.params), null, '\t') : ' - ', |
|
|
|
|
'decoded output': resolvedData && resolvedData.decodedReturnValue ? JSON.stringify(typeConversion.stringify(resolvedData.decodedReturnValue), null, '\t') : ' - ', |
|
|
|
|
logs: logs, |
|
|
|
|
val: tx.value, |
|
|
|
|
transactionCost: tx.transactionCost, |
|
|
|
|
executionCost: tx.executionCost |
|
|
|
|
}) : null} |
|
|
|
|
{ console.log('end')} |
|
|
|
|
</span> |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const handleAutoComplete = () => ( |
|
|
|
|
<div className='popup alert alert-secondary' style={{ display: autoCompletState.showSuggestions && autoCompletState.userInput !== '' ? 'block' : 'none' }}> |
|
|
|
|
<div> |
|
|
|
|
{autoCompletState.data._options.map((item, index) => { |
|
|
|
|
return ( |
|
|
|
|
<div key={index} data-id="autoCompletePopUpAutoCompleteItem" className={`autoCompleteItem listHandlerShow item ${autoCompletState.data._options[autoCompletState.activeSuggestion] === item ? 'border border-primary selectedOptions' : ''}`} onKeyDown={ handleSelect }> |
|
|
|
|
<div> |
|
|
|
|
{getKeyOf(item)} |
|
|
|
|
</div> |
|
|
|
|
<div> |
|
|
|
|
{getValueOf(item)} |
|
|
|
|
</div> |
|
|
|
|
</div> |
|
|
|
|
) |
|
|
|
|
})} |
|
|
|
|
</div> |
|
|
|
|
</div> |
|
|
|
|
) |
|
|
|
|
/* end of autoComplete */ |
|
|
|
|
|
|
|
|
|
const handlePaste = () => { |
|
|
|
|
setPaste(true) |
|
|
|
|
setAutoCompleteState(prevState => ({ ...prevState, activeSuggestion: 0, showSuggestions: false})) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
<div style={{ height: '323px', flexGrow: 1 }} className='panel'> |
|
|
|
|
<div className="bar"> |
|
|
|
@ -1050,7 +461,6 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => { |
|
|
|
|
</div> |
|
|
|
|
{/* ${self._view.pendingTxCount} */} |
|
|
|
|
<div className="mx-2" title='Pending Transactions'>0</div> |
|
|
|
|
<div className="verticalLine"></div> |
|
|
|
|
<div className="pt-1 h-80 mx-3 align-items-center listenOnNetwork custom-control custom-checkbox"> |
|
|
|
|
<input |
|
|
|
|
className="custom-control-input" |
|
|
|
@ -1086,15 +496,15 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => { |
|
|
|
|
{ |
|
|
|
|
handleAutoComplete() |
|
|
|
|
} |
|
|
|
|
<div data-id="terminalContainerDisplay" style = {{ |
|
|
|
|
position: 'absolute', |
|
|
|
|
<div data-id='terminalContainerDisplay' style = {{ |
|
|
|
|
position: 'relative', |
|
|
|
|
height: '100%', |
|
|
|
|
width: '100%', |
|
|
|
|
opacity: '0.1', |
|
|
|
|
zIndex: -1 |
|
|
|
|
}}></div> |
|
|
|
|
<div className="terminal"> |
|
|
|
|
<div id="journal" className="journal" data-id="terminalJournal"> |
|
|
|
|
<div id='journal' className='journal' data-id='terminalJournal'> |
|
|
|
|
{!clearConsole && <TerminalWelcomeMessage packageJson={props.version}/>} |
|
|
|
|
{newstate.journalBlocks && newstate.journalBlocks.map((x, index) => { |
|
|
|
|
if (x.name === 'emptyBlock') { |
|
|
|
@ -1142,4 +552,4 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => { |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
export default RemixUiTerminal |
|
|
|
|
export default RemixUiTerminal |