Merge branch 'master' into reset-error

pull/2093/head
David Disu 3 years ago committed by GitHub
commit aafd264a96
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      apps/remix-ide-e2e/src/commands/goToVMTraceStep.ts
  2. 83
      apps/remix-ide-e2e/src/tests/debugger.test.ts
  3. 16
      apps/remix-ide-e2e/src/tests/solidityUnittests.test.ts
  4. 21
      apps/remix-ide/src/app/components/hidden-panel.tsx
  5. 21
      apps/remix-ide/src/app/components/main-panel.tsx
  6. 32
      apps/remix-ide/src/app/components/plugin-manager-component.js
  7. 21
      apps/remix-ide/src/app/components/side-panel.tsx
  8. 31
      apps/remix-ide/src/app/components/vertical-icons.tsx
  9. 61
      apps/remix-ide/src/app/editor/editor.js
  10. 18
      apps/remix-ide/src/app/files/fileManager.ts
  11. 13
      apps/remix-ide/src/app/panels/file-panel.js
  12. 27
      apps/remix-ide/src/app/panels/tab-proxy.js
  13. 32
      apps/remix-ide/src/app/panels/terminal.js
  14. 52
      apps/remix-ide/src/app/tabs/analysis-tab.js
  15. 9
      apps/remix-ide/src/app/tabs/compile-tab.js
  16. 10
      apps/remix-ide/src/app/tabs/debugger-tab.js
  17. 48
      apps/remix-ide/src/app/tabs/settings-tab.tsx
  18. 23
      apps/remix-ide/src/app/tabs/test-tab.js
  19. 11
      apps/remix-ide/src/app/udapp/run-tab.js
  20. 13
      apps/remix-ide/src/app/ui/landing-page/landing-page.js
  21. 4
      apps/remix-ide/src/index.tsx
  22. 57
      apps/remix-ide/src/lib/commands.js
  23. 2
      apps/remix-ide/src/remixEngine.js
  24. 2
      libs/remix-core-plugin/src/lib/compiler-fetch-and-compile.ts
  25. 1
      libs/remix-debug/src/Ethdebugger.ts
  26. 6
      libs/remix-debug/src/debugger/debugger.ts
  27. 13
      libs/remix-debug/src/debugger/stepManager.ts
  28. 2
      libs/remix-debug/src/storage/storageResolver.ts
  29. 5
      libs/remix-lib/README.md
  30. 23
      libs/remix-lib/src/execution/eventsDecoder.ts
  31. 11
      libs/remix-lib/src/index.ts
  32. 55
      libs/remix-lib/src/web3Provider/dummyProvider.ts
  33. 38
      libs/remix-lib/src/web3Provider/web3Providers.ts
  34. 49
      libs/remix-simulator/src/VmProxy.ts
  35. 40
      libs/remix-simulator/src/vm-context.ts
  36. 39
      libs/remix-ui/app/src/lib/remix-app/remix-app.tsx
  37. 4
      libs/remix-ui/debugger-ui/src/lib/button-navigator/button-navigator.css
  38. 9
      libs/remix-ui/debugger-ui/src/lib/button-navigator/button-navigator.tsx
  39. 27
      libs/remix-ui/debugger-ui/src/lib/debugger-ui.tsx
  40. 17
      libs/remix-ui/debugger-ui/src/lib/slider/slider.tsx
  41. 2
      libs/remix-ui/debugger-ui/src/lib/vm-debugger/assembly-items.tsx
  42. 1
      libs/remix-ui/helper/src/index.ts
  43. 24
      libs/remix-ui/helper/src/lib/components/PluginViewWrapper.tsx
  44. 9
      libs/remix-ui/helper/src/lib/helper-components.tsx
  45. 2
      libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.tsx
  46. 1
      libs/remix-ui/panel/src/lib/plugins/panel-plugin.tsx
  47. 5
      libs/remix-ui/panel/src/lib/plugins/panel.css
  48. 1
      libs/remix-ui/terminal/src/lib/commands.ts
  49. 3
      libs/remix-ui/terminal/src/lib/remix-ui-terminal.css
  50. 2
      libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx
  51. 6
      libs/remix-ui/terminal/src/lib/terminalWelcome.tsx
  52. 1
      libs/remix-ui/vertical-icons-panel/src/lib/remix-ui-vertical-icons-panel.css
  53. 14
      package.json

@ -12,6 +12,7 @@ class GoToVmTraceStep extends EventEmitter {
function goToVMtraceStep (browser: NightwatchBrowser, step: number, incr: number, done: VoidFunction) { function goToVMtraceStep (browser: NightwatchBrowser, step: number, incr: number, done: VoidFunction) {
browser.execute(function (step) { (document.getElementById('slider') as HTMLInputElement).value = (step - 1).toString() }, [step]) browser.execute(function (step) { (document.getElementById('slider') as HTMLInputElement).value = (step - 1).toString() }, [step])
.setValue('*[data-id="slider"]', new Array(1).fill(browser.Keys.RIGHT_ARROW)) .setValue('*[data-id="slider"]', new Array(1).fill(browser.Keys.RIGHT_ARROW))
.pause(1000)
.perform(() => { .perform(() => {
done() done()
}) })

@ -39,12 +39,9 @@ module.exports = {
'Should debug transaction using slider #group1': function (browser: NightwatchBrowser) { 'Should debug transaction using slider #group1': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="verticalIconsKindudapp"]') browser.waitForElementVisible('*[data-id="verticalIconsKindudapp"]')
.waitForElementVisible('*[data-id="slider"]') .waitForElementVisible('*[data-id="slider"]')
// eslint-disable-next-line dot-notation .goToVMTraceStep(51)
.execute(function () { document.getElementById('slider')['value'] = '50' }) // It only moves slider to 50 but vm traces are not updated .waitForElementContainsText('*[data-id="solidityLocals"]', 'toast', 60000)
.setValue('*[data-id="slider"]', new Array(1).fill(browser.Keys.RIGHT_ARROW)) .waitForElementContainsText('*[data-id="solidityLocals"]', '999', 60000)
.pause(2000)
.click('*[data-id="dropdownPanelSolidityLocals"]')
.waitForElementContainsText('*[data-id="solidityLocals"]', 'no locals', 60000)
.waitForElementContainsText('*[data-id="stepdetail"]', 'vm trace step:\n51', 60000) .waitForElementContainsText('*[data-id="stepdetail"]', 'vm trace step:\n51', 60000)
}, },
@ -159,10 +156,7 @@ module.exports = {
.pause(2000) .pause(2000)
.debugTransaction(0) .debugTransaction(0)
.waitForElementVisible('*[data-id="slider"]').pause(2000) .waitForElementVisible('*[data-id="slider"]').pause(2000)
// .setValue('*[data-id="slider"]', '5000') // Like this, setValue doesn't work properly for input type = range .goToVMTraceStep(7453)
// eslint-disable-next-line dot-notation
.execute(function () { document.getElementById('slider')['value'] = '7450' }).pause(10000) // It only moves slider to 7450 but vm traces are not updated
.setValue('*[data-id="slider"]', new Array(3).fill(browser.Keys.RIGHT_ARROW)) // This will press NEXT 3 times and will update the trace details
.waitForElementPresent('*[data-id="treeViewDivtreeViewItemarray"]') .waitForElementPresent('*[data-id="treeViewDivtreeViewItemarray"]')
.click('*[data-id="treeViewDivtreeViewItemarray"]') .click('*[data-id="treeViewDivtreeViewItemarray"]')
.waitForElementPresent('*[data-id="treeViewDivtreeViewLoadMore"]') .waitForElementPresent('*[data-id="treeViewDivtreeViewLoadMore"]')
@ -210,15 +204,7 @@ module.exports = {
.pause(3000) .pause(3000)
.clickLaunchIcon('debugger') .clickLaunchIcon('debugger')
.waitForElementVisible('*[data-id="slider"]') .waitForElementVisible('*[data-id="slider"]')
// eslint-disable-next-line dot-notation .goToVMTraceStep(154)
.execute(function () { document.getElementById('slider')['value'] = '153' }) // It only moves slider to 153 but vm traces are not updated
.setValue('*[data-id="slider"]', new Array(1).fill(browser.Keys.RIGHT_ARROW))
.pause(1000)
/*
setting the slider to 5 leads to "vm trace step: 91" for chrome and "vm trace step: 92" for firefox
=> There is something going wrong with the nightwatch API here.
As we are only testing if debugger is active, this is ok to keep that for now.
*/
.waitForElementContainsText('*[data-id="stepdetail"]', 'vm trace step:\n154', 60000) .waitForElementContainsText('*[data-id="stepdetail"]', 'vm trace step:\n154', 60000)
}, },
@ -241,8 +227,23 @@ module.exports = {
.waitForElementVisible('*[data-id="solidityLocals"]', 60000) .waitForElementVisible('*[data-id="solidityLocals"]', 60000)
.pause(10000) .pause(10000)
.checkVariableDebug('soliditylocals', { num: { value: '2', type: 'uint256' } }) .checkVariableDebug('soliditylocals', { num: { value: '2', type: 'uint256' } })
.checkVariableDebug('soliditystate', { number: { value: '0', type: 'uint256', constant: false, immutable: false } }) .checkVariableDebug('soliditystate', { number: { value: '0', type: 'uint256', constant: false, immutable: false } })
.end() },
'Should debug reverted transactions #group5': function (browser: NightwatchBrowser) {
browser
.testContracts('reverted.sol', sources[6]['reverted.sol'], ['A', 'B', 'C'])
.clickLaunchIcon('udapp')
.selectContract('A')
.createContract('')
.pause(500)
.clickInstance(0)
.clickFunction('callA - transact (not payable)')
.debugTransaction(1)
.goToVMTraceStep(79)
.waitForElementVisible('*[data-id="debugGoToRevert"]', 60000)
.click('*[data-id="debugGoToRevert"]')
.waitForElementContainsText('*[data-id="asmitems"] div[selected="selected"]', '117 REVERT')
} }
} }
@ -366,6 +367,46 @@ const sources = [
} }
` `
} }
},
{
'reverted.sol': {
content: `contract A {
B b;
uint p;
constructor () {
b = new B();
}
function callA() public {
p = 123;
try b.callB() {
}
catch (bytes memory reason) {
}
}
}
contract B {
C c;
uint p;
constructor () {
c = new C();
}
function callB() public {
p = 124;
revert("revert!");
c.callC();
}
}
contract C {
uint p;
function callC() public {
p = 125;
}
}`
}
} }
] ]

