diff --git a/apps/remix-ide-e2e/src/tests/compiler_api.test.ts b/apps/remix-ide-e2e/src/tests/compiler_api.test.ts index 0b6cf00023f..acb51fbf84c 100644 --- a/apps/remix-ide-e2e/src/tests/compiler_api.test.ts +++ b/apps/remix-ide-e2e/src/tests/compiler_api.test.ts @@ -23,7 +23,7 @@ module.exports = { .addFile('test_jsCompile.js', { content: jsCompile }) .executeScript('remix.exeCurrent()') .pause(5000) - .journalChildIncludes(`version: '0.6.8+commit.0bbfe453'`) + .journalChildIncludes(`"languageversion": "0.6.8+commit.0bbfe453"`) }, 'Should update the compiler configuration with "setCompilerConfig" API': function (browser: NightwatchBrowser) { diff --git a/apps/remix-ide-e2e/src/tests/terminal.test.ts b/apps/remix-ide-e2e/src/tests/terminal.test.ts index 363b0538c99..0ea14271115 100644 --- a/apps/remix-ide-e2e/src/tests/terminal.test.ts +++ b/apps/remix-ide-e2e/src/tests/terminal.test.ts @@ -35,24 +35,11 @@ module.exports = { browser .waitForElementVisible('*[data-id="terminalCli"]') .executeScript('remix.help()') - .journalChildIncludes('remix.call(message: {name, key, payload})') - .journalChildIncludes('remix.getFile(path)') - .journalChildIncludes('remix.debug(hash)') .journalChildIncludes('remix.loadgist(id)') .journalChildIncludes('remix.loadurl(url)') - .journalChildIncludes('remix.setproviderurl(url)') .journalChildIncludes('remix.execute(filepath)') .journalChildIncludes('remix.exeCurrent()') .journalChildIncludes('remix.help()') - .journalChildIncludes('remix.debugHelp()') - }, - - 'Should execute remix.debugHelp() command': function (browser: NightwatchBrowser) { - browser - .waitForElementVisible('*[data-id="terminalCli"]') - .executeScript('remix.debugHelp()') - .journalChildIncludes('Here are some examples of scripts that can be run (using remix.exeCurrent() or directly from the console)') - .journalChildIncludes('Please see https://www.npmjs.com/package/remix-debug for more informations') }, 'Async/Await Script': function (browser: NightwatchBrowser) { diff --git a/apps/remix-ide/src/app/files/file-explorer.js b/apps/remix-ide/src/app/files/file-explorer.js index e829d42dd95..1b6d89797ea 100644 --- a/apps/remix-ide/src/app/files/file-explorer.js +++ b/apps/remix-ide/src/app/files/file-explorer.js @@ -16,7 +16,7 @@ const globalRegistry = require('../../global/registry') const queryParams = new QueryParams() let MENU_HANDLE -function fileExplorer (localRegistry, files, menuItems) { +function fileExplorer (localRegistry, files, menuItems, plugin) { var self = this this.events = new EventManager() // file provider backend @@ -292,6 +292,14 @@ function fileExplorer (localRegistry, files, menuItems) { () => {} ) } + if (key.endsWith('.js')) { + actions['Run'] = async () => { + provider.get(key, (error, content) => { + if (error) return console.log(error) + plugin.call('scriptRunner', 'execute', content) + }) + } + } } MENU_HANDLE = contextMenu(event, actions) }) diff --git a/apps/remix-ide/src/app/panels/file-panel.js b/apps/remix-ide/src/app/panels/file-panel.js index 89279816200..32d8a1e303f 100644 --- a/apps/remix-ide/src/app/panels/file-panel.js +++ b/apps/remix-ide/src/app/panels/file-panel.js @@ -54,7 +54,7 @@ module.exports = class Filepanel extends ViewPlugin { } function createProvider (key, menuItems) { - return new FileExplorer(self._components.registry, self._deps.fileProviders[key], menuItems) + return new FileExplorer(self._components.registry, self._deps.fileProviders[key], menuItems, self) } var fileExplorer = createProvider('browser', ['createNewFile', 'publishToGist', canUpload ? 'uploadFile' : '']) diff --git a/apps/remix-ide/src/app/panels/terminal.js b/apps/remix-ide/src/app/panels/terminal.js index 0bce0577c37..672133986c8 100644 --- a/apps/remix-ide/src/app/panels/terminal.js +++ b/apps/remix-ide/src/app/panels/terminal.js @@ -5,12 +5,9 @@ import * as packageJson from '../../../../../package.json' var yo = require('yo-yo') var javascriptserialize = require('javascript-serialize') var jsbeautify = require('js-beautify') -var ethers = require('ethers') var type = require('component-type') var vm = require('vm') var EventManager = require('../../lib/events') -var Web3 = require('web3') -var swarmgw = require('swarmgw')() var CommandInterpreterAPI = require('../../lib/cmdInterpreterAPI') var AutoCompletePopup = require('../ui/auto-complete-popup') @@ -77,7 +74,6 @@ class Terminal extends Plugin { self.registerCommand('error', self._blocksRenderer('error'), { activate: true }) self.registerCommand('script', function execute (args, scopedCommands, append) { var script = String(args[0]) - scopedCommands.log(`> ${script}`) self._shell(script, scopedCommands, function (error, output) { if (error) scopedCommands.error(error) else if (output) scopedCommands.log(output) @@ -91,9 +87,6 @@ class Terminal extends Plugin { self.registerFilter('error', basicFilter) self.registerFilter('script', basicFilter) - self._jsSandboxContext = {} - self._jsSandboxRegistered = {} - if (opts.shell) self._shell = opts.shell // ??? register(self) } @@ -451,20 +444,26 @@ class Terminal extends Plugin { var intro = yo`
- Welcome to Remix ${packageJson.version} -

-
You can use this terminal for:
+
You can use this terminal to:
+
` @@ -481,6 +480,21 @@ class Terminal extends Plugin { return self._view.el + function wrapScript (script) { + if (script.startsWith('remix.')) return script + return ` + try { + const ret = ${script}; + if (ret instanceof Promise) { + ret.then((result) => { console.log(result) }).catch((error) => { console.log(error) }) + } else { + console.log(ret) + } + } catch (e) { + console.log(e.message) + } + ` + } function change (event) { if (self._components.autoCompletePopup.handleAutoComplete( event, @@ -500,7 +514,7 @@ class Terminal extends Plugin { self._view.input.innerText = '\n' if (script.length) { self._cmdHistory.unshift(script) - self.commands.script(script) + self.commands.script(wrapScript(script)) } self._components.autoCompletePopup.removeAutoComplete() } @@ -624,14 +638,17 @@ class Terminal extends Plugin { error: 'text-danger' }[mode] // defaults if (mode) { + const filterUndefined = (el) => el !== undefined && el !== null return function logger (args, scopedCommands, append) { - var types = args.map(type) - var values = javascriptserialize.apply(null, args).map(function (val, idx) { + var types = args.filter(filterUndefined).map(type) + var values = javascriptserialize.apply(null, args.filter(filterUndefined)).map(function (val, idx) { if (typeof args[idx] === 'string') val = args[idx] if (types[idx] === 'element') val = jsbeautify.html(val) return val }) - append(yo`${values}`) + if (values.length) { + append(yo`${values}`) + } } } else { throw new Error('mode is not supported') @@ -707,9 +724,8 @@ class Terminal extends Plugin { // for all the other case, we use the Code Executor plugin var context = domTerminalFeatures(self, scopedCommands, self.blockchain) try { - var cmds = vm.createContext(Object.assign(self._jsSandboxContext, context, self._jsSandboxRegistered)) + var cmds = vm.createContext(context) var result = vm.runInContext(script, cmds) - self._jsSandboxContext = Object.assign(cmds, context) return done(null, result) } catch (error) { return done(error.message) @@ -726,29 +742,7 @@ class Terminal extends Plugin { function domTerminalFeatures (self, scopedCommands, blockchain) { return { - swarmgw, - ethers, - remix: self._components.cmdInterpreter, - web3: new Web3(blockchain.web3().currentProvider), - console: { - log: function () { scopedCommands.log.apply(scopedCommands, arguments) }, - info: function () { scopedCommands.info.apply(scopedCommands, arguments) }, - warn: function () { scopedCommands.warn.apply(scopedCommands, arguments) }, - error: function () { scopedCommands.error.apply(scopedCommands, arguments) } - }, - setTimeout: (fn, time) => { - return setTimeout(() => { self._shell('(' + fn.toString() + ')()', scopedCommands, () => {}) }, time) - }, - setInterval: (fn, time) => { - return setInterval(() => { self._shell('(' + fn.toString() + ')()', scopedCommands, () => {}) }, time) - }, - clearTimeout: clearTimeout, - clearInterval: clearInterval, - exports: { - register: (key, obj) => { self._jsSandboxRegistered[key] = obj }, - remove: (key) => { delete self._jsSandboxRegistered[key] }, - clear: () => { self._jsSandboxRegistered = {} } - } + remix: self._components.cmdInterpreter } } diff --git a/apps/remix-ide/src/app/ui/auto-complete-popup.js b/apps/remix-ide/src/app/ui/auto-complete-popup.js index 92445bc30cc..1bb1ec3386b 100644 --- a/apps/remix-ide/src/app/ui/auto-complete-popup.js +++ b/apps/remix-ide/src/app/ui/auto-complete-popup.js @@ -192,7 +192,7 @@ class AutoCompletePopup { this.opts.appManager.event.on('activate', async (profile) => { if (!profile.methods) return profile.methods.forEach((method) => { - const key = `remix.call({name: '${profile.name}', key:'${method}', payload: []}).then((result) => { console.log(result) }).catch((error) => { console.log(error) })` + const key = `remix.call('${profile.name}', '${method}')` const keyValue = {} keyValue[key] = `call ${profile.name} - ${method}` if (this.extraCommands.includes(keyValue)) return diff --git a/apps/remix-ide/src/lib/cmdInterpreterAPI.js b/apps/remix-ide/src/lib/cmdInterpreterAPI.js index 8f609c279a3..b9ed96649bb 100644 --- a/apps/remix-ide/src/lib/cmdInterpreterAPI.js +++ b/apps/remix-ide/src/lib/cmdInterpreterAPI.js @@ -1,16 +1,12 @@ 'use strict' var yo = require('yo-yo') var async = require('async') -var remixDebug = require('@remix-project/remix-debug') var EventManager = require('../lib/events') var CompilerImport = require('../app/compiler/compiler-imports') var toolTip = require('../app/ui/tooltip') var globalRegistry = require('../global/registry') var SourceHighlighter = require('../app/editor/sourceHighlighter') -var RemixDebug = require('@remix-project/remix-debug').EthDebugger -var TreeView = require('../app/ui/TreeView') // TODO setup a direct reference to the UI components -var solidityTypeFormatter = require('../app/tabs/debugger/debuggerUI/vmDebugger/utils/SolidityTypeFormatter') var GistHandler = require('./gist-handler') class CmdInterpreterAPI { @@ -31,112 +27,14 @@ class CmdInterpreterAPI { offsetToLineColumnConverter: self._components.registry.get('offsettolinecolumnconverter').api } self.commandHelp = { - 'remix.call(message: {name, key, payload})': 'Call a registered plugins', - 'remix.getFile(path)': 'Returns the content of the file located at the given path', - 'remix.setFile(path, content)': 'set the content of the file located at the given path', - 'remix.debug(hash)': 'Start debugging a transaction.', 'remix.loadgist(id)': 'Load a gist in the file explorer.', 'remix.loadurl(url)': 'Load the given url in the file explorer. The url can be of type github, swarm, ipfs or raw http', - 'remix.setproviderurl(url)': 'Change the current provider to Web3 provider and set the url endpoint.', 'remix.execute(filepath)': 'Run the script specified by file path. If filepath is empty, script currently displayed in the editor is executed.', 'remix.exeCurrent()': 'Run the script currently displayed in the editor', 'remix.help()': 'Display this help message', - 'remix.debugHelp()': 'Display help message for debugging' } } - call (message) { - return this._components.terminal.externalApi.request(message) - } log () { arguments[0] != null ? this._components.terminal.commands.html(arguments[0]) : this._components.terminal.commands.html(arguments[1]) } - highlight (rawLocation) { - var self = this - if (!rawLocation) { - self._components.sourceHighlighter.currentSourceLocation(null) - return - } - var lineColumnPos = self._deps.offsetToLineColumnConverter.offsetToLineColumn(rawLocation, rawLocation.file, - self._deps.compilersArtefacts['__last'].getSourceCode().sources, - self._deps.compilersArtefacts['__last'].getAsts()) - self._components.sourceHighlighter.currentSourceLocation(lineColumnPos, rawLocation) - } - debug (hash, cb) { - var self = this - delete self.d - self.blockchain.web3().eth.getTransaction(hash, (error, tx) => { - if (error) return cb(error) - var debugSession = new RemixDebug({ - compilationResult: () => { - return self._deps.compilersArtefacts['__last'].getData() - } - }) - debugSession.addProvider('web3', self.blockchain.web3()) - debugSession.switchProvider('web3') - debugSession.debug(tx) - self.d = debugSession - this._components.terminal.commands.log('A new debugging session is available at remix.d') - if (cb) cb(null, debugSession) - // helpers - self.d.highlight = (address, vmtraceIndex) => { - if (!address) return self.highlight() - self.d.sourceLocationFromVMTraceIndex(address, vmtraceIndex, (error, rawLocation) => { - if (!error && rawLocation) { - self.highlight(rawLocation) - } - }) - } - self.d.stateAt = (vmTraceIndex) => { - self.d.extractStateAt(vmTraceIndex, (error, state) => { - if (error) return self.log(error) - self.d.decodeStateAt(vmTraceIndex, state, (error, state) => { - if (error) return this._components.terminal.commands.html(error) - var treeView = new TreeView({ - json: true, - formatSelf: solidityTypeFormatter.formatSelf, - extractData: solidityTypeFormatter.extractData - }) - self.log('State at ' + vmTraceIndex) - self._components.terminal.commands.html(treeView.render(state, true)) - }) - }) - } - self.d.localsAt = (contractAddress, vmTraceIndex) => { - debugSession.sourceLocationFromVMTraceIndex(contractAddress, vmTraceIndex, (error, location) => { - if (error) return self.log(error) - debugSession.decodeLocalsAt(23, location, (error, locals) => { - if (error) return this._components.terminal.commands.html(error) - var treeView = new TreeView({ - json: true, - formatSelf: solidityTypeFormatter.formatSelf, - extractData: solidityTypeFormatter.extractData - }) - self.log('Locals at ' + vmTraceIndex) - self._components.terminal.commands.html(treeView.render(locals, true)) - }) - }) - } - self.d.goTo = (row) => { - if (self._deps.editor.current()) { - var breakPoint = new remixDebug.BreakpointManager(self.d, (sourceLocation) => { - return self._deps.offsetToLineColumnConverter.offsetToLineColumn(sourceLocation, sourceLocation.file, - self._deps.compilersArtefacts['__last'].getSourceCode().sources, - self._deps.compilersArtefacts['__last'].getAsts()) - }) - breakPoint.event.register('breakpointHit', (sourceLocation, currentStep) => { - self.log(null, 'step index ' + currentStep) - self.highlight(sourceLocation) - self.d.stateAt(currentStep) - self.d.traceManager.getCurrentCalledAddressAt(currentStep, (error, address) => { - if (error) return self.log(address) - self.d.localsAt(address, currentStep) - }) - }) - breakPoint.event.register('NoBreakpointHit', () => { self.log('line ' + row + ' is not part of the current execution') }) - breakPoint.add({fileName: self._deps.editor.current(), row: row - 1}) - breakPoint.jumpNextBreakpoint(0, true) - } - } - }) - } loadgist (id, cb) { const self = this self._components.gistHandler.loadFromGist({gist: id}, this._deps.fileManager) @@ -179,36 +77,9 @@ class CmdInterpreterAPI { } }) } - setproviderurl (url, cb) { - this.blockchain.setProviderFromEndpoint(url, 'web3', (error) => { - if (error) toolTip(error) - if (cb) cb() - }) - } exeCurrent (cb) { return this.execute(undefined, cb) } - getFile (path, cb) { - var provider = this._deps.fileManager.fileProviderOf(path) - if (provider) { - provider.get(path, cb) - } else { - cb('file not found') - } - } - setFile (path, content, cb) { - cb = cb || function () {} - var provider = this._deps.fileManager.fileProviderOf(path) - if (provider) { - provider.set(path, content, (error) => { - if (error) return cb(error) - this._deps.fileManager.syncEditor(path) - cb() - }) - } else { - cb('file not found') - } - } execute (file, cb) { const self = this @@ -257,37 +128,6 @@ class CmdInterpreterAPI { if (cb) cb() return '' } - debugHelp (cb) { - const self = this - var help = yo`
Here are some examples of scripts that can be run (using remix.exeCurrent() or directly from the console)
` - help.appendChild(yo`
`) - help.appendChild(yo`
`) - help.appendChild(yo`
remix.debug('0x3c247ac268afb9a9c183feb9d4e83df51efbc8a2f4624c740789b788dac43029', function (error, debugSession) { - remix.log = function () { arguments[0] != null ? console.log(arguments[0]) : console.log(arguments[1]) } - - remix.d.traceManager.getLength(remix.log) - remix.storageView = remix.d.storageViewAt(97, '0x692a70d2e424a56d2c6c27aa97d1a86395877b3a') - console.log('storage at 97 :') - remix.storageView.storageRange(remix.log) -})
`) - help.appendChild(yo`
remix.log = function () { arguments[0] != null ? console.log(arguments[0]) : console.log(arguments[1]) } - remix.d.extractStateAt(2, function (error, state) { - remix.d.decodeStateAt(97, state, remix.log) - })
`) - help.appendChild(yo`
`) - help.appendChild(yo`
remix.highlight(contractAddress, vmTraceIndex)
`) - help.appendChild(yo`
`) - help.appendChild(yo`
remix.goTo(row) (this log the index in the vm trace, state and local variables)
`) - help.appendChild(yo`
`) - help.appendChild(yo`
remix.stateAt(vmTraceIndex)
`) - help.appendChild(yo`
`) - help.appendChild(yo`
remix.localsAt(vmTraceIndex)
`) - help.appendChild(yo`
`) - help.appendChild(yo`
Please see https://www.npmjs.com/package/remix-debug for more informations
`) - self._components.terminal.commands.html(help) - if (cb) cb() - return '' - } } module.exports = CmdInterpreterAPI diff --git a/apps/remix-ide/src/lib/commands.js b/apps/remix-ide/src/lib/commands.js index fe579ee0374..b20783557b6 100644 --- a/apps/remix-ide/src/lib/commands.js +++ b/apps/remix-ide/src/lib/commands.js @@ -6,16 +6,11 @@ const allPrograms = [ ] const allCommands = [ - {'remix.debug(hash)': 'Start debugging a transaction.'}, - {'remix.debugHelp()': 'Display help message for debugging'}, {'remix.execute(filepath)': 'Run the script specified by file path. If filepath is empty, script currently displayed in the editor is executed.'}, {'remix.exeCurrent()': 'Run the script currently displayed in the editor.'}, - {'remix.getFile(path)': 'Returns the content of the file located at the given path'}, {'remix.help()': 'Display this help message.'}, {'remix.loadgist(id)': 'Load a gist in the file explorer.'}, {'remix.loadurl(url)': 'Load the given url in the file explorer. The url can be of type github, swarm or ipfs.'}, - {'remix.setFile(path, content)': 'set the content of the file located at the given path'}, - {'remix.setproviderurl(url)': 'Change the current provider to Web3 provider and set the url endpoint.'}, {'swarmgw.get(url, cb)': 'Download files from Swarm via https://swarm-gateways.net/'}, {'swarmgw.put(content, cb)': 'Upload files to Swarm via https://swarm-gateways.net/'}, @@ -30,8 +25,7 @@ const allCommands = [ {'ethers.utils.RLP': 'This encoding method is used internally for several aspects of Ethereum, such as encoding transactions and determining contract addresses.'}, {'ethers.Wallet': 'A wallet manages a private/public key pair which is used to cryptographically sign transactions and prove ownership on the Ethereum network.'}, {'ethers.version': 'Contains the version of the ethers container object.'}, - - {'web3.bzz': 'Bzz module for interacting with the swarm network.'}, + {'web3.eth': 'Eth module for interacting with the Ethereum network.'}, {'web3.eth.accounts': 'The web3.eth.accounts contains functions to generate Ethereum accounts and sign transactions and data.'}, {'web3.eth.abi': 'The web3.eth.abi functions let you de- and encode parameters to ABI (Application Binary Interface) for function calls to the EVM (Ethereum Virtual Machine).'}, @@ -49,7 +43,12 @@ const allCommands = [ {'web3.eth.clearSubscriptions();': 'Resets subscriptions.'}, {'web3.eth.Contract(jsonInterface[, address][, options])': 'The web3.eth.Contract object makes it easy to interact with smart contracts on the ethereum blockchain.'}, - {'web3.eth.accounts.create([entropy]);': 'The web3.eth.accounts contains functions to generate Ethereum accounts and sign transactions and data.'} + {'web3.eth.accounts.create([entropy]);': 'The web3.eth.accounts contains functions to generate Ethereum accounts and sign transactions and data.'}, + {'web3.eth.getAccounts();': 'Retrieve the list of accounts'}, + {'web3.eth.accounts.privateKeyToAccount(privateKey [, ignoreLength ]);': 'Get the account from the private key'}, + {'web3.eth.accounts.signTransaction(tx, privateKey [, callback]);': 'Sign Transaction'}, + {'web3.eth.accounts.recoverTransaction(rawTransaction);': 'Sign Transaction'}, + {'web3.eth.accounts.hashMessage(message);': 'Hash message'}, ] module.exports = {