@ -283,9 +283,7 @@ module.exports = {
.waitForElementVisible('*[data-id="dropdownPanelSolidityLocals"]').pause(1000) .waitForElementVisible('*[data-id="dropdownPanelSolidityLocals"]').pause(1000)
.click('*[data-id="dropdownPanelSolidityLocals"]') .click('*[data-id="dropdownPanelSolidityLocals"]')
.waitForElementContainsText('*[data-id="solidityLocals"]', 'no locals', 60000) .waitForElementContainsText('*[data-id="solidityLocals"]', 'no locals', 60000)
// eslint-disable-next-line dot-notation .goToVMTraceStep(316)
.execute(function () { document.getElementById('slider')['value'] = '315' }) // It only moves slider to 315 but vm traces are not updated
.setValue('*[data-id="slider"]', new Array(1).fill(browser.Keys.RIGHT_ARROW))
.waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinningProposalFailed()', 60000) .waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinningProposalFailed()', 60000)
.waitForElementContainsText('*[data-id="functionPanel"]', 'vote(proposal)', 60000) .waitForElementContainsText('*[data-id="functionPanel"]', 'vote(proposal)', 60000)
.pause(5000) .pause(5000)
@ -295,9 +293,7 @@ module.exports = {
.scrollAndClick('#Check_winning_proposal_passed') .scrollAndClick('#Check_winning_proposal_passed')
.waitForElementContainsText('*[data-id="sidePanelSwapitTitle"]', 'DEBUGGER', 60000) .waitForElementContainsText('*[data-id="sidePanelSwapitTitle"]', 'DEBUGGER', 60000)
.waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinningProposalPassed()', 60000) .waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinningProposalPassed()', 60000)
// eslint-disable-next-line dot-notation .goToVMTraceStep(1451)
.execute(function () { document.getElementById('slider')['value'] = '1450' })
.setValue('*[data-id="slider"]', new Array(1).fill(browser.Keys.RIGHT_ARROW))
.waitForElementContainsText('*[data-id="functionPanel"]', 'equal(a, b, message)', 60000) .waitForElementContainsText('*[data-id="functionPanel"]', 'equal(a, b, message)', 60000)
.waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinningProposalPassed()', 60000) .waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinningProposalPassed()', 60000)
// remix_test.sol should be opened in editor // remix_test.sol should be opened in editor
@ -307,9 +303,7 @@ module.exports = {
.scrollAndClick('#Check_winning_proposal_again') .scrollAndClick('#Check_winning_proposal_again')
.waitForElementContainsText('*[data-id="sidePanelSwapitTitle"]', 'DEBUGGER', 60000) .waitForElementContainsText('*[data-id="sidePanelSwapitTitle"]', 'DEBUGGER', 60000)
.waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinningProposalAgain()', 60000) .waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinningProposalAgain()', 60000)
// eslint-disable-next-line dot-notation .goToVMTraceStep(1151)
.execute(function () { document.getElementById('slider')['value'] = '1150' })
.setValue('*[data-id="slider"]', new Array(1).fill(browser.Keys.RIGHT_ARROW))
.waitForElementContainsText('*[data-id="functionPanel"]', 'equal(a, b, message)', 60000) .waitForElementContainsText('*[data-id="functionPanel"]', 'equal(a, b, message)', 60000)
.waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinningProposalAgain()', 60000) .waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinningProposalAgain()', 60000)
.pause(5000) .pause(5000)
@ -317,9 +311,7 @@ module.exports = {
.scrollAndClick('#Check_winnin_proposal_with_return_value').pause(5000) .scrollAndClick('#Check_winnin_proposal_with_return_value').pause(5000)
.waitForElementContainsText('*[data-id="sidePanelSwapitTitle"]', 'DEBUGGER', 60000) .waitForElementContainsText('*[data-id="sidePanelSwapitTitle"]', 'DEBUGGER', 60000)
.waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinninProposalWithReturnValue()', 60000) .waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinninProposalWithReturnValue()', 60000)
// eslint-disable-next-line dot-notation .goToVMTraceStep(321)
.execute(function () { document.getElementById('slider')['value'] = '320' })
.setValue('*[data-id="slider"]', new Array(1).fill(browser.Keys.RIGHT_ARROW))
.waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinninProposalWithReturnValue()', 60000) .waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinninProposalWithReturnValue()', 60000)
.clickLaunchIcon('filePanel') .clickLaunchIcon('filePanel')
.pause(2000) .pause(2000)

@ -1,9 +1,9 @@
// eslint-disable-next-line no-use-before-define // eslint-disable-next-line no-use-before-define
import React from 'react' import React from 'react'
import ReactDOM from 'react-dom' // eslint-disable-line
import { AbstractPanel } from './panel' import { AbstractPanel } from './panel'
import * as packageJson from '../../../../../package.json' import * as packageJson from '../../../../../package.json'
import { RemixPluginPanel } from '@remix-ui/panel' import { RemixPluginPanel } from '@remix-ui/panel'
import { PluginViewWrapper } from '@remix-ui/helper'
const profile = { const profile = {
name: 'hiddenPanel', name: 'hiddenPanel',
@ -15,6 +15,7 @@ const profile = {
export class HiddenPanel extends AbstractPanel { export class HiddenPanel extends AbstractPanel {
el: HTMLElement el: HTMLElement
dispatch: React.Dispatch<any> = () => {}
constructor () { constructor () {
super(profile) super(profile)
this.el = document.createElement('div') this.el = document.createElement('div')
@ -27,11 +28,23 @@ export class HiddenPanel extends AbstractPanel {
this.renderComponent() this.renderComponent()
} }
render () { updateComponent (state: any) {
return this.el return <RemixPluginPanel header={<></>} plugins={state.plugins}/>
}
setDispatch (dispatch: React.Dispatch<any>) {
this.dispatch = dispatch
}
render() {
return (
<div className='pluginsContainer'><PluginViewWrapper plugin={this} /></div>
);
} }
renderComponent () { renderComponent () {
ReactDOM.render(<RemixPluginPanel header={<></>} plugins={this.plugins}/>, this.el) this.dispatch({
plugins: this.plugins,
})
} }
} }

@ -1,8 +1,8 @@
import React from 'react' // eslint-disable-line import React from 'react' // eslint-disable-line
import { AbstractPanel } from './panel' import { AbstractPanel } from './panel'
import ReactDOM from 'react-dom' // eslint-disable-line
import { RemixPluginPanel } from '@remix-ui/panel' import { RemixPluginPanel } from '@remix-ui/panel'
import packageJson from '../../../../../package.json' import packageJson from '../../../../../package.json'
import { PluginViewWrapper } from '@remix-ui/helper'
const profile = { const profile = {
name: 'mainPanel', name: 'mainPanel',
@ -14,6 +14,7 @@ const profile = {
export class MainPanel extends AbstractPanel { export class MainPanel extends AbstractPanel {
element: HTMLDivElement element: HTMLDivElement
dispatch: React.Dispatch<any> = () => {}
constructor (config) { constructor (config) {
super(profile) super(profile)
this.element = document.createElement('div') this.element = document.createElement('div')
@ -22,6 +23,10 @@ export class MainPanel extends AbstractPanel {
// this.config = config // this.config = config
} }
setDispatch (dispatch: React.Dispatch<any>) {
this.dispatch = dispatch
}
onActivation () { onActivation () {
this.renderComponent() this.renderComponent()
} }
@ -47,11 +52,17 @@ export class MainPanel extends AbstractPanel {
this.renderComponent() this.renderComponent()
} }
render () { renderComponent () {
return this.element this.dispatch({
plugins: this.plugins
})
}
render() {
return <div style={{height: '100%', width: '100%'}} data-id='mainPanelPluginsContainer'><PluginViewWrapper plugin={this} /></div>
} }
renderComponent () { updateComponent (state: any) {
ReactDOM.render(<RemixPluginPanel header={<></>} plugins={this.plugins}/>, this.element) return <RemixPluginPanel header={<></>} plugins={state.plugins}/>
} }
} }

@ -1,8 +1,8 @@
import { ViewPlugin } from '@remixproject/engine-web' import { ViewPlugin } from '@remixproject/engine-web'
import React from 'react' // eslint-disable-line import React from 'react' // eslint-disable-line
import ReactDOM from 'react-dom'
import {RemixUiPluginManager} from '@remix-ui/plugin-manager' // eslint-disable-line import {RemixUiPluginManager} from '@remix-ui/plugin-manager' // eslint-disable-line
import * as packageJson from '../../../../../package.json' import * as packageJson from '../../../../../package.json'
import { PluginViewWrapper } from '@remix-ui/helper'
const _paq = window._paq = window._paq || [] const _paq = window._paq = window._paq || []
const profile = { const profile = {
@ -31,6 +31,7 @@ class PluginManagerComponent extends ViewPlugin {
this.inactivePlugins = [] this.inactivePlugins = []
this.activeProfiles = this.appManager.actives this.activeProfiles = this.appManager.actives
this._paq = _paq this._paq = _paq
this.dispatch = null
this.listenOnEvent() this.listenOnEvent()
} }
@ -40,7 +41,7 @@ class PluginManagerComponent extends ViewPlugin {
* RemixAppManager * RemixAppManager
* @param {string} name name of Plugin * @param {string} name name of Plugin
*/ */
isActive (name) { isActive = (name) =>{
return this.appManager.actives.includes(name) return this.appManager.actives.includes(name)
} }
@ -49,7 +50,7 @@ class PluginManagerComponent extends ViewPlugin {
* RemixAppManager to enable plugin activation * RemixAppManager to enable plugin activation
* @param {string} name name of Plugin * @param {string} name name of Plugin
*/ */
activateP (name) { activateP = (name) => {
this.appManager.activatePlugin(name) this.appManager.activatePlugin(name)
_paq.push(['trackEvent', 'manager', 'activate', name]) _paq.push(['trackEvent', 'manager', 'activate', name])
} }
@ -60,7 +61,7 @@ class PluginManagerComponent extends ViewPlugin {
* @param {Profile} pluginName * @param {Profile} pluginName
* @returns {void} * @returns {void}
*/ */
async activateAndRegisterLocalPlugin (localPlugin) { activateAndRegisterLocalPlugin = async (localPlugin) => {
if (localPlugin) { if (localPlugin) {
this.engine.register(localPlugin) this.engine.register(localPlugin)
this.appManager.activatePlugin(localPlugin.profile.name) this.appManager.activatePlugin(localPlugin.profile.name)
@ -75,28 +76,33 @@ class PluginManagerComponent extends ViewPlugin {
* of the plugin * of the plugin
* @param {string} name name of Plugin * @param {string} name name of Plugin
*/ */
deactivateP (name) { deactivateP = (name) => {
this.call('manager', 'deactivatePlugin', name) this.call('manager', 'deactivatePlugin', name)
_paq.push(['trackEvent', 'manager', 'deactivate', name]) _paq.push(['trackEvent', 'manager', 'deactivate', name])
} }
onActivation () { setDispatch (dispatch) {
this.dispatch = dispatch
this.renderComponent() this.renderComponent()
} }
updateComponent(state){
return <RemixUiPluginManager
pluginComponent={state}/>
}
renderComponent () { renderComponent () {
ReactDOM.render( if(this.dispatch) this.dispatch({...this, activePlugins: this.activePlugins, inactivePlugins: this.inactivePlugins})
<RemixUiPluginManager
pluginComponent={this}
/>,
this.htmlElement)
} }
render () { render () {
return this.htmlElement return (
<div id='pluginManager'><PluginViewWrapper plugin={this} /></div>
);
} }
getAndFilterPlugins (filter) { getAndFilterPlugins = (filter) => {
this.filter = typeof filter === 'string' ? filter.toLowerCase() : this.filter this.filter = typeof filter === 'string' ? filter.toLowerCase() : this.filter
const isFiltered = (profile) => (profile.displayName ? profile.displayName : profile.name).toLowerCase().includes(this.filter) const isFiltered = (profile) => (profile.displayName ? profile.displayName : profile.name).toLowerCase().includes(this.filter)

@ -1,10 +1,10 @@
// eslint-disable-next-line no-use-before-define // eslint-disable-next-line no-use-before-define
import React from 'react' import React from 'react'
import ReactDOM from 'react-dom'
import { AbstractPanel } from './panel' import { AbstractPanel } from './panel'
import { RemixPluginPanel } from '@remix-ui/panel' import { RemixPluginPanel } from '@remix-ui/panel'
import packageJson from '../../../../../package.json' import packageJson from '../../../../../package.json'
import RemixUIPanelHeader from 'libs/remix-ui/panel/src/lib/plugins/panel-header' import RemixUIPanelHeader from 'libs/remix-ui/panel/src/lib/plugins/panel-header'
import { PluginViewWrapper } from '@remix-ui/helper'
// const csjs = require('csjs-inject') // const csjs = require('csjs-inject')
const sidePanel = { const sidePanel = {
@ -17,6 +17,7 @@ const sidePanel = {
export class SidePanel extends AbstractPanel { export class SidePanel extends AbstractPanel {
sideelement: any sideelement: any
dispatch: React.Dispatch<any> = () => {}
constructor() { constructor() {
super(sidePanel) super(sidePanel)
this.sideelement = document.createElement('section') this.sideelement = document.createElement('section')
@ -78,11 +79,23 @@ export class SidePanel extends AbstractPanel {
this.renderComponent() this.renderComponent()
} }
render() { setDispatch (dispatch: React.Dispatch<any>) {
return this.sideelement this.dispatch = dispatch
}
render() {
return (
<section className='panel plugin-manager'> <PluginViewWrapper plugin={this} /></section>
);
}
updateComponent(state: any) {
return <RemixPluginPanel header={<RemixUIPanelHeader plugins={state.plugins}></RemixUIPanelHeader>} plugins={state.plugins} />
} }
renderComponent() { renderComponent() {
ReactDOM.render(<RemixPluginPanel header={<RemixUIPanelHeader plugins={this.plugins}></RemixUIPanelHeader>} plugins={this.plugins} />, this.sideelement) this.dispatch({
plugins: this.plugins
})
} }
} }

@ -1,11 +1,11 @@
// eslint-disable-next-line no-use-before-define // eslint-disable-next-line no-use-before-define
import React from 'react' import React from 'react'
import ReactDOM from 'react-dom'
import packageJson from '../../../../../package.json' import packageJson from '../../../../../package.json'
import { Plugin } from '@remixproject/engine' import { Plugin } from '@remixproject/engine'
import { EventEmitter } from 'events' import { EventEmitter } from 'events'
import { IconRecord, RemixUiVerticalIconsPanel } from '@remix-ui/vertical-icons-panel' import { IconRecord, RemixUiVerticalIconsPanel } from '@remix-ui/vertical-icons-panel'
import { Profile } from '@remixproject/plugin-utils' import { Profile } from '@remixproject/plugin-utils'
import { PluginViewWrapper } from '@remix-ui/helper'
const profile = { const profile = {
name: 'menuicons', name: 'menuicons',
@ -20,6 +20,7 @@ export class VerticalIcons extends Plugin {
events: EventEmitter events: EventEmitter
htmlElement: HTMLDivElement htmlElement: HTMLDivElement
icons: Record<string, IconRecord> = {} icons: Record<string, IconRecord> = {}
dispatch: React.Dispatch<any> = () => {}
constructor () { constructor () {
super(profile) super(profile)
this.events = new EventEmitter() this.events = new EventEmitter()
@ -46,12 +47,15 @@ export class VerticalIcons extends Plugin {
...divived.filter((value) => { return !value.isRequired }) ...divived.filter((value) => { return !value.isRequired })
] ]
ReactDOM.render( this.dispatch({
<RemixUiVerticalIconsPanel verticalIconsPlugin: this,
verticalIconsPlugin={this} icons: sorted
icons={sorted} })
/>,
this.htmlElement) }
setDispatch (dispatch: React.Dispatch<any>) {
this.dispatch = dispatch
} }
onActivation () { onActivation () {
@ -107,7 +111,16 @@ export class VerticalIcons extends Plugin {
this.events.emit('toggleContent', name) this.events.emit('toggleContent', name)
} }
render () { updateComponent(state: any){
return this.htmlElement return <RemixUiVerticalIconsPanel
verticalIconsPlugin={state.verticalIconsPlugin}
icons={state.icons}
/>
}
render() {
return (
<div id='icon-panel'><PluginViewWrapper plugin={this} /></div>
);
} }
} }

@ -1,9 +1,9 @@
'use strict' 'use strict'
import React from 'react' // eslint-disable-line import React from 'react' // eslint-disable-line
import ReactDOM from 'react-dom'
import { EditorUI } from '@remix-ui/editor' // eslint-disable-line import { EditorUI } from '@remix-ui/editor' // eslint-disable-line
import { Plugin } from '@remixproject/engine' import { Plugin } from '@remixproject/engine'
import * as packageJson from '../../../../../package.json' import * as packageJson from '../../../../../package.json'
import { PluginViewWrapper } from '@remix-ui/helper'
const EventManager = require('../../lib/events') const EventManager = require('../../lib/events')
@ -61,10 +61,29 @@ class Editor extends Plugin {
// to be implemented by the react component // to be implemented by the react component
this.api = {} this.api = {}
this.dispatch = null
this.ref = null
}
setDispatch (dispatch) {
this.dispatch = dispatch
}
updateComponent(state) {
return <EditorUI
editorAPI={state.api}
themeType={state.currentThemeType}
currentFile={state.currentFile}
sourceAnnotationsPerFile={state.sourceAnnotationsPerFile}
markerPerFile={state.markerPerFile}
events={state.events}
plugin={state.plugin}
/>
} }
render () { render () {
if (this.el) return this.el
/* if (this.el) return this.el
this.el = document.createElement('div') this.el = document.createElement('div')
this.el.setAttribute('id', 'editorView') this.el.setAttribute('id', 'editorView')
@ -76,22 +95,34 @@ class Editor extends Plugin {
} }
} }
this.el.gotoLine = (line, column) => this.gotoLine(line, column || 0) this.el.gotoLine = (line, column) => this.gotoLine(line, column || 0)
this.el.getCursorPosition = () => this.getCursorPosition() this.el.getCursorPosition = () => this.getCursorPosition() */
return this.el
return <div ref={(element)=>{
this.ref = element
this.ref.currentContent = () => this.currentContent() // used by e2e test
this.ref.setCurrentContent = (value) => {
if (this.sessions[this.currentFile]) {
this.sessions[this.currentFile].setValue(value)
this._onChange(this.currentFile)
}
}
this.ref.gotoLine = (line, column) => this.gotoLine(line, column || 0)
this.ref.getCursorPosition = () => this.getCursorPosition()
}} id='editorView'>
<PluginViewWrapper plugin={this} />
</div>
} }
renderComponent () { renderComponent () {
ReactDOM.render( this.dispatch({
<EditorUI api: this.api,
editorAPI={this.api} currentThemeType: this.currentThemeType,
themeType={this.currentThemeType} currentFile: this.currentFile,
currentFile={this.currentFile} sourceAnnotationsPerFile: this.sourceAnnotationsPerFile,
sourceAnnotationsPerFile={this.sourceAnnotationsPerFile} markerPerFile: this.markerPerFile,
markerPerFile={this.markerPerFile} events: this.events,
events={this.events} plugin: this
plugin={this} })
/>
, this.el)
} }
triggerEvent (name, params) { triggerEvent (name, params) {

@ -4,7 +4,7 @@ import * as packageJson from '../../../../../package.json'
import Registry from '../state/registry' import Registry from '../state/registry'
import { EventEmitter } from 'events' import { EventEmitter } from 'events'
import { RemixAppManager } from '../../../../../libs/remix-ui/plugin-manager/src/types' import { RemixAppManager } from '../../../../../libs/remix-ui/plugin-manager/src/types'
import { fileChangedToastMsg } from '@remix-ui/helper' import { fileChangedToastMsg, storageFullMessage } from '@remix-ui/helper'
import helper from '../../lib/helper.js' import helper from '../../lib/helper.js'
/* /*
@ -722,8 +722,22 @@ class FileManager extends Plugin {
if ((input !== null) && (input !== undefined)) { if ((input !== null) && (input !== undefined)) {
const provider = this.fileProviderOf(currentFile) const provider = this.fileProviderOf(currentFile)
if (provider) { if (provider) {
await provider.set(currentFile, input) // use old content as default if save operation fails.
provider.get(currentFile, (error, oldContent) => {
provider.set(currentFile, input, (error) => {
if (error) {
if (error.message ) this.call('notification', 'toast',
error.message.indexOf(
'LocalStorage is full') !== -1 ? storageFullMessage()
: error.message
)
provider.set(currentFile, oldContent)
return console.error(error)
} else {
this.emit('fileSaved', currentFile) this.emit('fileSaved', currentFile)
}
})
})
} else { } else {
console.log('cannot save ' + currentFile + '. Does not belong to any explorer') console.log('cannot save ' + currentFile + '. Does not belong to any explorer')
} }

@ -2,7 +2,6 @@ import { ViewPlugin } from '@remixproject/engine-web'
import * as packageJson from '../../../../../package.json' import * as packageJson from '../../../../../package.json'
import React from 'react' // eslint-disable-line import React from 'react' // eslint-disable-line
import ReactDOM from 'react-dom'
import { FileSystemProvider } from '@remix-ui/workspace' // eslint-disable-line import { FileSystemProvider } from '@remix-ui/workspace' // eslint-disable-line
import Registry from '../state/registry' import Registry from '../state/registry'
import { RemixdHandle } from '../plugins/remixd-handle' import { RemixdHandle } from '../plugins/remixd-handle'
@ -58,18 +57,8 @@ module.exports = class Filepanel extends ViewPlugin {
this.currentWorkspaceMetadata = {} this.currentWorkspaceMetadata = {}
} }
onActivation () {
this.renderComponent()
}
render () { render () {
return this.el return <div id='fileExplorerView'><FileSystemProvider plugin={this} /></div>
}
renderComponent () {
ReactDOM.render(
<FileSystemProvider plugin={this} />
, this.el)
} }
/** /**

@ -1,8 +1,7 @@
import React from 'react' // eslint-disable-line import React from 'react' // eslint-disable-line
import ReactDOM from 'react-dom'
import { Plugin } from '@remixproject/engine' import { Plugin } from '@remixproject/engine'
import { TabsUI } from '@remix-ui/tabs' import { TabsUI } from '@remix-ui/tabs'
import { getPathIcon } from '@remix-ui/helper' import { PluginViewWrapper, getPathIcon } from '@remix-ui/helper'
const EventEmitter = require('events') const EventEmitter = require('events')
const profile = { const profile = {
@ -11,7 +10,6 @@ const profile = {
kind: 'other' kind: 'other'
} }
// @todo(#650) Merge this with MainPanel into one plugin
export class TabProxy extends Plugin { export class TabProxy extends Plugin {
constructor (fileManager, editor) { constructor (fileManager, editor) {
super(profile) super(profile)
@ -23,6 +21,7 @@ export class TabProxy extends Plugin {
this._handlers = {} this._handlers = {}
this.loadedTabs = [] this.loadedTabs = []
this.el = document.createElement('div') this.el = document.createElement('div')
this.dispatch = null
} }
onActivation () { onActivation () {
@ -286,6 +285,15 @@ export class TabProxy extends Plugin {
this.handlers[type] = fn this.handlers[type] = fn
} }
setDispatch (dispatch) {
this.dispatch = dispatch
this.renderComponent()
}
updateComponent(state) {
return <TabsUI tabs={state.loadedTabs} onSelect={state.onSelect} onClose={state.onClose} onZoomIn={state.onZoomIn} onZoomOut={state.onZoomOut} onReady={state.onReady} />
}
renderComponent () { renderComponent () {
const onSelect = (index) => { const onSelect = (index) => {
if (this.loadedTabs[index]) { if (this.loadedTabs[index]) {
@ -308,12 +316,17 @@ export class TabProxy extends Plugin {
const onReady = (api) => { this.tabsApi = api } const onReady = (api) => { this.tabsApi = api }
ReactDOM.render( this.dispatch({
<TabsUI tabs={this.loadedTabs} onSelect={onSelect} onClose={onClose} onZoomIn={onZoomIn} onZoomOut={onZoomOut} onReady={onReady} /> loadedTabs: this.loadedTabs,
, this.el) onSelect,
onClose,
onZoomIn,
onZoomOut,
onReady
})
} }
renderTabsbar () { renderTabsbar () {
return this.el return <div><PluginViewWrapper plugin={this} /></div>
} }
} }

@ -1,15 +1,16 @@
/* global Node, requestAnimationFrame */ // eslint-disable-line /* global Node, requestAnimationFrame */ // eslint-disable-line
import React from 'react' // eslint-disable-line import React from 'react' // eslint-disable-line
import ReactDOM from 'react-dom'
import { RemixUiTerminal } from '@remix-ui/terminal' // eslint-disable-line import { RemixUiTerminal } from '@remix-ui/terminal' // eslint-disable-line
import { Plugin } from '@remixproject/engine' import { Plugin } from '@remixproject/engine'
import * as packageJson from '../../../../../package.json' import * as packageJson from '../../../../../package.json'
import Registry from '../state/registry' import Registry from '../state/registry'
import { PluginViewWrapper } from '@remix-ui/helper'
const vm = require('vm') const vm = require('vm')
const EventManager = require('../../lib/events') const EventManager = require('../../lib/events')
import { CompilerImports } from '@remix-project/core-plugin' // eslint-disable-line import { CompilerImports } from '@remix-project/core-plugin' // eslint-disable-line
const KONSOLES = [] const KONSOLES = []
function register (api) { KONSOLES.push(api) } function register (api) { KONSOLES.push(api) }
@ -79,9 +80,12 @@ class Terminal extends Plugin {
this.call('menuicons', 'select', 'debugger') this.call('menuicons', 'select', 'debugger')
this.call('debugger', 'debug', hash) this.call('debugger', 'debug', hash)
}) })
this.dispatch = null
} }
onActivation () { onActivation() {
this.renderComponent() this.renderComponent()
} }
@ -100,19 +104,27 @@ class Terminal extends Plugin {
this.terminalApi.log(message) this.terminalApi.log(message)
} }
setDispatch(dispatch) {
this.dispatch = dispatch
}
render () { render () {
return this.element return <div id='terminal-view' className='panel' data-id='terminalContainer-view'><PluginViewWrapper plugin={this}/></div>
}
updateComponent(state) {
return <RemixUiTerminal
plugin={state.plugin}
onReady={state.onReady}
/>
} }
renderComponent () { renderComponent () {
const onReady = (api) => { this.terminalApi = api } const onReady = (api) => { this.terminalApi = api }
ReactDOM.render( this.dispatch({
<RemixUiTerminal plugin: this,
plugin={this} onReady: onReady
onReady={onReady} })
/>,
this.element
)
} }
scroll2bottom () { scroll2bottom () {

@ -1,10 +1,10 @@
import React from 'react' // eslint-disable-line import React from 'react' // eslint-disable-line
import { ViewPlugin } from '@remixproject/engine-web' import { ViewPlugin } from '@remixproject/engine-web'
import ReactDOM from 'react-dom'
import { EventEmitter } from 'events' import { EventEmitter } from 'events'
import {RemixUiStaticAnalyser} from '@remix-ui/static-analyser' // eslint-disable-line import {RemixUiStaticAnalyser} from '@remix-ui/static-analyser' // eslint-disable-line
import * as packageJson from '../../../../../package.json' import * as packageJson from '../../../../../package.json'
import Registry from '../state/registry' import Registry from '../state/registry'
import { PluginViewWrapper } from '@remix-ui/helper'
var EventManager = require('../../lib/events') var EventManager = require('../../lib/events')
@ -35,6 +35,7 @@ class AnalysisTab extends ViewPlugin {
offsetToLineColumnConverter: this.registry.get( offsetToLineColumnConverter: this.registry.get(
'offsettolinecolumnconverter').api 'offsettolinecolumnconverter').api
} }
this.dispatch = null
} }
async onActivation () { async onActivation () {
@ -43,33 +44,40 @@ class AnalysisTab extends ViewPlugin {
await this.call('manager', 'activatePlugin', 'solidity') await this.call('manager', 'activatePlugin', 'solidity')
} }
this.renderComponent() this.renderComponent()
this.event.register('staticAnaysisWarning', (count) => {
if (count > 0) {
this.emit('statusChanged', { key: count, title: `${count} warning${count === 1 ? '' : 's'}`, type: 'warning' })
} else if (count === 0) {
this.emit('statusChanged', { key: 'succeed', title: 'no warning', type: 'success' })
} else {
// count ==-1 no compilation result
this.emit('statusChanged', { key: 'none' })
}
})
}
setDispatch (dispatch) {
this.dispatch = dispatch
} }
render () { render () {
return this.element return <div id='staticAnalyserView'><PluginViewWrapper plugin={this} /></div>
}
updateComponent(state) {
return <RemixUiStaticAnalyser
registry={state.registry}
analysisModule={state.analysisModule}
event={state.event}
/>
} }
renderComponent () { renderComponent () {
ReactDOM.render( this.dispatch({
<RemixUiStaticAnalyser registry: this.registry,
registry={this.registry} analysisModule: this,
analysisModule={this} event: this.event
event={this.event} })
/>,
this.element,
() => {
this.event.register('staticAnaysisWarning', (count) => {
if (count > 0) {
this.emit('statusChanged', { key: count, title: `${count} warning${count === 1 ? '' : 's'}`, type: 'warning' })
} else if (count === 0) {
this.emit('statusChanged', { key: 'succeed', title: 'no warning', type: 'success' })
} else {
// count ==-1 no compilation result
this.emit('statusChanged', { key: 'none' })
}
})
}
)
} }
} }

@ -1,6 +1,5 @@
/* global */ /* global */
import React from 'react' // eslint-disable-line import React from 'react' // eslint-disable-line
import ReactDOM from 'react-dom'
import { SolidityCompiler } from '@remix-ui/solidity-compiler' // eslint-disable-line import { SolidityCompiler } from '@remix-ui/solidity-compiler' // eslint-disable-line
import { CompileTabLogic } from '@remix-ui/solidity-compiler' // eslint-disable-line import { CompileTabLogic } from '@remix-ui/solidity-compiler' // eslint-disable-line
import { CompilerApiMixin } from '@remixproject/solidity-compiler-plugin' // eslint-disable-line import { CompilerApiMixin } from '@remixproject/solidity-compiler-plugin' // eslint-disable-line
@ -42,9 +41,7 @@ class CompileTab extends CompilerApiMixin(ViewPlugin) { // implements ICompilerA
} }
renderComponent () { renderComponent () {
ReactDOM.render( // empty method, is a state update needed?
<SolidityCompiler api={this}/>
, this.el)
} }
onCurrentFileChanged () { onCurrentFileChanged () {
@ -72,9 +69,7 @@ class CompileTab extends CompilerApiMixin(ViewPlugin) { // implements ICompilerA
} }
render () { render () {
this.renderComponent() return <div id='compileTabView'><SolidityCompiler api={this}/></div>
return this.el
} }
async compileWithParameters (compilationTargets, settings) { async compileWithParameters (compilationTargets, settings) {

@ -3,7 +3,6 @@ import { DebuggerApiMixin } from '@remixproject/debugger-plugin' // eslint-disab
import { ViewPlugin } from '@remixproject/engine-web' import { ViewPlugin } from '@remixproject/engine-web'
import * as packageJson from '../../../../../package.json' import * as packageJson from '../../../../../package.json'
import React from 'react' // eslint-disable-line import React from 'react' // eslint-disable-line
import ReactDOM from 'react-dom'
import * as remixBleach from '../../lib/remixBleach' import * as remixBleach from '../../lib/remixBleach'
import { compilationFinishedToastMsg, compilingToastMsg, localCompilationToastMsg, notFoundToastMsg, sourceVerificationNotAvailableToastMsg } from '@remix-ui/helper' import { compilationFinishedToastMsg, compilingToastMsg, localCompilationToastMsg, notFoundToastMsg, sourceVerificationNotAvailableToastMsg } from '@remix-ui/helper'
const css = require('./styles/debugger-tab-styles') const css = require('./styles/debugger-tab-styles')
@ -51,9 +50,7 @@ export class DebuggerTab extends DebuggerApiMixin(ViewPlugin) {
this.on('fetchAndCompile', 'sourceVerificationNotAvailable', () => { this.on('fetchAndCompile', 'sourceVerificationNotAvailable', () => {
this.call('notification', 'toast', sourceVerificationNotAvailableToastMsg()) this.call('notification', 'toast', sourceVerificationNotAvailableToastMsg())
}) })
return <div className={css.debuggerTabView} id='debugView'><DebuggerUI debuggerAPI={this} /></div>
this.renderComponent()
return this.el
} }
showMessage (title, message) { showMessage (title, message) {
@ -68,9 +65,4 @@ export class DebuggerTab extends DebuggerApiMixin(ViewPlugin) {
} }
} }
renderComponent () {
ReactDOM.render(
<DebuggerUI debuggerAPI={this} />
, this.el)
}
} }

@ -1,9 +1,9 @@
import React from 'react' // eslint-disable-line import React from 'react' // eslint-disable-line
import { ViewPlugin } from '@remixproject/engine-web' import { ViewPlugin } from '@remixproject/engine-web'
import ReactDOM from 'react-dom'
import * as packageJson from '../../../../../package.json' import * as packageJson from '../../../../../package.json'
import { RemixUiSettings } from '@remix-ui/settings' //eslint-disable-line import { RemixUiSettings } from '@remix-ui/settings' //eslint-disable-line
import Registry from '../state/registry' import Registry from '../state/registry'
import { PluginViewWrapper } from '@remix-ui/helper'
const profile = { const profile = {
name: 'settings', name: 'settings',
@ -20,6 +20,15 @@ const profile = {
} }
module.exports = class SettingsTab extends ViewPlugin { module.exports = class SettingsTab extends ViewPlugin {
config: any = {}
editor: any
private _deps: {
themeModule: any // eslint-disable-line
}
element: HTMLDivElement
public useMatomoAnalytics: any
dispatch: React.Dispatch<any> = () => {}
constructor (config, editor) { constructor (config, editor) {
super(profile) super(profile)
this.config = config this.config = config
@ -32,24 +41,31 @@ module.exports = class SettingsTab extends ViewPlugin {
this.useMatomoAnalytics = null this.useMatomoAnalytics = null
} }
onActivation () { setDispatch (dispatch: React.Dispatch<any>) {
this.dispatch = dispatch
this.renderComponent() this.renderComponent()
} }
render () { render() {
return this.element return (
<div id='settingsTab'>
<PluginViewWrapper plugin={this} />
</div>
);
}
updateComponent(state: any){
return <RemixUiSettings
config={state.config}
editor={state.editor}
_deps={state._deps}
useMatomoAnalytics={state.useMatomoAnalytics}
themeModule = {state._deps.themeModule}
/>
} }
renderComponent () { renderComponent () {
ReactDOM.render( this.dispatch(this)
<RemixUiSettings
config = { this.config }
editor = { this.editor }
_deps = { this._deps }
useMatomoAnalytics = {this.useMatomoAnalytics}
/>,
this.element
)
} }
get (key) { get (key) {
@ -59,6 +75,8 @@ module.exports = class SettingsTab extends ViewPlugin {
updateMatomoAnalyticsChoice (isChecked) { updateMatomoAnalyticsChoice (isChecked) {
this.config.set('settings/matomo-analytics', isChecked) this.config.set('settings/matomo-analytics', isChecked)
this.useMatomoAnalytics = isChecked this.useMatomoAnalytics = isChecked
this.renderComponent() this.dispatch({
...this
})
} }
} }

@ -1,12 +1,12 @@
/* global */ /* global */
import React from 'react' // eslint-disable-line import React from 'react' // eslint-disable-line
import ReactDOM from 'react-dom'
import { SolidityUnitTesting } from '@remix-ui/solidity-unit-testing' // eslint-disable-line import { SolidityUnitTesting } from '@remix-ui/solidity-unit-testing' // eslint-disable-line
import { TestTabLogic } from '@remix-ui/solidity-unit-testing' // eslint-disable-line import { TestTabLogic } from '@remix-ui/solidity-unit-testing' // eslint-disable-line
import { ViewPlugin } from '@remixproject/engine-web' import { ViewPlugin } from '@remixproject/engine-web'
import helper from '../../lib/helper' import helper from '../../lib/helper'
import { canUseWorker, urlFromVersion } from '@remix-project/remix-solidity' import { canUseWorker, urlFromVersion } from '@remix-project/remix-solidity'
import { PluginViewWrapper } from '@remix-ui/helper'
var { UnitTestRunner, assertLibCode } = require('@remix-project/remix-tests') var { UnitTestRunner, assertLibCode } = require('@remix-project/remix-tests')
@ -34,6 +34,7 @@ module.exports = class TestTab extends ViewPlugin {
this.offsetToLineColumnConverter = offsetToLineColumnConverter this.offsetToLineColumnConverter = offsetToLineColumnConverter
this.allFilesInvolved = ['.deps/remix-tests/remix_tests.sol', '.deps/remix-tests/remix_accounts.sol'] this.allFilesInvolved = ['.deps/remix-tests/remix_tests.sol', '.deps/remix-tests/remix_accounts.sol']
this.element = document.createElement('div') this.element = document.createElement('div')
this.dispatch = null
} }
onActivationInternal () { onActivationInternal () {
@ -128,15 +129,25 @@ module.exports = class TestTab extends ViewPlugin {
}) })
} }
setDispatch (dispatch) {
this.dispatch = dispatch
this.renderComponent('tests')
}
render () { render () {
this.onActivationInternal() this.onActivationInternal()
this.renderComponent('tests') return <div><PluginViewWrapper plugin={this} /></div>
return this.element }
updateComponent(state) {
return <SolidityUnitTesting testTab={state.testTab} helper={state.helper} initialPath={state.testDirPath} />
} }
renderComponent (testDirPath) { renderComponent (testDirPath) {
ReactDOM.render( this.dispatch({
<SolidityUnitTesting testTab={this} helper={helper} initialPath={testDirPath} /> testTab: this,
, this.element) helper: this.helper,
testDirPath: testDirPath
})
} }
} }

@ -1,5 +1,4 @@
import React from 'react' // eslint-disable-line import React from 'react' // eslint-disable-line
import ReactDOM from 'react-dom'
import { RunTabUI } from '@remix-ui/run-tab' import { RunTabUI } from '@remix-ui/run-tab'
import { ViewPlugin } from '@remixproject/engine-web' import { ViewPlugin } from '@remixproject/engine-web'
import * as packageJson from '../../../../../package.json' import * as packageJson from '../../../../../package.json'
@ -40,9 +39,6 @@ export class RunTab extends ViewPlugin {
this.el = document.createElement('div') this.el = document.createElement('div')
} }
onActivation () {
this.renderComponent()
}
setupEvents () { setupEvents () {
this.blockchain.events.on('newTransaction', (tx, receipt) => { this.blockchain.events.on('newTransaction', (tx, receipt) => {
@ -86,14 +82,9 @@ export class RunTab extends ViewPlugin {
} }
render () { render () {
return this.el return <div><RunTabUI plugin={this} /></div>
} }
renderComponent () {
ReactDOM.render(
<RunTabUI plugin={this} />
, this.el)
}
onReady (api) { onReady (api) {
this.REACT_API = api this.REACT_API = api

@ -1,6 +1,5 @@
/* global */ /* global */
import React from 'react' // eslint-disable-line import React from 'react' // eslint-disable-line
import ReactDOM from 'react-dom'
import * as packageJson from '../../../../../../package.json' import * as packageJson from '../../../../../../package.json'
import { ViewPlugin } from '@remixproject/engine-web' import { ViewPlugin } from '@remixproject/engine-web'
import { RemixUiHomeTab } from '@remix-ui/home-tab' // eslint-disable-line import { RemixUiHomeTab } from '@remix-ui/home-tab' // eslint-disable-line
@ -31,15 +30,9 @@ export class LandingPage extends ViewPlugin {
} }
render () { render () {
this.renderComponent() return <div id='landingPageHomeContainer' className='remixui_homeContainer justify-content-between bg-light d-flex' data-id='landingPageHomeContainer'><RemixUiHomeTab
return this.el plugin={this}
/></div>
} }
renderComponent () {
ReactDOM.render(
<RemixUiHomeTab
plugin={this}
/>
, this.el)
}
} }

@ -33,9 +33,9 @@ import ('./app').then((AppComponent) => {
const appComponent = new AppComponent.default() const appComponent = new AppComponent.default()
appComponent.run().then(() => { appComponent.run().then(() => {
render( render(
<React.StrictMode> <>
<RemixApp app={appComponent} /> <RemixApp app={appComponent} />
</React.StrictMode>, </>,
document.getElementById('root') document.getElementById('root')
) )
}) })

@ -1,57 +0,0 @@
const allPrograms = [
{ ethers: 'The ethers.js library is a compact and complete JavaScript library for Ethereum.' },
{ remix: 'Ethereum IDE and tools for the web.' },
{ web3: 'The web3.js library is a collection of modules which contain specific functionality for the ethereum ecosystem.' },
{ swarmgw: 'This library can be used to upload/download files to Swarm via https://swarm-gateways.net/.' }
]
const allCommands = [
{ '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.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.' },
{ '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/' },
{ 'ethers.Contract': 'This API provides a graceful connection to a contract deployed on the blockchain, simplifying calling and querying its functions and handling all the binary protocol and conversion as necessarily.' },
{ 'ethers.HDNode': 'A Hierarchical Deterministic Wallet represents a large tree of private keys which can reliably be reproduced from an initial seed.' },
{ 'ethers.Interface': 'The Interface Object is a meta-class that accepts a Solidity (or compatible) Application Binary Interface (ABI) and populates functions to deal with encoding and decoding the parameters to pass in and results returned.' },
{ 'ethers.providers': 'A Provider abstracts a connection to the Ethereum blockchain, for issuing queries and sending state changing transactions.' },
{ 'ethers.SigningKey': 'The SigningKey interface provides an abstraction around the secp256k1 elliptic curve cryptography library.' },
{ 'ethers.utils': 'The utility functions exposed in both the ethers umbrella package and the ethers-utils.' },
{ 'ethers.utils.AbiCoder': 'Create a new ABI Coder object' },
{ '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.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).' },
{ 'web3.eth.ens': 'The web3.eth.ens functions let you interacting with ENS.' },
{ 'web3.eth.Iban': 'The web3.eth.Iban function lets convert Ethereum addresses from and to IBAN and BBAN.' },
{ 'web3.eth.net': 'Net module for interacting with network properties.' },
{ 'web3.eth.personal': 'Personal module for interacting with the Ethereum accounts.' },
{ 'web3.eth.subscribe': 'The web3.eth.subscribe function lets you subscribe to specific events in the blockchain.' },
{ 'web3.givenProvider': 'When using web3.js in an Ethereum compatible browser, it will set with the current native provider by that browser. Will return the given provider by the (browser) environment, otherwise null.' },
{ 'web3.modules': 'Contains the version of the web3 container object.' },
{ 'web3.providers': 'Contains the current available providers.' },
{ 'web3.shh': 'Shh module for interacting with the whisper protocol' },
{ 'web3.utils': 'This package provides utility functions for Ethereum dapps and other web3.js packages.' },
{ 'web3.version': 'Contains the version of the web3 container object.' },
{ '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.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 = {
allPrograms,
allCommands
}

@ -15,6 +15,8 @@ export class RemixEngine extends Engine {
if (name === 'hardhat') return { queueTimeout: 60000 * 4 } if (name === 'hardhat') return { queueTimeout: 60000 * 4 }
if (name === 'localPlugin') return { queueTimeout: 60000 * 4 } if (name === 'localPlugin') return { queueTimeout: 60000 * 4 }
if (name === 'notification') return { queueTimeout: 60000 * 4 } if (name === 'notification') return { queueTimeout: 60000 * 4 }
if (name === 'sourcify') return { queueTimeout: 60000 * 4 }
if (name === 'fetchAndCompile') return { queueTimeout: 60000 * 4 }
return { queueTimeout: 10000 } return { queueTimeout: 10000 }
} }

@ -108,7 +108,7 @@ export class FetchAndCompile extends Plugin {
language: data.metadata.language, language: data.metadata.language,
evmVersion: data.metadata.settings.evmVersion, evmVersion: data.metadata.settings.evmVersion,
optimize: data.metadata.settings.optimizer.enabled, optimize: data.metadata.settings.optimizer.enabled,
runs: data.metadata.settings.runs runs: data.metadata.settings.optimizer.runs
} }
try { try {
setTimeout(_ => this.emit('compiling', settings), 0) setTimeout(_ => this.emit('compiling', settings), 0)

@ -11,7 +11,6 @@ import { SolidityProxy, stateDecoder, localDecoder, InternalCallTree } from './s
/** /**
* Ethdebugger is a wrapper around a few classes that helps debugging a transaction * Ethdebugger is a wrapper around a few classes that helps debugging a transaction
* *
* - Web3Providers - define which environment (web3) the transaction will be retrieved from
* - TraceManager - Load / Analyze the trace and retrieve details of specific test * - TraceManager - Load / Analyze the trace and retrieve details of specific test
* - CodeManager - Retrieve loaded byte code and help to resolve AST item from vmtrace index * - CodeManager - Retrieve loaded byte code and help to resolve AST item from vmtrace index
* - SolidityProxy - Basically used to extract state variable from AST * - SolidityProxy - Basically used to extract state variable from AST

@ -68,7 +68,10 @@ export class Debugger {
try { try {
const address = this.debugger.traceManager.getCurrentCalledAddressAt(index) const address = this.debugger.traceManager.getCurrentCalledAddressAt(index)
const compilationResultForAddress = await this.compilationResult(address) const compilationResultForAddress = await this.compilationResult(address)
if (!compilationResultForAddress) return if (!compilationResultForAddress) {
this.event.trigger('newSourceLocation', [null])
return
}
this.debugger.callTree.sourceLocationTracker.getValidSourceLocationFromVMTraceIndex(address, index, compilationResultForAddress.data.contracts).then(async (rawLocation) => { this.debugger.callTree.sourceLocationTracker.getValidSourceLocationFromVMTraceIndex(address, index, compilationResultForAddress.data.contracts).then(async (rawLocation) => {
if (compilationResultForAddress && compilationResultForAddress.data) { if (compilationResultForAddress && compilationResultForAddress.data) {
@ -91,6 +94,7 @@ export class Debugger {
}) })
// }) // })
} catch (error) { } catch (error) {
this.event.trigger('newSourceLocation', [null])
return console.log(error) return console.log(error)
} }
} }

@ -51,15 +51,18 @@ export class DebuggerStepManager {
this.traceManager.buildCallPath(index).then((callsPath) => { this.traceManager.buildCallPath(index).then((callsPath) => {
this.currentCall = callsPath[callsPath.length - 1] this.currentCall = callsPath[callsPath.length - 1]
if (this.currentCall.reverted) { if (this.currentCall.reverted) {
const revertedReason = this.currentCall.outofgas ? 'outofgas' : '' const revertedReason = this.currentCall.outofgas ? 'outofgas' : 'reverted'
this.revertionPoint = this.currentCall.return this.revertionPoint = this.currentCall.return
return this.event.trigger('revertWarning', [revertedReason]) this.event.trigger('revertWarning', [revertedReason])
return
} }
for (let k = callsPath.length - 2; k >= 0; k--) { for (let k = callsPath.length - 2; k >= 0; k--) {
const parent = callsPath[k] const parent = callsPath[k]
if (!parent.reverted) continue if (parent.reverted) {
this.revertionPoint = parent.return this.revertionPoint = parent.return
this.event.trigger('revertWarning', ['parenthasthrown']) this.event.trigger('revertWarning', ['parenthasthrown'])
return
}
} }
this.event.trigger('revertWarning', ['']) this.event.trigger('revertWarning', [''])
}).catch((error) => { }).catch((error) => {

@ -133,7 +133,7 @@ export class StorageResolver {
resolve([{}, null]) resolve([{}, null])
} else { } else {
this.web3.debug.storageRangeAt( this.web3.debug.storageRangeAt(
tx.blockHash, tx.hash, tx.blockHash, tx.transactionIndex,
address, address,
start, start,
maxSize, maxSize,

@ -23,11 +23,6 @@
ui: uiHelper, ui: uiHelper,
compiler: compilerHelper compiler: compilerHelper
}, },
vm: {
Web3Providers: Web3Providers,
DummyProvider: DummyProvider,
Web3VMProvider: Web3VmProvider
},
Storage: Storage, Storage: Storage,
util: util, util: util,
execution: { execution: {

@ -56,18 +56,19 @@ export class EventsDecoder {
return eventsABI return eventsABI
} }
_event (hash, eventsABI) { _event (hash: string, eventsABI: Record<string, unknown>, contractName: string) {
for (const k in eventsABI) { const events = eventsABI[contractName]
if (eventsABI[k][hash]) { if (!events) return null
const event = eventsABI[k][hash]
for (const input of event.inputs) { if (events[hash]) {
if (input.type === 'function') { const event = events[hash]
input.type = 'bytes24' for (const input of event.inputs) {
input.baseType = 'bytes24' if (input.type === 'function') {
} input.type = 'bytes24'
input.baseType = 'bytes24'
} }
return event
} }
return event
} }
return null return null
} }
@ -94,7 +95,7 @@ export class EventsDecoder {
// [address, topics, mem] // [address, topics, mem]
const log = logs[i] const log = logs[i]
const topicId = log.topics[0] const topicId = log.topics[0]
const eventAbi = this._event(topicId.replace('0x', ''), eventsABI) const eventAbi = this._event(topicId.replace('0x', ''), eventsABI, contractName)
if (eventAbi) { if (eventAbi) {
const decodedlog = eventAbi.abi.parseLog(log) const decodedlog = eventAbi.abi.parseLog(log)
const decoded = {} const decoded = {}

@ -2,9 +2,6 @@ import { EventManager } from './eventManager'
import * as uiHelper from './helpers/uiHelper' import * as uiHelper from './helpers/uiHelper'
import * as compilerHelper from './helpers/compilerHelper' import * as compilerHelper from './helpers/compilerHelper'
import * as util from './util' import * as util from './util'
import { Web3Providers } from './web3Provider/web3Providers'
import { DummyProvider } from './web3Provider/dummyProvider'
import { Web3VmProvider } from './web3Provider/web3VmProvider'
import { Storage } from './storage' import { Storage } from './storage'
import { EventsDecoder } from './execution/eventsDecoder' import { EventsDecoder } from './execution/eventsDecoder'
import * as txExecution from './execution/txExecution' import * as txExecution from './execution/txExecution'
@ -18,6 +15,7 @@ import * as typeConversion from './execution/typeConversion'
import { TxRunnerVM } from './execution/txRunnerVM' import { TxRunnerVM } from './execution/txRunnerVM'
import { TxRunnerWeb3 } from './execution/txRunnerWeb3' import { TxRunnerWeb3 } from './execution/txRunnerWeb3'
import * as txResultHelper from './helpers/txResultHelper' import * as txResultHelper from './helpers/txResultHelper'
export { ConsoleLogs } from './helpers/hhconsoleSigs'
export { ICompilerApi, ConfigurationSettings } from './types/ICompilerApi' export { ICompilerApi, ConfigurationSettings } from './types/ICompilerApi'
export { QueryParams } from './query-params' export { QueryParams } from './query-params'
@ -26,11 +24,6 @@ const helpers = {
compiler: compilerHelper, compiler: compilerHelper,
txResultHelper txResultHelper
} }
const vm = {
Web3Providers: Web3Providers,
DummyProvider: DummyProvider,
Web3VMProvider: Web3VmProvider
}
const execution = { const execution = {
EventsDecoder: EventsDecoder, EventsDecoder: EventsDecoder,
txExecution: txExecution, txExecution: txExecution,
@ -44,4 +37,4 @@ const execution = {
LogsManager, LogsManager,
forkAt forkAt
} }
export { EventManager, helpers, vm, Storage, util, execution } export { EventManager, helpers, Storage, util, execution }

@ -1,55 +0,0 @@
export class DummyProvider {
eth
debug
providers
currentProvider
constructor () {
this.eth = {}
this.debug = {}
this.eth.getCode = (address, cb) => { return this.getCode(address, cb) }
this.eth.getTransaction = (hash, cb) => { return this.getTransaction(hash, cb) }
this.eth.getTransactionFromBlock = (blockNumber, txIndex, cb) => { return this.getTransactionFromBlock(blockNumber, txIndex, cb) }
this.eth.getBlockNumber = (cb) => { return this.getBlockNumber(cb) }
this.debug.traceTransaction = (hash, options, cb) => { return this.traceTransaction(hash, options, cb) }
this.debug.storageRangeAt = (blockNumber, txIndex, address, start, end, maxLength, cb) => { return this.storageRangeAt(blockNumber, txIndex, address, start, end, maxLength, cb) }
this.providers = { HttpProvider: function (url) {} }
this.currentProvider = { host: '' }
}
getCode (address, cb) {
cb(null, '')
}
setProvider (provider) {}
traceTransaction (txHash, options, cb) {
if (cb) {
cb(null, {})
}
return {}
}
storageRangeAt (blockNumber, txIndex, address, start, end, maxLength, cb) {
if (cb) {
cb(null, {})
}
return {}
}
getBlockNumber (cb) { cb(null, '') }
getTransaction (txHash, cb) {
if (cb) {
cb(null, {})
}
return {}
}
getTransactionFromBlock (blockNumber, txIndex, cb) {
if (cb) {
cb(null, {})
}
return {}
}
}

@ -1,38 +0,0 @@
import { Web3VmProvider } from './web3VmProvider'
import { loadWeb3, extendWeb3 } from '../init'
export class Web3Providers {
modes
constructor () {
this.modes = {}
}
addProvider (type, obj) {
if (type === 'INTERNAL') {
const web3 = loadWeb3()
this.addWeb3(type, web3)
} else if (type === 'vm') {
this.addVM(type, obj)
} else {
extendWeb3(obj)
this.addWeb3(type, obj)
}
}
get (type, cb) {
if (this.modes[type]) {
return cb(null, this.modes[type])
}
cb('error: this provider has not been setup (' + type + ')', null)
}
addWeb3 (type, web3) {
this.modes[type] = web3
}
addVM (type, vm) {
const vmProvider = new Web3VmProvider()
vmProvider.setVM(vm)
this.modes[type] = vmProvider
}
}

@ -1,12 +1,16 @@
import { hexListFromBNs, formatMemory } from '../util' import { util } from '@remix-project/remix-lib'
import { normalizeHexAddress } from '../helpers/uiHelper' const { hexListFromBNs, formatMemory } = util
import { ConsoleLogs } from '../helpers/hhconsoleSigs' import { helpers } from '@remix-project/remix-lib'
const { normalizeHexAddress } = helpers.ui
import { ConsoleLogs } from '@remix-project/remix-lib'
import { toChecksumAddress, BN, bufferToHex, Address } from 'ethereumjs-util' import { toChecksumAddress, BN, bufferToHex, Address } from 'ethereumjs-util'
import Web3 from 'web3' import Web3 from 'web3'
import { ethers } from 'ethers' import { ethers } from 'ethers'
import { VMContext } from './vm-context'
export class Web3VmProvider { export class VmProxy {
web3 vmContext: VMContext
web3: Web3
vm vm
vmTraces vmTraces
txs txs
@ -38,7 +42,8 @@ export class Web3VmProvider {
blocks blocks
latestBlockNumber latestBlockNumber
constructor () { constructor (vmContext: VMContext) {
this.vmContext = vmContext
this.web3 = new Web3() this.web3 = new Web3()
this.vm = null this.vm = null
this.vmTraces = {} this.vmTraces = {}
@ -66,15 +71,15 @@ export class Web3VmProvider {
this.lastProcessedStorageTxHash = {} this.lastProcessedStorageTxHash = {}
this.sha3Preimages = {} this.sha3Preimages = {}
// util // util
this.sha3 = (...args) => this.web3.utils.sha3(...args) this.sha3 = (...args) => this.web3.utils.sha3.apply(this, args)
this.toHex = (...args) => this.web3.utils.toHex(...args) this.toHex = (...args) => this.web3.utils.toHex.apply(this, args)
this.toAscii = (...args) => this.web3.utils.hexToAscii(...args) this.toAscii = (...args) => this.web3.utils.toAscii.apply(this, args)
this.fromAscii = (...args) => this.web3.utils.asciiToHex(...args) this.fromAscii = (...args) => this.web3.utils.fromAscii.apply(this, args)
this.fromDecimal = (...args) => this.web3.utils.numberToHex(...args) this.fromDecimal = (...args) => this.web3.utils.fromDecimal.apply(this, args)
this.fromWei = (...args) => this.web3.utils.fromWei(...args) this.fromWei = (...args) => this.web3.utils.fromWei.apply(this, args)
this.toWei = (...args) => this.web3.utils.toWei(...args) this.toWei = (...args) => this.web3.utils.toWei.apply(this, args)
this.toBigNumber = (...args) => this.web3.utils.toBN(...args) this.toBigNumber = (...args) => this.web3.utils.toBN.apply(this, args)
this.isAddress = (...args) => this.web3.utils.isAddress(...args) this.isAddress = (...args) => this.web3.utils.isAddress.apply(this, args)
this.utils = Web3.utils || [] this.utils = Web3.utils || []
this.txsMapBlock = {} this.txsMapBlock = {}
this.blocks = {} this.blocks = {}
@ -289,16 +294,22 @@ export class Web3VmProvider {
} }
} }
storageRangeAt (blockNumber, txIndex, address, start, maxLength, cb) { // txIndex is the hash in the case of the VM storageRangeAt (blockNumber, txIndex, address, start, maxLength, cb) {
// we don't use the range params here // we don't use the range params here
address = toChecksumAddress(address) address = toChecksumAddress(address)
let txHash
if (txIndex === 'latest') { if (txIndex === 'latest') {
txIndex = this.lastProcessedStorageTxHash[address] txHash = this.lastProcessedStorageTxHash[address]
} else {
const block = this.vmContext.blocks[blockNumber]
txHash = '0x' + block.transactions[txIndex].hash().toString('hex')
} }
if (this.storageCache[txIndex] && this.storageCache[txIndex][address]) { if (this.storageCache[txHash] && this.storageCache[txHash][address]) {
const storage = this.storageCache[txIndex][address] const storage = this.storageCache[txHash][address]
return cb(null, { return cb(null, {
storage: JSON.parse(JSON.stringify(storage)), storage: JSON.parse(JSON.stringify(storage)),
nextKey: null nextKey: null

@ -2,11 +2,15 @@
'use strict' 'use strict'
import Web3 from 'web3' import Web3 from 'web3'
import { rlp, keccak, bufferToHex } from 'ethereumjs-util' import { rlp, keccak, bufferToHex } from 'ethereumjs-util'
import { vm as remixLibVm, execution } from '@remix-project/remix-lib' import { execution } from '@remix-project/remix-lib'
const { LogsManager } = execution
import { VmProxy } from './VmProxy'
import VM from '@ethereumjs/vm' import VM from '@ethereumjs/vm'
import Common from '@ethereumjs/common' import Common from '@ethereumjs/common'
import StateManager from '@ethereumjs/vm/dist/state/stateManager' import StateManager from '@ethereumjs/vm/dist/state/stateManager'
import { StorageDump } from '@ethereumjs/vm/dist/state/interface' import { StorageDump } from '@ethereumjs/vm/dist/state/interface'
import { Block } from '@ethereumjs/block'
import { Transaction } from '@ethereumjs/tx'
/* /*
extend vm state manager and instanciate VM extend vm state manager and instanciate VM
@ -75,6 +79,13 @@ class StateManagerCommonStorageDump extends StateManager {
} }
} }
export type CurrentVm = {
vm: VM,
web3vm: VmProxy,
stateManager: StateManagerCommonStorageDump,
common: Common
}
/* /*
trigger contextChanged, web3EndpointChanged trigger contextChanged, web3EndpointChanged
*/ */
@ -82,15 +93,14 @@ export class VMContext {
currentFork: string currentFork: string
blockGasLimitDefault: number blockGasLimitDefault: number
blockGasLimit: number blockGasLimit: number
customNetWorks blocks: Record<string, Block>
blocks latestBlockNumber: string
latestBlockNumber blockByTxHash: Record<string, Block>
blockByTxHash txByHash: Record<string, Transaction>
txByHash currentVm: CurrentVm
currentVm web3vm: VmProxy
web3vm logsManager: any // LogsManager
logsManager exeResults: Record<string, Transaction>
exeResults
constructor (fork?) { constructor (fork?) {
this.blockGasLimitDefault = 4300000 this.blockGasLimitDefault = 4300000
@ -98,11 +108,11 @@ export class VMContext {
this.currentFork = fork || 'london' this.currentFork = fork || 'london'
this.currentVm = this.createVm(this.currentFork) this.currentVm = this.createVm(this.currentFork)
this.blocks = {} this.blocks = {}
this.latestBlockNumber = 0 this.latestBlockNumber = "0x0"
this.blockByTxHash = {} this.blockByTxHash = {}
this.txByHash = {} this.txByHash = {}
this.exeResults = {} this.exeResults = {}
this.logsManager = new execution.LogsManager() this.logsManager = new LogsManager()
} }
createVm (hardfork) { createVm (hardfork) {
@ -115,7 +125,9 @@ export class VMContext {
allowUnlimitedContractSize: true allowUnlimitedContractSize: true
}) })
const web3vm = new remixLibVm.Web3VMProvider() // VmProxy and VMContext are very intricated.
// VmProxy is used to track the EVM execution (to listen on opcode execution, in order for instance to generate the VM trace)
const web3vm = new VmProxy(this)
web3vm.setVM(vm) web3vm.setVM(vm)
return { vm, web3vm, stateManager, common } return { vm, web3vm, stateManager, common }
} }
@ -140,7 +152,7 @@ export class VMContext {
return this.currentVm return this.currentVm
} }
addBlock (block) { addBlock (block: Block) {
let blockNumber = '0x' + block.header.number.toString('hex') let blockNumber = '0x' + block.header.number.toString('hex')
if (blockNumber === '0x') { if (blockNumber === '0x') {
blockNumber = '0x0' blockNumber = '0x0'

@ -1,4 +1,4 @@
import React, { useEffect, useRef, useState } from 'react' import React, { useContext, useEffect, useRef, useState } from 'react'
import './style/remix-app.css' import './style/remix-app.css'
import { RemixUIMainPanel } from '@remix-ui/panel' import { RemixUIMainPanel } from '@remix-ui/panel'
import MatomoDialog from './components/modals/matomo' import MatomoDialog from './components/modals/matomo'
@ -8,6 +8,7 @@ import { AppProvider } from './context/provider'
import AppDialogs from './components/modals/dialogs' import AppDialogs from './components/modals/dialogs'
import DialogViewPlugin from './components/modals/dialogViewPlugin' import DialogViewPlugin from './components/modals/dialogViewPlugin'
import { AppContext } from './context/context' import { AppContext } from './context/context'
import { RemixUiVerticalIconsPanel } from '@remix-ui/vertical-icons-panel'
interface IRemixAppUi { interface IRemixAppUi {
app: any app: any
@ -17,31 +18,8 @@ const RemixApp = (props: IRemixAppUi) => {
const [appReady, setAppReady] = useState<boolean>(false) const [appReady, setAppReady] = useState<boolean>(false)
const [hideSidePanel, setHideSidePanel] = useState<boolean>(false) const [hideSidePanel, setHideSidePanel] = useState<boolean>(false)
const sidePanelRef = useRef(null) const sidePanelRef = useRef(null)
const mainPanelRef = useRef(null)
const iconPanelRef = useRef(null)
const hiddenPanelRef = useRef(null)
useEffect(() => { useEffect(() => {
if (sidePanelRef.current) {
if (props.app.sidePanel) {
sidePanelRef.current.appendChild(props.app.sidePanel.render())
}
}
if (mainPanelRef.current) {
if (props.app.mainview) {
mainPanelRef.current.appendChild(props.app.mainview.render())
}
}
if (iconPanelRef.current) {
if (props.app.menuicons) {
iconPanelRef.current.appendChild(props.app.menuicons.render())
}
}
if (hiddenPanelRef.current) {
if (props.app.hiddenPanel) {
hiddenPanelRef.current.appendChild(props.app.hiddenPanel.render())
}
}
async function activateApp () { async function activateApp () {
props.app.themeModule.initTheme(() => { props.app.themeModule.initTheme(() => {
setAppReady(true) setAppReady(true)
@ -72,13 +50,6 @@ const RemixApp = (props: IRemixAppUi) => {
}) })
} }
const components = {
iconPanel: <div ref={iconPanelRef} id="icon-panel" data-id="remixIdeIconPanel" className="iconpanel bg-light"></div>,
sidePanel: <div ref={sidePanelRef} id="side-panel" data-id="remixIdeSidePanel" className={`sidepanel border-right border-left ${hideSidePanel ? 'd-none' : ''}`}></div>,
mainPanel: <div ref={mainPanelRef} id="main-panel" data-id="remixIdeMainPanel" className='mainpanel'></div>,
hiddenPanel: <div ref={hiddenPanelRef}></div>
}
const value = { const value = {
settings: props.app.settings, settings: props.app.settings,
showMatamo: props.app.showMatamo, showMatamo: props.app.showMatamo,
@ -93,14 +64,14 @@ const RemixApp = (props: IRemixAppUi) => {
<MatomoDialog hide={!appReady}></MatomoDialog> <MatomoDialog hide={!appReady}></MatomoDialog>
<div className={`remixIDE ${appReady ? '' : 'd-none'}`} data-id="remixIDE"> <div className={`remixIDE ${appReady ? '' : 'd-none'}`} data-id="remixIDE">
{components.iconPanel} <div id="icon-panel" data-id="remixIdeIconPanel" className="iconpanel bg-light">{props.app.menuicons.render()}</div>
{components.sidePanel} <div ref={sidePanelRef} id="side-panel" data-id="remixIdeSidePanel" className={`sidepanel border-right border-left ${hideSidePanel ? 'd-none' : ''}`}>{props.app.sidePanel.render()}</div>
<DragBar minWidth={250} refObject={sidePanelRef} hidden={hideSidePanel} setHideStatus={setHideSidePanel}></DragBar> <DragBar minWidth={250} refObject={sidePanelRef} hidden={hideSidePanel} setHideStatus={setHideSidePanel}></DragBar>
<div id="main-panel" data-id="remixIdeMainPanel" className='mainpanel'> <div id="main-panel" data-id="remixIdeMainPanel" className='mainpanel'>
<RemixUIMainPanel Context={AppContext}></RemixUIMainPanel> <RemixUIMainPanel Context={AppContext}></RemixUIMainPanel>
</div> </div>
</div> </div>
{components.hiddenPanel} <div>{props.app.hiddenPanel.render()}</div>
<AppDialogs></AppDialogs> <AppDialogs></AppDialogs>
<DialogViewPlugin></DialogViewPlugin> <DialogViewPlugin></DialogViewPlugin>
</AppProvider> </AppProvider>

@ -19,4 +19,8 @@
.navigator { .navigator {
} }
.navigator:hover { .navigator:hover {
}
.cursorPointerRemixDebugger {
cursor: pointer;
} }

@ -65,11 +65,10 @@ export const ButtonNavigation = ({ stepOverBack, stepIntoBack, stepIntoForward,
<button className='btn btn-primary btn-sm navigator jumpButton fas fa-step-forward' id='jumpnextbreakpoint' data-id="buttonNavigatorJumpNextBreakpoint" title='Jump to the next breakpoint' onClick={() => { jumpNextBreakpoint && jumpNextBreakpoint() }} disabled={state.jumpNextBreakpointDisabled}></button> <button className='btn btn-primary btn-sm navigator jumpButton fas fa-step-forward' id='jumpnextbreakpoint' data-id="buttonNavigatorJumpNextBreakpoint" title='Jump to the next breakpoint' onClick={() => { jumpNextBreakpoint && jumpNextBreakpoint() }} disabled={state.jumpNextBreakpointDisabled}></button>
</div> </div>
<div id='reverted' style={{ display: revertedReason === '' ? 'none' : 'block' }}> <div id='reverted' style={{ display: revertedReason === '' ? 'none' : 'block' }}>
<button id='jumptoexception' title='Jump to exception' className='btn btn-danger btn-sm navigator button fas fa-exclamation-triangle' onClick={() => { jumpToException && jumpToException() }} disabled={state.jumpOutDisabled}> <span className='text-warning'>This call has reverted, state changes made during the call will be reverted.</span>
</button> <span className='text-warning' id='outofgas' style={{ display: revertedReason === 'outofgas' ? 'inline' : 'none' }}>This call will run out of gas.</span>
<span>State changes made during this call will be reverted.</span> <span className='text-warning' id='parenthasthrown' style={{ display: revertedReason === 'parenthasthrown' ? 'inline' : 'none' }}>The parent call will throw an exception</span>
<span id='outofgas' style={{ display: revertedReason === 'outofgas' ? 'inline' : 'none' }}>This call will run out of gas.</span> <div className='text-warning'>Click <u data-id="debugGoToRevert" className="cursorPointerRemixDebugger" role="button" onClick={() => { jumpToException && jumpToException() }}>here</u> to jump where the call reverted.</div>
<span id='parenthasthrown' style={{ display: revertedReason === 'parenthasthrown' ? 'inline' : 'none' }}>The parent call will throw an exception</span>
</div> </div>
</div> </div>
) )

@ -32,7 +32,8 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
toastMessage: '', toastMessage: '',
validationError: '', validationError: '',
txNumberIsEmpty: true, txNumberIsEmpty: true,
isLocalNodeUsed: false isLocalNodeUsed: false,
sourceLocationStatus: ''
}) })
useEffect(() => { useEffect(() => {
@ -87,7 +88,13 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
}) })
debuggerInstance.event.register('newSourceLocation', async (lineColumnPos, rawLocation, generatedSources, address) => { debuggerInstance.event.register('newSourceLocation', async (lineColumnPos, rawLocation, generatedSources, address) => {
if (!lineColumnPos) return if (!lineColumnPos) {
await debuggerModule.discardHighlight()
setState(prevState => {
return { ...prevState, sourceLocationStatus: 'Source location not available.' }
})
return
}
const contracts = await debuggerModule.fetchContractAndCompile( const contracts = await debuggerModule.fetchContractAndCompile(
address || currentReceipt.contractAddress || currentReceipt.to, address || currentReceipt.contractAddress || currentReceipt.to,
currentReceipt) currentReceipt)
@ -113,6 +120,9 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
} }
} }
if (path) { if (path) {
setState(prevState => {
return { ...prevState, sourceLocationStatus: '' }
})
await debuggerModule.discardHighlight() await debuggerModule.discardHighlight()
await debuggerModule.highlight(lineColumnPos, path) await debuggerModule.highlight(lineColumnPos, path)
} }
@ -138,6 +148,12 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
const unloadRequested = (blockNumber, txIndex, tx) => { const unloadRequested = (blockNumber, txIndex, tx) => {
unLoad() unLoad()
setState(prevState => {
return {
...prevState,
sourceLocationStatus: ''
}
})
} }
const unLoad = () => { const unLoad = () => {
@ -168,7 +184,8 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
setState(prevState => { setState(prevState => {
return { return {
...prevState, ...prevState,
txNumber: txNumber txNumber: txNumber,
sourceLocationStatus: ''
} }
}) })
if (!isValidHash(txNumber)) { if (!isValidHash(txNumber)) {
@ -266,7 +283,8 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
return { return {
...prevState, ...prevState,
validationError: '', validationError: '',
txNumber: txHash txNumber: txHash,
sourceLocationStatus: ''
} }
}) })
startDebugging(null, txHash, null, web3) startDebugging(null, txHash, null, web3)
@ -315,6 +333,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
{ state.validationError && <span className="w-100 py-1 text-danger validationError">{state.validationError}</span> } { state.validationError && <span className="w-100 py-1 text-danger validationError">{state.validationError}</span> }
</div> </div>
<TxBrowser requestDebug={ requestDebug } unloadRequested={ unloadRequested } updateTxNumberFlag={ updateTxNumberFlag } transactionNumber={ state.txNumber } debugging={ state.debugging } /> <TxBrowser requestDebug={ requestDebug } unloadRequested={ unloadRequested } updateTxNumberFlag={ updateTxNumberFlag } transactionNumber={ state.txNumber } debugging={ state.debugging } />
{ state.debugging && state.sourceLocationStatus && <div className="text-warning"><i className="fas fa-exclamation-triangle" aria-hidden="true"></i> {state.sourceLocationStatus}</div> }
{ state.debugging && <StepManager stepManager={ stepManager } /> } { state.debugging && <StepManager stepManager={ stepManager } /> }
{ state.debugging && <VmDebuggerHead vmDebugger={ vmDebugger } /> } { state.debugging && <VmDebuggerHead vmDebugger={ vmDebugger } /> }
</div> </div>

@ -1,10 +1,12 @@
import React, { useState, useEffect } from 'react' // eslint-disable-line import React, { useState, useEffect, useRef } from 'react' // eslint-disable-line
export const Slider = ({ jumpTo, sliderValue, traceLength }) => { export const Slider = ({ jumpTo, sliderValue, traceLength }) => {
const [state, setState] = useState({ const [state, setState] = useState({
currentValue: 0 currentValue: 0
}) })
const onChangeId = useRef(null)
useEffect(() => { useEffect(() => {
setValue(sliderValue) setValue(sliderValue)
}, [sliderValue]) }, [sliderValue])
@ -18,9 +20,16 @@ export const Slider = ({ jumpTo, sliderValue, traceLength }) => {
} }
const handleChange = (e) => { const handleChange = (e) => {
const value = parseInt(e.target.value) if (onChangeId.current) {
window.clearTimeout(onChangeId.current)
setValue(value) }
((value) => {
onChangeId.current = setTimeout(() => {
console.log(value)
value = parseInt(value)
setValue(value)
}, 100)
})(e.target.value)
} }
return ( return (

@ -125,7 +125,7 @@ export const AssemblyItems = ({ registerEvent }) => {
<div className="border rounded px-1 mt-1 bg-light"> <div className="border rounded px-1 mt-1 bg-light">
<div className='dropdownpanel'> <div className='dropdownpanel'>
<div className='dropdowncontent'> <div className='dropdowncontent'>
<div className="pl-2 my-1 small instructions" id='asmitems' ref={asmItemsRef}> <div className="pl-2 my-1 small instructions" data-id="asmitems" id='asmitems' ref={asmItemsRef}>
{ {
assemblyItems.display.map((item, i) => { assemblyItems.display.map((item, i) => {
return <div className="px-1" key={i} ref={ref => { refs.current[i] = ref }}><span>{item}</span></div> return <div className="px-1" key={i} ref={ref => { refs.current[i] = ref }}><span>{item}</span></div>

@ -1,2 +1,3 @@
export * from './lib/remix-ui-helper' export * from './lib/remix-ui-helper'
export * from './lib/helper-components' export * from './lib/helper-components'
export * from './lib/components/PluginViewWrapper'

@ -0,0 +1,24 @@
import React from "react"
import { useEffect, useState } from "react"
interface IPluginViewWrapperProps {
plugin: any
}
export const PluginViewWrapper = (props: IPluginViewWrapperProps) => {
const [state, setState] = useState<any>(null)
useEffect(() => {
if(props.plugin.setDispatch){
props.plugin.setDispatch(setState)
}
}, [])
return (
<>{state?
<>{props.plugin.updateComponent(state)}</>
:<></>
}</>
)
}

@ -69,3 +69,12 @@ export const envChangeNotification = (env: { context: string, fork: string }, fr
</span> </span>
</div> </div>
) )
export const storageFullMessage = () => (
<div>
<i className="fas fa-exclamation-triangle text-danger mr-1"></i>
<span className="font-weight-bold">
<span>Cannot save this file due to full LocalStorage. Backup existing files and free up some space.</span>
</span>
</div>
)

@ -297,7 +297,7 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => {
</div> </div>
<div className="row mx-2 mr-4" data-id="landingPageHpSections"> <div className="row mx-2 mr-4" data-id="landingPageHpSections">
<div className="ml-3"> <div className="ml-3">
<div className="mb-5"> <div className="mb-3">
<h4>Featured Plugins</h4> <h4>Featured Plugins</h4>
<div className="d-flex flex-row pt-2"> <div className="d-flex flex-row pt-2">
<ThemeContext.Provider value={ state.themeQuality }> <ThemeContext.Provider value={ state.themeQuality }>

@ -10,6 +10,7 @@ const RemixUIPanelPlugin = (props: panelPLuginProps, panelRef: any) => {
const localRef = useRef<HTMLDivElement>(null) const localRef = useRef<HTMLDivElement>(null)
const [view, setView] = useState<JSX.Element | HTMLDivElement>() const [view, setView] = useState<JSX.Element | HTMLDivElement>()
useEffect(() => { useEffect(() => {
const ref:any = panelRef || localRef const ref:any = panelRef || localRef
if (ref.current) { if (ref.current) {
if (props.pluginRecord.view) { if (props.pluginRecord.view) {

@ -76,11 +76,6 @@ iframe {
display: block; display: block;
} }
.pluginsContainer {
height: 100%;
overflow-y: hidden;
}
#editorView { #editorView {
height: 100%; height: 100%;
width: 100%; width: 100%;

@ -8,7 +8,6 @@ export const allPrograms = [
export const allCommands = [ export const allCommands = [
{ 'remix.execute(filepath)': 'Run the script specified by file path. If filepath is empty, script currently displayed in the editor is executed.' }, { '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.exeCurrent()': 'Run the script currently displayed in the editor.' },
// { 'remix.help()': 'Display this help message.' },
{ 'remix.loadgist(id)': 'Load a gist in the file explorer.' }, { '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.loadurl(url)': 'Load the given url in the file explorer. The url can be of type github, swarm or ipfs.' },

@ -53,6 +53,9 @@ element.style {
.remix_ui_terminal_block > pre { .remix_ui_terminal_block > pre {
max-height : 200px; max-height : 200px;
} }
.remix_ui_terminal_welcome {
font-weight: bold;
}
.remix_ui_terminal_cli { .remix_ui_terminal_cli {
white-space : nowrap; white-space : nowrap;
line-height : 1.7em; line-height : 1.7em;

@ -382,7 +382,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
const handleAutoComplete = () => ( const handleAutoComplete = () => (
<div <div
className='remix_ui_terminal_popup bg-light ml-4 p-2 position-absolute text-left ' className='remix_ui_terminal_popup bg-light ml-4 p-2 position-absolute text-left '
style={{ display: (autoCompletState.showSuggestions && autoCompletState.userInput !== '' && (autoCompletState.userInput.length > 2)) && autoCompletState.data._options.length > 0 ? 'block' : 'none' }} style={{ display: (autoCompletState.showSuggestions && autoCompletState.userInput !== '' && (autoCompletState.userInput.length > 0)) && autoCompletState.data._options.length > 0 ? 'block' : 'none' }}
> >
<div> <div>
{autoCompletState.data._options.map((item, index) => { {autoCompletState.data._options.map((item, index) => {

@ -3,7 +3,7 @@ import React from 'react' // eslint-disable-line
const TerminalWelcomeMessage = ({ packageJson }) => { const TerminalWelcomeMessage = ({ packageJson }) => {
return ( return (
<div className="remix_ui_terminal_block px-4 " data-id="block_null"> <div className="remix_ui_terminal_block px-4 " data-id="block_null">
<div> - Welcome to Remix {packageJson} - </div><br /> <div className="remix_ui_terminal_welcome"> Welcome to Remix {packageJson} </div><br />
<div>You can use this terminal to: </div> <div>You can use this terminal to: </div>
<ul className='ml-0 mr-4'> <ul className='ml-0 mr-4'>
<li>Check transactions details and start debugging.</li> <li>Check transactions details and start debugging.</li>
@ -16,12 +16,14 @@ const TerminalWelcomeMessage = ({ packageJson }) => {
<i> - Right click on a JavaScript file in the file explorer and then click \`Run\` </i> <i> - Right click on a JavaScript file in the file explorer and then click \`Run\` </i>
</li> </li>
</ul> </ul>
<div>The following libraries are accessible:</div> <div>The following libraries are accessible:</div>
<ul className='ml-0 mr-4'> <ul className='ml-0 mr-4'>
<li><a target="_blank" href="https://web3js.readthedocs.io/en/1.0/">web3 version 1.5.2</a></li> <li><a target="_blank" href="https://web3js.readthedocs.io/en/1.0/">web3 version 1.5.2</a></li>
<li><a target="_blank" href="https://docs.ethers.io">ethers.js</a> </li> <li><a target="_blank" href="https://docs.ethers.io">ethers.js</a> </li>
<li>remix (run remix.help() for more info)</li> <li>remix</li>
</ul> </ul>
<div>Type the library name to see available commands.</div>
</div> </div>
) )
} }

@ -111,6 +111,7 @@
flex-basis: 510px; flex-basis: 510px;
flex-grow: 2; flex-grow: 2;
text-align: center; text-align: center;
margin-top: -6px;
/* border-bottom: 3px solid #3f4455; */ /* border-bottom: 3px solid #3f4455; */
} }
.remixui_scrollbar::-webkit-scrollbar { /* Chrome, Safari and other Webkit browsers*/ .remixui_scrollbar::-webkit-scrollbar { /* Chrome, Safari and other Webkit browsers*/

@ -149,13 +149,13 @@
"@ethereumjs/tx": "^3.3.2", "@ethereumjs/tx": "^3.3.2",
"@ethereumjs/vm": "^5.5.3", "@ethereumjs/vm": "^5.5.3",
"@monaco-editor/react": "^4.3.1", "@monaco-editor/react": "^4.3.1",
"@remixproject/engine": "^0.3.26", "@remixproject/engine": "^0.3.28",
"@remixproject/engine-web": "^0.3.26", "@remixproject/engine-web": "^0.3.28",
"@remixproject/plugin": "^0.3.26", "@remixproject/plugin": "^0.3.28",
"@remixproject/plugin-api": "^0.3.26", "@remixproject/plugin-api": "^0.3.28",
"@remixproject/plugin-utils": "^0.3.26", "@remixproject/plugin-utils": "^0.3.28",
"@remixproject/plugin-webview": "^0.3.26", "@remixproject/plugin-webview": "^0.3.28",
"@remixproject/plugin-ws": "^0.3.26", "@remixproject/plugin-ws": "^0.3.28",
"ansi-gray": "^0.1.1", "ansi-gray": "^0.1.1",
"async": "^2.6.2", "async": "^2.6.2",
"axios": ">=0.21.1", "axios": ">=0.21.1",

Loading…
Cancel
Save