Merge branch 'master' of https://github.com/ethereum/remix-project into multiselectdelete

pull/1184/head
filip mertens 3 years ago
commit 4e4ec8d3e5
  1. 7
      .circleci/config.yml
  2. 8
      apps/debugger/src/app/debugger-api.ts
  3. 19
      apps/remix-ide-e2e/src/commands/currentWorkspaceIs.ts
  4. 9
      apps/remix-ide-e2e/src/helpers/init.ts
  5. 2
      apps/remix-ide-e2e/src/tests/ballot.test.ts
  6. 2
      apps/remix-ide-e2e/src/tests/ballot_0_4_11.spec.ts
  7. 64
      apps/remix-ide-e2e/src/tests/debugger.spec.ts
  8. 4
      apps/remix-ide-e2e/src/tests/gist.spec.ts
  9. 14
      apps/remix-ide-e2e/src/tests/recorder.spec.ts
  10. 2
      apps/remix-ide-e2e/src/tests/terminal.test.ts
  11. 15
      apps/remix-ide-e2e/src/tests/transactionExecution.spec.ts
  12. 12
      apps/remix-ide-e2e/src/tests/url.spec.ts
  13. 1
      apps/remix-ide-e2e/src/types/index.d.ts
  14. 27
      apps/remix-ide/src/app/files/remixd-handle.js
  15. 50
      apps/remix-ide/src/app/panels/file-panel.js
  16. 14
      apps/remix-ide/src/app/tabs/compile-tab.js
  17. 2
      apps/remix-ide/src/app/tabs/compileTab/compileTab.js
  18. 5
      apps/remix-ide/src/app/tabs/compileTab/compilerContainer.js
  19. 31
      apps/remix-ide/src/app/tabs/runTab/settings.js
  20. 97
      apps/remix-ide/src/app/tabs/test-tab.js
  21. 8
      apps/remix-ide/src/app/tabs/testTab/testTab.js
  22. 10
      apps/remix-ide/src/blockchain/blockchain.js
  23. 21
      apps/remix-ide/src/blockchain/execution-context.js
  24. 12
      apps/remix-ide/src/blockchain/providers/vm.js
  25. 8
      apps/remix-ide/src/lib/helper.js
  26. 2
      apps/remix-ide/src/remixAppManager.js
  27. 16
      libs/remix-analyzer/package.json
  28. 18
      libs/remix-astwalker/package.json
  29. 16
      libs/remix-debug/package.json
  30. 4
      libs/remix-debug/src/Ethdebugger.ts
  31. 6
      libs/remix-debug/src/code/codeManager.ts
  32. 6
      libs/remix-debug/src/code/codeResolver.ts
  33. 40
      libs/remix-debug/src/code/codeUtils.ts
  34. 12
      libs/remix-debug/src/debugger/VmDebugger.ts
  35. 3
      libs/remix-debug/src/debugger/debugger.ts
  36. 3
      libs/remix-debug/src/init.ts
  37. 23
      libs/remix-debug/src/solidity-decoder/astHelper.ts
  38. 8
      libs/remix-debug/src/solidity-decoder/decodeInfo.ts
  39. 6
      libs/remix-debug/src/trace/traceHelper.ts
  40. 6
      libs/remix-debug/src/trace/traceManager.ts
  41. 10
      libs/remix-lib/package.json
  42. 48
      libs/remix-lib/src/execution/txRunnerVM.ts
  43. 3
      libs/remix-lib/src/execution/txRunnerWeb3.ts
  44. 3
      libs/remix-lib/src/init.ts
  45. 14
      libs/remix-simulator/package.json
  46. 2
      libs/remix-simulator/src/methods/transactions.ts
  47. 6
      libs/remix-simulator/src/provider.ts
  48. 31
      libs/remix-simulator/src/vm-context.ts
  49. 2
      libs/remix-simulator/test/accounts.ts
  50. 4
      libs/remix-simulator/test/blocks.ts
  51. 2
      libs/remix-simulator/test/misc.ts
  52. 16
      libs/remix-solidity/package.json
  53. 20
      libs/remix-tests/package.json
  54. 2
      libs/remix-tests/src/deployer.ts
  55. 13
      libs/remix-tests/src/runTestFiles.ts
  56. 4
      libs/remix-tests/src/testRunner.ts
  57. 7
      libs/remix-ui/debugger-ui/src/lib/debugger-ui.tsx
  58. 138
      libs/remix-ui/file-explorer/src/lib/actions/fileSystem.ts
  59. 2
      libs/remix-ui/file-explorer/src/lib/file-explorer.tsx
  60. 19
      libs/remix-ui/static-analyser/src/lib/remix-ui-static-analyser.tsx
  61. 26
      libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx
  62. 4
      libs/remix-url-resolver/package.json
  63. 2
      libs/remixd/package.json
  64. 23
      libs/remixd/src/bin/remixd.ts
  65. 8
      libs/remixd/src/websocket.ts
  66. 5272
      package-lock.json
  67. 15
      package.json

@ -238,8 +238,8 @@ jobs:
steps:
- checkout
- run: npm install
- run: npx nx build remix-ide --with-deps
- run: npm run downloadsolc_assets
- run: npx nx build remix-ide --with-deps
- run:
name: Deploy
command: |
@ -267,6 +267,7 @@ jobs:
- checkout
- setup_remote_docker
- run: npm install
- run: npm run downloadsolc_assets
- run: npx nx build remix-ide --with-deps
- run: ./apps/remix-ide/ci/copy_resources.sh
- run: ./apps/remix-ide/ci/publishIpfs
@ -291,8 +292,8 @@ jobs:
steps:
- checkout
- run: npm install
- run: npx nx build remix-ide --with-deps
- run: npm run downloadsolc_assets
- run: npx nx build remix-ide --with-deps
- run:
name: Deploy
command: |
@ -320,8 +321,8 @@ jobs:
- checkout
- run: npm install
- run: npm run build:libs
- run: npm run build
- run: npm run downloadsolc_assets
- run: npm run build
- run:
name: Deploy
command: |

@ -74,7 +74,10 @@ export const DebuggerApiMixin = (Base) => class extends Base {
const targetAddress = target || receipt.contractAddress || receipt.to
const codeAtAddress = await this._web3.eth.getCode(targetAddress)
const output = await this.call('fetchAndCompile', 'resolve', targetAddress, codeAtAddress, 'browser/.debug')
return new CompilerAbstract(output.languageversion, output.data, output.source)
if (output) {
return new CompilerAbstract(output.languageversion, output.data, output.source)
}
return null
}
async getDebugWeb3 () {
@ -108,7 +111,8 @@ export const DebuggerApiMixin = (Base) => class extends Base {
}
return null
},
debugWithGeneratedSources: false
debugWithGeneratedSources: false,
fork: 'berlin'
})
return await debug.debugger.traceManager.getTrace(hash)
}

@ -0,0 +1,19 @@
import { NightwatchBrowser } from 'nightwatch'
import EventEmitter from 'events'
class CurrentWorkspaceIs extends EventEmitter {
command (this: NightwatchBrowser, name: string): NightwatchBrowser {
this.api
.execute(function () {
const el = document.querySelector('select[data-id="workspacesSelect"]') as HTMLSelectElement
return el.value
}, [], (result) => {
console.log(result)
this.api.assert.equal(result.value, name)
this.emit('complete')
})
return this
}
}
module.exports = CurrentWorkspaceIs

@ -2,18 +2,11 @@ import { NightwatchBrowser } from 'nightwatch'
require('dotenv').config()
export default function (browser: NightwatchBrowser, callback: VoidFunction, url?: string, preloadPlugins = true, closeWorkspaceAlert = true): void {
export default function (browser: NightwatchBrowser, callback: VoidFunction, url?: string, preloadPlugins = true): void {
browser
.url(url || 'http://127.0.0.1:8080')
.pause(5000)
.switchBrowserTab(0)
.perform((done) => {
if (closeWorkspaceAlert) {
browser.waitForElementVisible('*[data-id="modalDialogModalBody"]', 60000)
.modalFooterOKClick()
}
done()
})
.fullscreenWindow(() => {
if (preloadPlugins) {
initModules(browser, () => {

@ -49,7 +49,7 @@ module.exports = {
'Debug Ballot / delegate': function (browser: NightwatchBrowser) {
browser.pause(500)
.click('*[data-id="txLoggerDebugButton0x41fab8ea5b1d9fba5e0a6545ca1a2d62fff518578802c033c2b9a031a01c31b3"]')
.click('*[data-id="txLoggerDebugButton0xf88bc0ac0761f78d8c883b32550c68dadcdb095595c30e1a1b7c583e5e958dcb"]')
.waitForElementVisible('*[data-id="buttonNavigatorJumpPreviousBreakpoint"]')
.click('*[data-id="buttonNavigatorJumpPreviousBreakpoint"]')
.pause(2000)

@ -45,7 +45,7 @@ module.exports = {
'Debug Ballot / delegate': function (browser: NightwatchBrowser) {
browser.pause(500)
.click('*[data-id="txLoggerDebugButton0x41fab8ea5b1d9fba5e0a6545ca1a2d62fff518578802c033c2b9a031a01c31b3"]')
.click('*[data-id="txLoggerDebugButton0xf88bc0ac0761f78d8c883b32550c68dadcdb095595c30e1a1b7c583e5e958dcb"]')
.pause(2000)
.waitForElementVisible('*[data-id="buttonNavigatorJumpPreviousBreakpoint"]')
.click('*[data-id="buttonNavigatorJumpPreviousBreakpoint"]')

@ -178,7 +178,7 @@ module.exports = {
.click('*[data-id="debuggerTransactionStartButton"]') // start debugging
.pause(2000)
.getEditorValue((content) => {
browser.assert.ok(content.indexOf('if slt(sub(dataEnd, headStart), 32) { revert(0, 0) }') !== -1, 'current displayed content is not a generated source')
browser.assert.ok(content.indexOf('if slt(sub(dataEnd, headStart), 32)') !== -1, 'current displayed content is not a generated source')
})
.click('*[data-id="debuggerTransactionStartButton"]')
},
@ -207,6 +207,28 @@ module.exports = {
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)
},
'Should start debugging using remix debug nodes (rinkeby)': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('solidity')
.setSolidityCompilerVersion('soljson-v0.8.4+commit.c7e474f2.js')
.addFile('useDebugNodes.sol', sources[5]['useDebugNodes.sol']) // compile contract
.clickLaunchIcon('udapp')
.click('*[data-id="settingsWeb3Mode"]') // select web3 provider with debug nodes URL
.clearValue('*[data-id="modalDialogCustomPromptText"]')
.setValue('*[data-id="modalDialogCustomPromptText"]', 'https://remix-rinkeby.ethdevops.io')
.modalFooterOKClick()
.waitForElementPresent('*[title="Deploy - transact (not payable)"]', 65000) // wait for the compilation to succeed
.clickLaunchIcon('debugger')
.clearValue('*[data-id="debuggerTransactionInput"]')
.setValue('*[data-id="debuggerTransactionInput"]', '0x156dbf7d0f9b435dd900cfc8f3264d523dd25733418ddbea1ce53e294f421013')
.click('*[data-id="debugGeneratedSourcesLabel"]') // unselect debug with generated sources
.click('*[data-id="debuggerTransactionStartButton"]')
.waitForElementVisible('*[data-id="solidityLocals"]', 60000)
.pause(10000)
.checkVariableDebug('soliditylocals', { num: { value: '2', type: 'uint256' } })
.checkVariableDebug('soliditystate', { number: { value: '0', type: 'uint256', constant: false } })
.end()
}
}
@ -246,7 +268,7 @@ const sources = [
}
},
{
'externalImport.sol': { content: 'import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol"; contract test7 {}' }
'externalImport.sol': { content: 'import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.1/contracts/token/ERC20/ERC20.sol"; contract test7 {}' }
},
{
'withABIEncoderV2.sol': {
@ -297,6 +319,40 @@ const sources = [
}
`
}
},
{
'useDebugNodes.sol': {
content: `
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
/**
* @title Storage
* @dev Store & retrieve value in a variable
*/
contract Storage {
uint256 number;
/**
* @dev Store value in variable
* @param num value to store
*/
function store(uint256 num) public {
number = num;
}
/**
* @dev Return value
* @return value of 'number'
*/
function retrieve() public view returns (uint256){
return number;
}
}
`
}
}
]
@ -367,7 +423,7 @@ const localVariable_step717_ABIEncoder = { // eslint-disable-line
const jsGetTrace = `(async () => {
try {
const result = await remix.call('debugger', 'getTrace', '0xbf309c0d71579d595f04a42e89d66d1ec17523dd3edea710b03f46a9b82ee0af')
const result = await remix.call('debugger', 'getTrace', '0xa38bff6f06e7c4fc91df1db6aa31a69ab5d5882faa953b1e7a88bfa523268ed7')
console.log('result ', result)
} catch (e) {
console.log(e.message)
@ -376,7 +432,7 @@ const jsGetTrace = `(async () => {
const jsDebug = `(async () => {
try {
const result = await remix.call('debugger', 'debug', '0xbf309c0d71579d595f04a42e89d66d1ec17523dd3edea710b03f46a9b82ee0af')
const result = await remix.call('debugger', 'debug', '0xa38bff6f06e7c4fc91df1db6aa31a69ab5d5882faa953b1e7a88bfa523268ed7')
console.log('result ', result)
} catch (e) {
console.log(e.message)

@ -54,8 +54,8 @@ module.exports = {
.click('[data-id="default_workspace-modal-footer-cancel-react"]')
.executeScript(`remix.loadgist('${gistid}')`)
// .perform((done) => { if (runtimeBrowser === 'chrome') { browser.openFile('gists') } done() })
.waitForElementVisible(`[data-id="treeViewLitreeViewItem/gist-${gistid}"]`)
.click(`[data-id="treeViewLitreeViewItem/gist-${gistid}"]`)
.waitForElementVisible(`[data-id="treeViewLitreeViewItemgist-${gistid}"]`)
.click(`[data-id="treeViewLitreeViewItemgist-${gistid}"]`)
.openFile(`gist-${gistid}/README.txt`)
}
})

@ -129,7 +129,7 @@ contract t2est {
const records = `{
"accounts": {
"account{10}": "0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c"
"account{2}": "0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c"
},
"linkReferences": {
"testLib": "created{1512830014773}"
@ -146,7 +146,7 @@ const records = `{
"linkReferences": {},
"inputs": "()",
"type": "constructor",
"from": "account{10}"
"from": "account{2}"
}
},
{
@ -172,7 +172,7 @@ const records = `{
"name": "",
"type": "constructor",
"inputs": "(uint256)",
"from": "account{10}"
"from": "account{2}"
}
},
{
@ -188,7 +188,7 @@ const records = `{
"name": "set",
"inputs": "(uint256,address)",
"type": "function",
"from": "account{10}"
"from": "account{2}"
}
}
],
@ -287,7 +287,7 @@ const records = `{
const scenario = {
accounts: {
'account{10}': '0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c'
'account{2}': '0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c'
},
linkReferences: {},
transactions: [
@ -305,7 +305,7 @@ const scenario = {
name: '',
type: 'constructor',
inputs: '(uint256)',
from: 'account{10}'
from: 'account{2}'
}
},
{
@ -320,7 +320,7 @@ const scenario = {
name: 'set',
inputs: '(uint256)',
type: 'function',
from: 'account{10}'
from: 'account{2}'
}
}
],

@ -62,7 +62,7 @@ module.exports = {
'Call web3.eth.getAccounts() using JavaScript VM': function (browser: NightwatchBrowser) {
browser
.executeScript('web3.eth.getAccounts()')
.waitForElementContainsText('*[data-id="terminalJournal"]', '"0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c", "0x14723A09ACff6D2A60DcdF7aA4AFf308FDDC160C", "0x4B0897b0513fdC7C541B6d9D7E929C4e5364D2dB", "0x583031D1113aD414F02576BD6afaBfb302140225", "0xdD870fA1b7C4700F2BD7f44238821C26f7392148"', 80000)
.waitForElementContainsText('*[data-id="terminalJournal"]', '"0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2", "0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c", "0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db", "0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB", "0x617F2E2fD72FD9D5503197092aC168c91465E7f2", "0x17F6AD8Ef982297579C203069C1DbfFE4348c372", "0x14723A09ACff6D2A60DcdF7aA4AFf308FDDC160C"', 80000)
},
'Call web3.eth.getAccounts() using Web3 Provider': function (browser: NightwatchBrowser) {

@ -152,6 +152,21 @@ module.exports = {
.journalLastChildIncludes('Parameters:')
.journalLastChildIncludes('2,3,error_string_2')
.journalLastChildIncludes('Debug the transaction to get more information.')
},
'Should Compile and Deploy a contract which define a custom error, the error should be logged in the terminal , using London VM Fork': function (browser: NightwatchBrowser) {
browser
.click('*[data-id="settingsVMLondonMode"]') // switch to London fork
.selectAccount('0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c') // this account will be used for this test suite
.click('#runTabView button[class^="instanceButton"]')
.waitForElementPresent('.instance:nth-of-type(2)')
.click('.instance:nth-of-type(2) > div > button')
.clickFunction('g - transact (not payable)')
.journalLastChildIncludes('Error provided by the contract:')
.journalLastChildIncludes('CustomError')
.journalLastChildIncludes('Parameters:')
.journalLastChildIncludes('2,3,error_string_2')
.journalLastChildIncludes('Debug the transaction to get more information.')
.end()
}
}

@ -10,7 +10,7 @@ const sources = [
module.exports = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done, 'http://127.0.0.1:8080/#optimize=true&runs=300&evmVersion=istanbul&version=soljson-v0.7.4+commit.3f05b770.js&code=cHJhZ21hIHNvbGlkaXR5ID49MC42LjAgPDAuNy4wOwoKaW1wb3J0ICJodHRwczovL2dpdGh1Yi5jb20vT3BlblplcHBlbGluL29wZW56ZXBwZWxpbi1jb250cmFjdHMvYmxvYi9tYXN0ZXIvY29udHJhY3RzL2FjY2Vzcy9Pd25hYmxlLnNvbCI7Cgpjb250cmFjdCBHZXRQYWlkIGlzIE93bmFibGUgewogIGZ1bmN0aW9uIHdpdGhkcmF3KCkgZXh0ZXJuYWwgb25seU93bmVyIHsKICB9Cn0', true, false)
init(browser, done, 'http://127.0.0.1:8080/#optimize=true&runs=300&evmVersion=istanbul&version=soljson-v0.7.4+commit.3f05b770.js', true)
},
'@sources': function () {
@ -19,6 +19,11 @@ module.exports = {
'Should load the code from URL params': function (browser: NightwatchBrowser) {
browser
.pause(5000)
.url('http://127.0.0.1:8080/#optimize=true&runs=300&evmVersion=istanbul&version=soljson-v0.7.4+commit.3f05b770.js&code=cHJhZ21hIHNvbGlkaXR5ID49MC42LjAgPDAuNy4wOwoKaW1wb3J0ICJodHRwczovL2dpdGh1Yi5jb20vT3BlblplcHBlbGluL29wZW56ZXBwZWxpbi1jb250cmFjdHMvYmxvYi9tYXN0ZXIvY29udHJhY3RzL2FjY2Vzcy9Pd25hYmxlLnNvbCI7Cgpjb250cmFjdCBHZXRQYWlkIGlzIE93bmFibGUgewogIGZ1bmN0aW9uIHdpdGhkcmF3KCkgZXh0ZXJuYWwgb25seU93bmVyIHsKICB9Cn0')
.refresh() // we do one reload for making sure we already have the default workspace
.pause(5000)
.currentWorkspaceIs('code-sample')
.getEditorValue((content) => {
browser.assert.ok(content.indexOf(
'https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol') !== -1,
@ -28,6 +33,11 @@ module.exports = {
'Should load using URL compiler params': function (browser: NightwatchBrowser) {
browser
.pause(5000)
.url('http://127.0.0.1:8080/#optimize=true&runs=300&evmVersion=istanbul&version=soljson-v0.7.4+commit.3f05b770.js')
.refresh()
.pause(5000)
.clickLaunchIcon('solidity')
.assert.containsText('#versionSelector option[selected="selected"]', '0.7.4+commit.3f05b770')
.assert.containsText('#evmVersionSelector option[selected="selected"]', 'istanbul')
.verify.elementPresent('#optimize:checked')

@ -56,6 +56,7 @@ declare module "nightwatch" {
checkAnnotations(type: string, line: number): NightwatchBrowser
checkAnnotationsNotPresent(type: string): NightwatchBrowser
getLastTransactionHash(callback: (hash: string) => void)
currentWorkspaceIs(name: string): NightwatchBrowser
}
export interface NightwatchBrowser {

@ -1,6 +1,7 @@
import isElectron from 'is-electron'
import { WebsocketPlugin } from '@remixproject/engine-web'
import * as packageJson from '../../../../../package.json'
import { version as remixdVersion } from '../../../../../libs/remixd/package.json'
var yo = require('yo-yo')
var modalDialog = require('../ui/modaldialog')
var modalDialogCustom = require('../ui/modal-dialog-custom')
@ -18,6 +19,7 @@ var css = csjs`
word-break: break-word;
}
`
const LOCALHOST = ' - connect to localhost - '
const profile = {
name: 'remixd',
@ -82,7 +84,9 @@ export class RemixdHandle extends WebsocketPlugin {
this.canceled()
}
}, 3000)
this.localhostProvider.init(() => {})
this.localhostProvider.init(() => {
this.call('filePanel', 'setWorkspace', { name: LOCALHOST, isLocalhost: true }, true)
})
this.call('manager', 'activatePlugin', 'hardhat')
}
}
@ -130,25 +134,28 @@ export class RemixdHandle extends WebsocketPlugin {
}
function remixdDialog () {
const commandText = 'remixd -s absolute-path-to-the-shared-folder --remix-ide your-remix-ide-URL-instance'
const commandText = 'remixd -s path-to-the-shared-folder --remix-ide remix-ide-instance-URL'
return yo`
<div class=${css.dialog}>
<div class=${css.dialogParagraph}>
Access your file system from Remix IDE. Remixd the NPM module needs to be running in the background to use the Remixd plugin. For more info please check the <a target="_blank" href="https://remix-ide.readthedocs.io/en/latest/remixd.html">Remixd tutorial</a>.
Access your local file system from Remix IDE using <a target="_blank" href="https://www.npmjs.com/package/@remix-project/remixd">Remixd NPM package</a>.<br/><br/>
Remixd needs to be running in the background to load the files in localhost workspace. For more info, please check the <a target="_blank" href="https://remix-ide.readthedocs.io/en/latest/remixd.html">Remixd tutorial</a>.
</div>
<div class=${css.dialogParagraph}>If you are just looking for the remixd command here it is:
<br><br><b>remixd -s absolute-path-to-the-shared-folder --remix-ide your-remix-ide-URL-instance</b>
<div class=${css.dialogParagraph}>If you are just looking for the remixd command, here it is:
<br><br><b>${commandText}</b>
<span class="">${copyToClipboard(() => commandText)}</span>
</div>
<div class=${css.dialogParagraph}>A connection will start a session between <em>${window.location.origin}</em> and your local file system <i>ws://127.0.0.1:65520</i>
<br>To see that a connection has been made, check that there is a localhost section in the Files Explorer
<div class=${css.dialogParagraph}>When connected, a session will be started between <em>${window.location.origin}</em> and your local file system at <i>ws://127.0.0.1:65520</i>
and the shared folder will be in the File Explorers workspace named "localhost".
<br/>Note, if the shared folder is a Hardhat project, an additional Hardhat websocket plugin will be listening at <i>ws://127.0.0.1:65522</i>
</div>
<div class=${css.dialogParagraph}>Please make sure your system is secured enough (port 65520 should not be opened nor forwarded).
This feature is still in Alpha, so we recommend you to keep a copy of the shared folder.
<div class=${css.dialogParagraph}>Please make sure your system is secured enough and ports 65520, 65522 are not opened nor forwarded.
This feature is still in Alpha, so we recommend to keep a copy of the shared folder.
</div>
<div class=${css.dialogParagraph}>
<h6 class="text-danger">
Before using, make sure you have the <b>latest remixd version</b>.<br><a target="_blank" href="https://remix-ide.readthedocs.io/en/latest/remixd.html#update-to-the-latest-remixd">Read here how to update it</a>
Before using, make sure remixd version is latest i.e. <b>${remixdVersion}</b>
<br><a target="_blank" href="https://remix-ide.readthedocs.io/en/latest/remixd.html#update-to-the-latest-remixd">Read here how to update it</a>
</h6>
</div>
</div>

@ -34,7 +34,7 @@ const modalDialogCustom = require('../ui/modal-dialog-custom')
const profile = {
name: 'filePanel',
displayName: 'File explorers',
methods: ['createNewFile', 'uploadFile', 'getCurrentWorkspace', 'getWorkspaces', 'createWorkspace'],
methods: ['createNewFile', 'uploadFile', 'getCurrentWorkspace', 'getWorkspaces', 'createWorkspace', 'setWorkspace'],
events: ['setWorkspace', 'renameWorkspace', 'deleteWorkspace', 'createWorkspace'],
icon: 'assets/img/fileManager.webp',
description: ' - ',
@ -43,7 +43,6 @@ const profile = {
documentation: 'https://remix-ide.readthedocs.io/en/latest/file_explorer.html',
version: packageJson.version
}
module.exports = class Filepanel extends ViewPlugin {
constructor (appManager) {
super(profile)
@ -114,7 +113,6 @@ module.exports = class Filepanel extends ViewPlugin {
async getWorkspaces () {
const result = new Promise((resolve, reject) => {
const workspacesPath = this._deps.fileProviders.workspace.workspacesPath
this._deps.fileProviders.browser.resolveDirectory('/' + workspacesPath, (error, items) => {
if (error) {
console.error(error)
@ -166,12 +164,26 @@ module.exports = class Filepanel extends ViewPlugin {
return
}
// insert example contracts if there are no files to show
this._deps.fileProviders.browser.resolveDirectory('/', async (error, filesList) => {
if (error) console.error(error)
if (Object.keys(filesList).length === 0) {
await this.createWorkspace('default_workspace')
}
this.getWorkspaces()
return new Promise((resolve, reject) => {
this._deps.fileProviders.browser.resolveDirectory('/', async (error, filesList) => {
if (error) return reject(error)
if (Object.keys(filesList).length === 0) {
await this.createWorkspace('default_workspace')
resolve('default_workspace')
} else {
this._deps.fileProviders.browser.resolveDirectory('.workspaces', async (error, filesList) => {
if (error) return reject(error)
if (Object.keys(filesList).length > 0) {
const workspacePath = Object.keys(filesList)[0].split('/').filter(val => val)
const workspaceName = workspacePath[workspacePath.length - 1]
this._deps.fileProviders.workspace.setWorkspace(workspaceName)
return resolve(workspaceName)
}
return reject(new Error('Can\'t find available workspace.'))
})
}
})
})
}
@ -213,13 +225,11 @@ module.exports = class Filepanel extends ViewPlugin {
await this.request.setWorkspace(workspaceName) // tells the react component to switch to that workspace
if (setDefaults) {
for (const file in examples) {
setTimeout(async () => { // space creation of files to give react ui time to update.
try {
await workspaceProvider.set(examples[file].name, examples[file].content)
} catch (error) {
console.error(error)
}
}, 10)
try {
await workspaceProvider.set(examples[file].name, examples[file].content)
} catch (error) {
console.error(error)
}
}
}
}
@ -235,14 +245,16 @@ module.exports = class Filepanel extends ViewPlugin {
}
/** these are called by the react component, action is already finished whent it's called */
async setWorkspace (workspace) {
this._deps.fileManager.closeAllFiles()
async setWorkspace (workspace, setEvent = true) {
if (workspace.isLocalhost) {
this.call('manager', 'activatePlugin', 'remixd')
} else if (await this.call('manager', 'isActive', 'remixd')) {
this.call('manager', 'deactivatePlugin', 'remixd')
}
this.emit('setWorkspace', workspace)
if (setEvent) {
this._deps.fileManager.setMode(workspace.isLocalhost ? 'localhost' : 'browser')
this.emit('setWorkspace', workspace)
}
}
workspaceRenamed (workspace) {

@ -65,7 +65,14 @@ class CompileTab extends ViewPlugin {
eventHandlers: {},
loading: false
}
this.compileTabLogic = new CompileTabLogic(this.queryParams, this.fileManager, this.editor, this.config, this.fileProvider, this.contentImport)
this.compileTabLogic = new CompileTabLogic(
this.queryParams,
this.fileManager,
this.editor,
this.config,
this.fileProvider,
this.contentImport
)
}
onActivationInternal () {
@ -126,9 +133,14 @@ class CompileTab extends ViewPlugin {
this.emit('statusChanged', { key: 'loading', title: 'compiling...', type: 'info' })
}
this.data.eventHandlers.onRemoveAnnotations = () => {
this.call('editor', 'clearAnnotations')
}
this.on('filePanel', 'setWorkspace', () => this.resetResults())
this.compileTabLogic.event.on('startingCompilation', this.data.eventHandlers.onStartingCompilation)
this.compileTabLogic.event.on('removeAnnotations', this.data.eventHandlers.onRemoveAnnotations)
this.data.eventHandlers.onCurrentFileChanged = (name) => {
this.compilerContainer.currentFile = name

@ -118,7 +118,7 @@ class CompileTab extends Plugin {
}
}
this.fileManager.saveCurrentFile()
this.call('editor', 'clearAnnotations')
this.event.emit('removeAnnotations')
var currentFile = this.config.get('currentFile')
return this.compileFile(currentFile)
} catch (err) {

@ -170,6 +170,7 @@ class CompilerContainer {
_retrieveVersion (version) {
if (!version) version = this._view.versionSelector.value
if (version === 'builtin') version = this.data.defaultVersion
return semver.coerce(version) ? semver.coerce(version).version : ''
}
@ -487,7 +488,7 @@ class CompilerContainer {
this._view.versionSelector.appendChild(option)
}
})
if (semver.lt(this._retrieveVersion(), 'v0.4.12+commit.194ff033.js')) {
if (this.data.selectedVersion !== 'builtin' && semver.lt(this._retrieveVersion(), 'v0.4.12+commit.194ff033.js')) {
toaster(yo`
<div>
<b>Old compiler usage detected.</b>
@ -500,7 +501,7 @@ class CompilerContainer {
// Workers cannot load js on "file:"-URLs and we get a
// "Uncaught RangeError: Maximum call stack size exceeded" error on Chromium,
// resort to non-worker version in that case.
if (this.data.selectedVersion !== 'builtin' && canUseWorker(this.data.selectedVersion)) {
if (canUseWorker(this._retrieveVersion())) {
this.compileTabLogic.compiler.loadVersion(true, url)
this.setVersionText('(loading using worker)')
} else {

@ -98,11 +98,15 @@ class SettingsUI {
</label>
<div class="${css.environment}">
<select id="selectExEnvOptions" data-id="settingsSelectEnvOptions" class="form-control ${css.select} custom-select">
<option id="vm-mode"
<option id="vm-mode-berlin" data-id="settingsVMBerlinMode"
title="Execution environment does not connect to any node, everything is local and in memory only."
value="vm" name="executionContext"> JavaScript VM
value="vm-berlin" name="executionContext" fork="berlin" > JavaScript VM (Berlin)
</option>
<option id="injected-mode"
<option id="vm-mode-london" data-id="settingsVMLondonMode"
title="Execution environment does not connect to any node, everything is local and in memory only."
value="vm-london" name="executionContext" fork="london"> JavaScript VM (London)
</option>
<option id="injected-mode" data-id="settingsInjectedMode"
title="Execution environment has been provided by Metamask or similar provider."
value="injected" name="executionContext"> Injected Web3
</option>
@ -236,11 +240,14 @@ class SettingsUI {
this.blockchain.event.register('removeProvider', name => removeProvider(name))
selectExEnv.addEventListener('change', (event) => {
const context = selectExEnv.options[selectExEnv.selectedIndex].value
this.setExecutionContext(context)
const provider = selectExEnv.options[selectExEnv.selectedIndex]
const fork = provider.getAttribute('fork') // can be undefined if connected to an external source (web3 provider / injected)
let context = provider.value
context = context.startsWith('vm') ? 'vm' : context // context has to be 'vm', 'web3' or 'injected'
this.setExecutionContext({ context, fork })
})
selectExEnv.value = this.blockchain.getProvider()
selectExEnv.value = this._getProviderDropdownValue()
}
setExecutionContext (context) {
@ -278,9 +285,19 @@ class SettingsUI {
`
}
/**
* generate a value used by the env dropdown list.
* @return {String} - can return 'vm-berlin, 'vm-london', 'injected' or 'web3'
*/
_getProviderDropdownValue () {
const provider = this.blockchain.getProvider()
const fork = this.blockchain.getCurrentFork()
return provider === 'vm' ? provider + '-' + fork : provider
}
setFinalContext () {
// set the final context. Cause it is possible that this is not the one we've originaly selected
this.selectExEnv.value = this.blockchain.getProvider()
this.selectExEnv.value = this._getProviderDropdownValue()
this.event.trigger('clearInstance', [])
this.updatePlusButton()
}

@ -1,5 +1,7 @@
import { ViewPlugin } from '@remixproject/engine-web'
import { canUseWorker, urlFromVersion } from '../compiler/compiler-utils'
import { removeMultipleSlashes, removeTrailingSlashes } from '../../lib/helper'
var yo = require('yo-yo')
var async = require('async')
var tooltip = require('../ui/tooltip')
@ -51,19 +53,34 @@ module.exports = class TestTab extends ViewPlugin {
this.listenToEvents()
}
onDeactivation () {
this.off('filePanel', 'newTestFileCreated')
this.off('filePanel', 'setWorkspace')
this.fileManager.events.removeListener('currentFileChanged', this.updateForNewCurrent)
}
listenToEvents () {
this.on('filePanel', 'newTestFileCreated', file => {
var testList = this._view.el.querySelector("[class^='testList']")
var test = this.createSingleTest(file)
testList.appendChild(test)
this.on('filePanel', 'newTestFileCreated', async file => {
try {
await this.testTabLogic.getTests((error, tests) => {
if (error) return tooltip(error)
this.data.allTests = tests
this.data.selectedTests = [...this.data.allTests]
this.updateTestFileList(tests)
if (!this.testsOutput) return // eslint-disable-line
})
} catch (e) {
console.log(e)
}
this.data.allTests.push(file)
this.data.selectedTests.push(file)
})
this.on('filePanel', 'setWorkspace', () => {
this.on('filePanel', 'setWorkspace', async () => {
this.testTabLogic.setCurrentPath(this.defaultPath)
this.inputPath.value = this.defaultPath
this.updateForNewCurrent()
this.updateDirList(this.defaultPath)
await this.updateForNewCurrent()
})
this.fileManager.events.on('noFileSelected', () => {
@ -72,18 +89,23 @@ module.exports = class TestTab extends ViewPlugin {
this.fileManager.events.on('currentFileChanged', (file, provider) => this.updateForNewCurrent(file))
}
updateForNewCurrent (file) {
this.updateGenerateFileAction()
if (!this.areTestsRunning) this.updateRunAction(file)
async updateForNewCurrent (file) {
this.data.allTests = []
this.updateTestFileList()
this.clearResults()
this.testTabLogic.getTests((error, tests) => {
if (error) return tooltip(error)
this.data.allTests = tests
this.data.selectedTests = [...this.data.allTests]
this.updateTestFileList(tests)
if (!this.testsOutput) return // eslint-disable-line
})
this.updateGenerateFileAction()
if (!this.areTestsRunning) this.updateRunAction(file)
try {
await this.testTabLogic.getTests((error, tests) => {
if (error) return tooltip(error)
this.data.allTests = tests
this.data.selectedTests = [...this.data.allTests]
this.updateTestFileList(tests)
if (!this.testsOutput) return // eslint-disable-line
})
} catch (e) {
console.log(e)
}
}
createSingleTest (testFile) {
@ -96,7 +118,7 @@ module.exports = class TestTab extends ViewPlugin {
}
listTests () {
if (!this.data.allTests) return []
if (!this.data.allTests || !this.data.allTests.length) return []
return this.data.allTests.map(
testFile => this.createSingleTest(testFile)
)
@ -425,7 +447,10 @@ module.exports = class TestTab extends ViewPlugin {
handleCreateFolder () {
this.inputPath.value = this.trimTestDirInput(this.inputPath.value)
let path = removeMultipleSlashes(this.inputPath.value)
if (path !== '/') path = removeTrailingSlashes(path)
if (this.inputPath.value === '') this.inputPath.value = this.defaultPath
this.inputPath.value = path
this.testTabLogic.generateTestFolder(this.inputPath.value)
this.createTestFolder.disabled = true
this.updateGenerateFileAction().disabled = false
@ -587,9 +612,11 @@ module.exports = class TestTab extends ViewPlugin {
}
async handleTestDirInput (e) {
const testDirInput = this.trimTestDirInput(this.inputPath.value)
let testDirInput = this.trimTestDirInput(this.inputPath.value)
testDirInput = removeMultipleSlashes(testDirInput)
if (testDirInput !== '/') testDirInput = removeTrailingSlashes(testDirInput)
if (e.key === 'Enter') {
this.inputPath.value = this.trimTestDirInput(testDirInput)
this.inputPath.value = testDirInput
if (await this.testTabLogic.pathExists(testDirInput)) {
this.testTabLogic.setCurrentPath(testDirInput)
this.updateForNewCurrent()
@ -598,7 +625,8 @@ module.exports = class TestTab extends ViewPlugin {
}
if (testDirInput) {
if (testDirInput.endsWith('/')) {
if (testDirInput.endsWith('/') && testDirInput !== '/') {
testDirInput = removeTrailingSlashes(testDirInput)
if (this.testTabLogic.currentPath === testDirInput.substr(0, testDirInput.length - 1)) {
this.createTestFolder.disabled = true
this.updateGenerateFileAction().disabled = true
@ -606,13 +634,13 @@ module.exports = class TestTab extends ViewPlugin {
this.updateDirList(testDirInput)
} else {
// If there is no matching folder in the workspace with entered text, enable Create button
if (this.testTabLogic.pathExists(testDirInput)) {
if (await this.testTabLogic.pathExists(testDirInput)) {
this.createTestFolder.disabled = true
this.updateGenerateFileAction().disabled = false
} else {
// Enable Create button
this.createTestFolder.disabled = false
// Disable Generate button because dir is not existing
// Disable Generate button because dir does not exist
this.updateGenerateFileAction().disabled = true
}
}
@ -621,6 +649,16 @@ module.exports = class TestTab extends ViewPlugin {
}
}
async handleEnter (e) {
this.inputPath.value = removeMultipleSlashes(this.trimTestDirInput(this.inputPath.value))
if (this.createTestFolder.disabled) {
if (await this.testTabLogic.pathExists(this.inputPath.value)) {
this.testTabLogic.setCurrentPath(this.inputPath.value)
this.updateForNewCurrent()
}
}
}
render () {
this.onActivationInternal()
this.testsOutput = yo`<div class="mx-3 mb-2 pb-4 border-top border-primary" hidden='true' id="solidityUnittestsOutput" data-id="testTabSolidityUnitTestsOutput"></a>`
@ -634,18 +672,10 @@ module.exports = class TestTab extends ViewPlugin {
id="utPath"
data-id="uiPathInput"
name="utPath"
title="Press 'Enter' to change the path for test files."
style="background-image: var(--primary);"
onkeyup=${(e) => this.handleTestDirInput(e)}
onchange=${(e) => {
if (this.createTestFolder.disabled) {
this.inputPath.value = this.trimTestDirInput(this.inputPath.value)
if (this.testTabLogic.pathExists(this.inputPath.value)) {
this.inputPath.value = this.trimTestDirInput(this.inputPath.value)
this.testTabLogic.setCurrentPath(this.inputPath.value)
this.updateForNewCurrent()
}
}
}}
onchange=${async (e) => this.handleEnter(e)}
/>`
this.createTestFolder = yo`
@ -654,7 +684,8 @@ module.exports = class TestTab extends ViewPlugin {
data-id="testTabGenerateTestFolder"
title="Create a test folder"
disabled=true
onclick=${(e) => this.handleCreateFolder()}>
onclick=${(e) => this.handleCreateFolder()}
>
Create
</button>
`

@ -10,24 +10,26 @@ class TestTabLogic {
setCurrentPath (path) {
if (path.indexOf('/') === 0) return
this.currentPath = path
this.currentPath = helper.removeMultipleSlashes(helper.removeTrailingSlashes(path))
}
generateTestFolder (path) {
// Todo move this check to File Manager after refactoring
// Checking to ignore the value which contains only whitespaces
if (!path || !(/\S/.test(path))) return
path = helper.removeMultipleSlashes(path)
const fileProvider = this.fileManager.fileProviderOf(path.split('/')[0])
fileProvider.exists(path).then(res => {
if (!res) fileProvider.createDir(path)
})
}
pathExists (path) {
async pathExists (path) {
// Checking to ignore the value which contains only whitespaces
if (!path || !(/\S/.test(path))) return
const fileProvider = this.fileManager.fileProviderOf(path.split('/')[0])
return fileProvider.exists(path, (e, res) => { return res })
const res = await fileProvider.exists(path, (e, res) => { return res })
return res
}
generateTestFile () {

@ -23,6 +23,7 @@ class Blockchain {
detectNetwork: (cb) => {
this.executionContext.detectNetwork(cb)
},
isVM: () => { return this.executionContext.isVM() },
personalMode: () => {
return this.getProvider() === 'web3' ? this.config.get('settings/personal-mode') : false
}
@ -208,6 +209,14 @@ class Blockchain {
return this.executionContext.getProvider()
}
/**
* return the fork name applied to the current envionment
* @return {String} - fork name
*/
getCurrentFork () {
return this.executionContext.getCurrentFork()
}
isWeb3Provider () {
const isVM = this.getProvider() === 'vm'
const isInjected = this.getProvider() === 'injected'
@ -314,6 +323,7 @@ class Blockchain {
detectNetwork: (cb) => {
this.executionContext.detectNetwork(cb)
},
isVM: () => { return this.executionContext.isVM() },
personalMode: () => {
return this.getProvider() === 'web3' ? this.config.get('settings/personal-mode') : false
}

@ -21,7 +21,8 @@ export class ExecutionContext {
this.executionContext = null
this.blockGasLimitDefault = 4300000
this.blockGasLimit = this.blockGasLimitDefault
this.currentFork = 'berlin'
this.defaultFork = 'berlin'
this.currentFork = this.defaultFork
this.mainNetGenesisHash = '0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3'
this.customNetWorks = {}
this.blocks = {}
@ -48,6 +49,10 @@ export class ExecutionContext {
return this.executionContext
}
getCurrentFork () {
return this.currentFork
}
isVM () {
return this.executionContext === 'vm'
}
@ -58,7 +63,7 @@ export class ExecutionContext {
web3 () {
if (this.customWeb3[this.executionContext]) return this.customWeb3[this.executionContext]
return this.isVM() ? this.vms[this.currentFork].web3vm : web3
return web3
}
detectNetwork (callback) {
@ -118,16 +123,21 @@ export class ExecutionContext {
this.executionContextChange(context, endPointUrl, confirmCb, infoCb, null)
}
executionContextChange (context, endPointUrl, confirmCb, infoCb, cb) {
executionContextChange (value, endPointUrl, confirmCb, infoCb, cb) {
const context = value.context
const fork = value.fork || this.defaultFork
if (!cb) cb = () => {}
if (!confirmCb) confirmCb = () => {}
if (!infoCb) infoCb = () => {}
if (context === 'vm') {
this.executionContext = context
this.currentFork = fork
this.event.trigger('contextChanged', ['vm'])
return cb()
}
this.currentFork = this.defaultFork // in the case of injected and web3, we default to the last fork.
if (context === 'injected') {
if (injectedProvider === undefined) {
infoCb('No injected Web3 provider found. Make sure your provider (e.g. MetaMask) is active and running (when recently activated you may have to reload the page).')
@ -147,7 +157,7 @@ export class ExecutionContext {
}
if (this.customNetWorks[context]) {
var network = this.customNetWorks[context]
this.setProviderFromEndpoint(network.provider, network.name, (error) => {
this.setProviderFromEndpoint(network.provider, { context: network.name }, (error) => {
if (error) infoCb(error)
cb()
})
@ -184,8 +194,9 @@ export class ExecutionContext {
// TODO: remove this when this function is moved
setProviderFromEndpoint (endpoint, context, cb) {
setProviderFromEndpoint (endpoint, value, cb) {
const oldProvider = web3.currentProvider
const context = value.context
web3.setProvider(endpoint)
web3.eth.net.isListening((err, isConnected) => {

@ -5,12 +5,6 @@ const { Provider, extend } = require('@remix-project/remix-simulator')
class VMProvider {
constructor (executionContext) {
this.executionContext = executionContext
this.RemixSimulatorProvider = new Provider({})
this.RemixSimulatorProvider.init()
this.web3 = new Web3(this.RemixSimulatorProvider)
extend(this.web3)
this.accounts = {}
this.executionContext.setWeb3('vm', this.web3)
}
getAccounts (cb) {
@ -23,8 +17,14 @@ class VMProvider {
}
resetEnvironment () {
this.accounts = {}
this.RemixSimulatorProvider = new Provider({ fork: this.executionContext.getCurrentFork() })
this.RemixSimulatorProvider.init()
this.RemixSimulatorProvider.Accounts.resetAccounts()
this.web3 = new Web3(this.RemixSimulatorProvider)
extend(this.web3)
this.accounts = {}
this.executionContext.setWeb3('vm', this.web3)
}
// TODO: is still here because of the plugin API

@ -104,6 +104,14 @@ module.exports = {
const hexValue = hash.slice(2, hash.length)
return this.is0XPrefixed(hash) && /^[0-9a-fA-F]{64}$/.test(hexValue)
},
removeTrailingSlashes (text) {
// Remove single or consecutive trailing slashes
return text.replace(/\/+$/g, '')
},
removeMultipleSlashes (text) {
// Replace consecutive slashes with '/'
return text.replace(/\/+/g, '/')
},
find: find,
getPathIcon (path) {
return path.endsWith('.txt')

@ -9,7 +9,7 @@ const _paq = window._paq = window._paq || []
const requiredModules = [ // services + layout views + system views
'manager', 'compilerArtefacts', 'compilerMetadata', 'contextualListener', 'editor', 'offsetToLineColumnConverter', 'network', 'theme',
'fileManager', 'contentImport', 'web3Provider', 'scriptRunner', 'fetchAndCompile', 'mainPanel', 'hiddenPanel', 'sidePanel', 'menuicons',
'filePanel', 'terminal', 'settings', 'pluginManager', 'tabs', 'udapp']
'filePanel', 'terminal', 'settings', 'pluginManager', 'tabs', 'udapp', 'dGitProvider']
const dependentModules = ['git', 'hardhat'] // module which shouldn't be manually activated (e.g git is activated by remixd)

@ -1,6 +1,6 @@
{
"name": "@remix-project/remix-analyzer",
"version": "0.5.8",
"version": "0.5.11",
"description": "Tool to perform static analysis on Solidity smart contracts",
"main": "index.js",
"types": ".index.d.ts",
@ -19,11 +19,15 @@
}
],
"dependencies": {
"@ethereumjs/block": "^3.3.0",
"@ethereumjs/tx": "^3.2.1",
"@ethereumjs/vm": "^5.4.1",
"@remix-project/remix-astwalker": "^0.0.26",
"@remix-project/remix-lib": "^0.4.34",
"@ethereumjs/vm": "^5.3.2",
"@ethereumjs/block": "^3.2.1",
"@ethereumjs/tx": "^3.1.3"
"@remix-project/remix-lib": "^0.5.2",
"async": "^2.6.2",
"ethereumjs-util": "^7.0.10",
"ethers": "^5.1.4",
"web3": "1.2.4"
},
"publishConfig": {
"access": "public"
@ -46,5 +50,5 @@
"typescript": "^3.7.5"
},
"typings": "index.d.ts",
"gitHead": "a0db8476e581f72690f735891fdf1282a97ec100"
"gitHead": "50b32cc20d2d4dcc793bf7de955957e073e5b5b8"
}

@ -1,6 +1,6 @@
{
"name": "@remix-project/remix-astwalker",
"version": "0.0.29",
"version": "0.0.32",
"description": "Tool to walk through Solidity AST",
"main": "index.js",
"scripts": {
@ -34,19 +34,23 @@
]
},
"dependencies": {
"@remix-project/remix-lib": "^0.4.34",
"@ethereumjs/block": "^3.3.0",
"@ethereumjs/tx": "^3.2.1",
"@ethereumjs/vm": "^5.4.1",
"@remix-project/remix-lib": "^0.5.2",
"@types/tape": "^4.2.33",
"@ethereumjs/vm": "^5.3.2",
"@ethereumjs/block": "^3.2.1",
"@ethereumjs/tx": "^3.1.3",
"async": "^2.6.2",
"ethereumjs-util": "^7.0.10",
"ethers": "^5.1.4",
"nyc": "^13.3.0",
"tape": "^4.10.1",
"ts-node": "^8.0.3",
"typescript": "^3.4.3"
"typescript": "^3.4.3",
"web3": "1.2.4"
},
"devDependencies": {
"tap-spec": "^5.0.0"
},
"typings": "index.d.ts",
"gitHead": "a0db8476e581f72690f735891fdf1282a97ec100"
"gitHead": "50b32cc20d2d4dcc793bf7de955957e073e5b5b8"
}

@ -1,6 +1,6 @@
{
"name": "@remix-project/remix-debug",
"version": "0.4.10",
"version": "0.5.2",
"description": "Tool to debug Ethereum transactions",
"contributors": [
{
@ -18,13 +18,17 @@
],
"main": "src/index.js",
"dependencies": {
"@ethereumjs/block": "^3.3.0",
"@ethereumjs/common": "^2.2.0",
"@ethereumjs/tx": "^3.2.1",
"@ethereumjs/vm": "^5.4.1",
"@remix-project/remix-astwalker": "^0.0.26",
"@remix-project/remix-lib": "^0.4.34",
"@remix-project/remix-lib": "^0.5.2",
"async": "^2.6.2",
"commander": "^2.19.0",
"@ethereumjs/vm": "^5.3.2",
"@ethereumjs/block": "^3.2.1",
"@ethereumjs/tx": "^3.1.3",
"deep-equal": "^1.0.1",
"ethereumjs-util": "^7.0.10",
"ethers": "^5.1.4",
"web3": "^1.2.4"
},
"devDependencies": {
@ -56,5 +60,5 @@
},
"homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-debug#readme",
"typings": "src/index.d.ts",
"gitHead": "a0db8476e581f72690f735891fdf1282a97ec100"
"gitHead": "50b32cc20d2d4dcc793bf7de955957e073e5b5b8"
}

@ -41,7 +41,7 @@ export class Ethdebugger {
this.opts = opts
this.event = new EventManager()
this.traceManager = new TraceManager({ web3: this.web3 })
this.traceManager = new TraceManager({ web3: this.web3, fork: this.opts.fork })
this.codeManager = new CodeManager(this.traceManager)
this.solidityProxy = new SolidityProxy({ getCurrentCalledAddressAt: this.traceManager.getCurrentCalledAddressAt.bind(this.traceManager), getCode: this.codeManager.getCode.bind(this.codeManager) })
this.storageResolver = null
@ -55,7 +55,7 @@ export class Ethdebugger {
}
setManagers () {
this.traceManager = new TraceManager({ web3: this.web3 })
this.traceManager = new TraceManager({ web3: this.web3, fork: this.opts.fork })
this.codeManager = new CodeManager(this.traceManager)
this.solidityProxy = new SolidityProxy({ getCurrentCalledAddressAt: this.traceManager.getCurrentCalledAddressAt.bind(this.traceManager), getCode: this.codeManager.getCode.bind(this.codeManager) })
this.storageResolver = null

@ -4,6 +4,7 @@ import { EventManager } from '../eventManager'
import { isContractCreation } from '../trace/traceHelper'
import { findNodeAtInstructionIndex } from '../source/sourceMappingDecoder'
import { CodeResolver } from './codeResolver'
import { TraceManager } from '../trace/traceManager' // eslint-disable-line
/*
resolve contract code referenced by vmtrace in order to be used by asm listview.
@ -15,7 +16,7 @@ import { CodeResolver } from './codeResolver'
export class CodeManager {
event
isLoading: boolean
traceManager
traceManager: TraceManager
codeResolver
constructor (_traceManager) {
@ -32,7 +33,8 @@ export class CodeManager {
return resolve(code)
})
})
}
},
fork: this.traceManager.getCurrentFork()
})
}

@ -6,12 +6,14 @@ export class CodeResolver {
bytecodeByAddress
instructionsByAddress
instructionsIndexByBytesOffset
fork
constructor ({ getCode }) {
constructor ({ getCode, fork }) {
this.getCode = getCode
this.bytecodeByAddress = {} // bytes code by contract addesses
this.instructionsByAddress = {} // assembly items instructions list by contract addesses
this.instructionsIndexByBytesOffset = {} // mapping between bytes offset and instructions index.
this.fork = fork
}
clear () {
@ -39,7 +41,7 @@ export class CodeResolver {
}
formatCode (hexCode) {
const [code, instructionsIndexByBytesOffset] = nameOpCodes(Buffer.from(hexCode.substring(2), 'hex'))
const [code, instructionsIndexByBytesOffset] = nameOpCodes(Buffer.from(hexCode.substring(2), 'hex'), this.fork)
return { code, instructionsIndexByBytesOffset }
}

@ -1,14 +1,24 @@
'use strict'
import opcodes from './opcodes'
import Common from '@ethereumjs/common'
import { getOpcodesForHF } from '@ethereumjs/vm/dist/evm/opcodes'
import getOpcodes from './opcodes'
export function nameOpCodes (raw, hardfork) {
const common = new Common({ chain: 'mainnet', hardfork })
const opcodes = getOpcodesForHF(common)
export function nameOpCodes (raw) {
let pushData = ''
const codeMap = {}
const code = []
for (let i = 0; i < raw.length; i++) {
const pc = i
const curOpCode = opcodes(raw[pc], false).name
let curOpCode
try {
curOpCode = opcodes.get(raw[pc]).fullName
} catch (e) {
curOpCode = 'INVALID'
}
codeMap[i] = code.length
// no destinations into the middle of PUSH
if (curOpCode.slice(0, 4) === 'PUSH') {
@ -25,17 +35,35 @@ export function nameOpCodes (raw) {
return [code, codeMap]
}
type Opcode = {
name: String,
pushData?: Array<number>
in?: number
out?: number
}
/**
* Parses code as a list of integers into a list of objects containing
* information about the opcode.
*/
export function parseCode (raw) {
const common = new Common({ chain: 'mainnet', hardfork: 'berlin' })
const opcodes = getOpcodesForHF(common)
const code = []
for (let i = 0; i < raw.length; i++) {
const opcode = opcodes(raw[i], true)
const opcode: Opcode = { name: 'INVALID' }
try {
const code = opcodes.get(raw[i])
const opcodeDetails = getOpcodes(raw[i], false)
opcode.in = opcodeDetails.in
opcode.out = opcodeDetails.out
opcode.name = code.fullName
} catch (e) {
opcode.name = 'INVALID'
}
if (opcode.name.slice(0, 4) === 'PUSH') {
const length = raw[i] - 0x5f
opcode['pushData'] = raw.slice(i + 1, i + length + 1)
opcode.pushData = raw.slice(i + 1, i + length + 1)
// in case pushdata extends beyond code
if (i + 1 + length > raw.length) {
for (let j = opcode['pushData'].length; j < length; j++) {
@ -60,5 +88,5 @@ export function log (num, base) {
}
export function roundLog (num, base) {
return Math.ceil(this.log(num, base))
return Math.ceil(log(num, base))
}

@ -189,7 +189,7 @@ export class VmDebuggerLogic {
})
})
this.debugger.event.register('indexChanged', this, (index) => {
this.debugger.event.register('indexChanged', this, async (index) => {
if (index < 0) return
if (this.stepManager.currentStepIndex !== index) return
if (!this.storageResolver) return
@ -201,11 +201,13 @@ export class VmDebuggerLogic {
for (var k in this.addresses) {
var address = this.addresses[k]
var storageViewer = new StorageViewer({ stepIndex: this.stepManager.currentStepIndex, tx: this.tx, address: address }, this.storageResolver, this._traceManager)
storageViewer.storageRange().then((result) => {
storageJSON[address] = result
this.event.trigger('traceStorageUpdate', [storageJSON])
})
try {
storageJSON[address] = await storageViewer.storageRange()
} catch (e) {
console.error(e)
}
}
this.event.trigger('traceStorageUpdate', [storageJSON])
})
}

@ -26,7 +26,8 @@ export class Debugger {
this.debugger = new Ethdebugger({
web3: options.web3,
debugWithGeneratedSources: options.debugWithGeneratedSources,
compilationResult: this.compilationResult
compilationResult: this.compilationResult,
fork: options.fork
})
const { traceManager, callTree, solidityProxy } = this.debugger

@ -22,8 +22,7 @@ export function web3DebugNode (network) {
Main: 'https://rpc.archivenode.io/e50zmkroshle2e2e50zm0044i7ao04ym',
Rinkeby: 'https://remix-rinkeby.ethdevops.io',
Ropsten: 'https://remix-ropsten.ethdevops.io',
Goerli: 'https://remix-goerli.ethdevops.io',
Kovan: 'https://remix-kovan.ethdevops.io'
Goerli: 'https://remix-goerli.ethdevops.io'
}
if (web3DebugNodes[network]) {
return loadWeb3(web3DebugNodes[network])

@ -26,6 +26,27 @@ export function extractContractDefinitions (sourcesList) {
return ret
}
/**
* return nodes from an ast @arg sourcesList that are declared outside of a ContractDefinition @astList
*
* @param {Object} sourcesList - sources list (containing root AST node)
* @return {Object} - returns a list of node
*/
export function extractOrphanDefinitions (sourcesList) {
const ret = []
for (const k in sourcesList) {
const ast = sourcesList[k].ast
if (ast.nodes && ast.nodes.length) {
for (const node of ast.nodes) {
if (node.nodeType !== 'ContractDefinition') {
ret.push(node)
}
}
}
}
return ret
}
/**
* returns the linearized base contracts of the contract @arg id
*
@ -54,7 +75,7 @@ export function extractStateDefinitions (contractName, sourcesList, contracts) {
if (!node) {
return null
}
const stateItems = []
const stateItems = extractOrphanDefinitions(sourcesList)
const stateVar = []
const baseContracts = getLinearizedBaseContracts(node.id, contracts.contractsById)
baseContracts.reverse()

@ -233,11 +233,11 @@ function getEnum (type, stateDefinitions, contractName) {
* @return {Array} containing all members of the current struct type
*/
function getStructMembers (type, stateDefinitions, contractName, location) {
const split = type.split('.')
if (!split.length) {
if (type.indexOf('.') === -1) {
type = contractName + '.' + type
} else {
contractName = split[0]
}
if (!contractName) {
contractName = type.split('.')[0]
}
const state = stateDefinitions[contractName]
if (state) {

@ -15,11 +15,11 @@ export function resolveCalledAddress (vmTraceIndex, trace) {
}
export function isCallInstruction (step) {
return ['CALL', 'STATICCALL', 'CALLCODE', 'CREATE', 'DELEGATECALL'].includes(step.op)
return ['CALL', 'STATICCALL', 'CALLCODE', 'CREATE', 'DELEGATECALL', 'CREATE2'].includes(step.op)
}
export function isCreateInstruction (step) {
return step.op === 'CREATE'
return step.op === 'CREATE' || step.op === 'CREATE2'
}
export function isReturnInstruction (step) {
@ -47,7 +47,7 @@ export function isSHA3Instruction (step) {
}
export function newContextStorage (step) {
return step.op === 'CREATE' || step.op === 'CALL'
return step.op === 'CREATE' || step.op === 'CALL' || step.op === 'CREATE2'
}
export function isCallToPrecompiledContract (index, trace) {

@ -7,6 +7,7 @@ import { util } from '@remix-project/remix-lib'
export class TraceManager {
web3
fork: string
isLoading: boolean
trace
traceCache
@ -16,6 +17,7 @@ export class TraceManager {
constructor (options) {
this.web3 = options.web3
this.fork = options.fork
this.isLoading = false
this.trace = null
this.traceCache = new TraceCache()
@ -70,6 +72,10 @@ export class TraceManager {
this.traceCache.init()
}
getCurrentFork () {
return this.fork
}
// API section
inRange (step) {
return this.isLoaded() && step >= 0 && step < this.trace.length

@ -1,6 +1,6 @@
{
"name": "@remix-project/remix-lib",
"version": "0.4.34",
"version": "0.5.2",
"description": "Library to various Remix tools",
"contributors": [
{
@ -14,10 +14,10 @@
],
"main": "src/index.js",
"dependencies": {
"@ethereumjs/block": "^3.3.0",
"@ethereumjs/tx": "^3.2.1",
"@ethereumjs/vm": "^5.4.1",
"async": "^2.1.2",
"@ethereumjs/block": "^3.2.1",
"@ethereumjs/tx": "^3.1.3",
"@ethereumjs/vm": "^5.3.2",
"ethereumjs-util": "^7.0.10",
"ethers": "^4.0.40",
"events": "^3.0.0",
@ -52,5 +52,5 @@
},
"homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-lib#readme",
"typings": "src/index.d.ts",
"gitHead": "a0db8476e581f72690f735891fdf1282a97ec100"
"gitHead": "50b32cc20d2d4dcc793bf7de955957e073e5b5b8"
}

@ -1,5 +1,5 @@
'use strict'
import { Transaction } from '@ethereumjs/tx'
import { Transaction, FeeMarketEIP1559Transaction } from '@ethereumjs/tx'
import { Block } from '@ethereumjs/block'
import { BN, bufferToHex, Address } from 'ethereumjs-util'
import { EventManager } from '../eventManager'
@ -60,15 +60,40 @@ export class TxRunnerVM {
this.getVMObject().stateManager.getAccount(Address.fromString(from)).then((res) => {
// See https://github.com/ethereumjs/ethereumjs-tx/blob/master/docs/classes/transaction.md#constructor
// for initialization fields and their types
value = value ? parseInt(value) : 0
const tx = Transaction.fromTxData({
nonce: new BN(res.nonce),
gasPrice: '0x1',
gasLimit: gasLimit,
to: to,
value: value,
data: Buffer.from(data.slice(2), 'hex')
}, { common: this.commonContext }).sign(account.privateKey)
if (!value) value = 0
if (typeof value === 'string') {
if (value.startsWith('0x')) value = new BN(value.replace('0x', ''), 'hex')
else {
try {
value = new BN(value, 10)
} catch (e) {
return callback('Unable to parse the value ' + e.message)
}
}
}
const EIP1559 = this.commonContext.hardfork() !== 'berlin'
let tx
if (!EIP1559) {
tx = Transaction.fromTxData({
nonce: new BN(res.nonce),
gasPrice: '0x1',
gasLimit: gasLimit,
to: to,
value: value,
data: Buffer.from(data.slice(2), 'hex')
}, { common: this.commonContext }).sign(account.privateKey)
} else {
tx = FeeMarketEIP1559Transaction.fromTxData({
nonce: new BN(res.nonce),
maxPriorityFeePerGas: '0x01',
maxFeePerGas: '0x1',
gasLimit: gasLimit,
to: to,
value: value,
data: Buffer.from(data.slice(2), 'hex')
}).sign(account.privateKey)
}
const coinbases = ['0x0e9281e9c6a0808672eaba6bd1220e144c9bb07a', '0x8945a1288dc78a6d8952a92c77aee6730b414778', '0x94d76e24f818426ae84aa404140e8d5f60e10e7e']
const difficulties = [new BN('69762765929000', 10), new BN('70762765929000', 10), new BN('71762765929000', 10)]
@ -79,7 +104,8 @@ export class TxRunnerVM {
number: self.blockNumber,
coinbase: coinbases[self.blockNumber % coinbases.length],
difficulty: difficulties[self.blockNumber % difficulties.length],
gasLimit: new BN(gasLimit.replace('0x', ''), 16).imuln(2)
gasLimit: new BN(gasLimit.replace('0x', ''), 16).imuln(2),
baseFeePerGas: EIP1559 ? '0x1' : undefined
},
transactions: [tx]
}, { common: this.commonContext })

@ -72,9 +72,8 @@ export class TxRunnerWeb3 {
const tx = { from: from, to: to, data: data, value: value }
if (useCall) {
const tag = Date.now() // for e2e reference
tx['gas'] = gasLimit
tx['timestamp'] = timestamp
if (this._api && this._api.isVM()) tx['timestamp'] = timestamp
return this.getWeb3().eth.call(tx, function (error, result: any) {
if (error) return callback(error)
callback(null, {

@ -21,8 +21,7 @@ export function web3DebugNode (network) {
Main: 'https://gethmainnet.komputing.org',
Rinkeby: 'https://remix-rinkeby.ethdevops.io',
Ropsten: 'https://remix-ropsten.ethdevops.io',
Goerli: 'https://remix-goerli.ethdevops.io',
Kovan: 'https://remix-kovan.ethdevops.io'
Goerli: 'https://remix-goerli.ethdevops.io'
}
if (web3DebugNodes[network]) {
return this.loadWeb3(web3DebugNodes[network])

@ -1,6 +1,6 @@
{
"name": "@remix-project/remix-simulator",
"version": "0.1.10-beta.0",
"version": "0.2.2",
"description": "Ethereum IDE and tools for the web",
"contributors": [
{
@ -14,17 +14,19 @@
],
"main": "src/index.js",
"dependencies": {
"@remix-project/remix-lib": "../remix-lib",
"@ethereumjs/block": "^3.3.0",
"@ethereumjs/common": "^2.2.0",
"@ethereumjs/tx": "^3.2.1",
"@ethereumjs/vm": "^5.4.1",
"@remix-project/remix-lib": "^0.5.2",
"ansi-gray": "^0.1.1",
"async": "^3.1.0",
"body-parser": "^1.18.2",
"color-support": "^1.1.3",
"commander": "^2.19.0",
"cors": "^2.8.5",
"@ethereumjs/vm": "^5.3.2",
"@ethereumjs/block": "^3.2.1",
"@ethereumjs/tx": "^3.1.3",
"ethereumjs-util": "^7.0.10",
"ethers": "^5.1.4",
"express": "^4.16.3",
"express-ws": "^4.0.0",
"merge": "^1.2.0",
@ -63,5 +65,5 @@
},
"homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-simulator#readme",
"typings": "src/index.d.ts",
"gitHead": "a0db8476e581f72690f735891fdf1282a97ec100"
"gitHead": "50b32cc20d2d4dcc793bf7de955957e073e5b5b8"
}

@ -113,7 +113,7 @@ export class Transactions {
}
eth_estimateGas (payload, cb) {
cb(null, 3000000)
cb(null, 10000000 * 8)
}
eth_getCode (payload, cb) {

@ -18,14 +18,12 @@ export class Provider {
Accounts
Transactions
methods
host: string
connected: boolean;
constructor (host: string = 'vm', options: Record<string, unknown> = {}) {
constructor (options: Record<string, unknown> = {}) {
this.options = options
this.host = host
this.connected = true
this.vmContext = new VMContext()
this.vmContext = new VMContext(options['fork'])
this.Accounts = new Accounts(this.vmContext)
this.Transactions = new Transactions(this.vmContext)

@ -91,24 +91,18 @@ export class VMContext {
blocks
latestBlockNumber
txs
vms
defaultFork
currentVm
web3vm
logsManager
exeResults
constructor () {
constructor (fork?) {
this.blockGasLimitDefault = 4300000
this.blockGasLimit = this.blockGasLimitDefault
this.currentFork = 'berlin'
this.vms = {
/*
byzantium: createVm('byzantium'),
constantinople: createVm('constantinople'),
petersburg: createVm('petersburg'),
istanbul: createVm('istanbul'),
*/
berlin: this.createVm('berlin')
}
this.defaultFork = fork || 'berlin'
this.currentFork = this.defaultFork
this.currentVm = this.createVm(this.currentFork)
this.blocks = {}
this.latestBlockNumber = 0
this.txs = {}
@ -122,7 +116,8 @@ export class VMContext {
const vm = new VM({
common,
activatePrecompiles: true,
stateManager: stateManager
stateManager,
allowUnlimitedContractSize: true
})
const web3vm = new remixLibVm.Web3VMProvider()
@ -130,8 +125,12 @@ export class VMContext {
return { vm, web3vm, stateManager, common }
}
getCurrentFork () {
return this.currentFork
}
web3 () {
return this.vms[this.currentFork].web3vm
return this.currentVm.web3vm
}
blankWeb3 () {
@ -139,11 +138,11 @@ export class VMContext {
}
vm () {
return this.vms[this.currentFork].vm
return this.currentVm.vm
}
vmObject () {
return this.vms[this.currentFork]
return this.currentVm
}
addBlock (block) {

@ -8,7 +8,7 @@ describe('Accounts', () => {
before(async function () {
const provider = new Provider()
await provider.init()
web3.setProvider(provider)
web3.setProvider(provider as any)
})
describe('eth_getAccounts', () => {

@ -6,11 +6,11 @@ import * as assert from 'assert'
describe('blocks', () => {
before(async () => {
const provider = new Provider('vm', {
const provider = new Provider({
coinbase: '0x0000000000000000000000000000000000000001'
})
await provider.init()
web3.setProvider(provider)
web3.setProvider(provider as any)
})
describe('eth_getBlockByNumber', () => {

@ -8,7 +8,7 @@ describe('Misc', () => {
before(async () => {
const provider = new Provider()
await provider.init()
web3.setProvider(provider)
web3.setProvider(provider as any)
})
describe('web3_clientVersion', () => {

@ -1,6 +1,6 @@
{
"name": "@remix-project/remix-solidity",
"version": "0.3.35",
"version": "0.4.2",
"description": "Tool to load and run Solidity compiler",
"main": "index.js",
"types": "./index.d.ts",
@ -15,12 +15,16 @@
}
],
"dependencies": {
"@remix-project/remix-lib": "../remix-lib",
"@ethereumjs/block": "^3.3.0",
"@ethereumjs/tx": "^3.2.1",
"@ethereumjs/vm": "^5.4.1",
"@remix-project/remix-lib": "^0.5.2",
"async": "^2.6.2",
"eslint-scope": "^5.0.0",
"@ethereumjs/vm": "^5.3.2",
"@ethereumjs/block": "^3.2.1",
"@ethereumjs/tx": "^3.1.3",
"ethereumjs-util": "^7.0.10",
"ethers": "^5.1.4",
"solc": "^0.7.4",
"web3": "1.2.4",
"webworkify-webpack": "^2.1.5"
},
"devDependencies": {
@ -54,5 +58,5 @@
},
"homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-solidity#readme",
"typings": "index.d.ts",
"gitHead": "a0db8476e581f72690f735891fdf1282a97ec100"
"gitHead": "50b32cc20d2d4dcc793bf7de955957e073e5b5b8"
}

@ -1,6 +1,6 @@
{
"name": "@remix-project/remix-tests",
"version": "0.1.38",
"version": "0.2.2",
"description": "Tool to test Solidity smart contracts",
"main": "src/index.js",
"types": "./src/index.d.ts",
@ -35,9 +35,13 @@
},
"homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-tests#readme",
"dependencies": {
"@remix-project/remix-lib": "../remix-lib",
"@remix-project/remix-simulator": "../remix-simulator",
"@remix-project/remix-solidity": "../remix-solidity",
"@ethereumjs/block": "^3.3.0",
"@ethereumjs/common": "^2.2.0",
"@ethereumjs/tx": "^3.2.1",
"@ethereumjs/vm": "^5.4.1",
"@remix-project/remix-lib": "^0.5.2",
"@remix-project/remix-simulator": "^0.2.2",
"@remix-project/remix-solidity": "^0.4.2",
"ansi-gray": "^0.1.1",
"async": "^2.6.0",
"axios": ">=0.21.1",
@ -45,13 +49,13 @@
"color-support": "^1.1.3",
"colors": "^1.1.2",
"commander": "^2.13.0",
"@ethereumjs/vm": "^5.3.2",
"@ethereumjs/block": "^3.2.1",
"@ethereumjs/tx": "^3.1.3",
"ethereumjs-util": "^7.0.10",
"ethers": "^5.1.4",
"express-ws": "^4.0.0",
"merge": "^1.2.0",
"signale": "^1.4.0",
"time-stamp": "^2.2.0",
"tslib": "^2.3.0",
"web3": "^1.2.4",
"winston": "^3.0.0"
},
@ -72,5 +76,5 @@
"typescript": "^3.3.1"
},
"typings": "src/index.d.ts",
"gitHead": "a0db8476e581f72690f735891fdf1282a97ec100"
"gitHead": "50b32cc20d2d4dcc793bf7de955957e073e5b5b8"
}

@ -110,7 +110,7 @@ export function deployAll (compileResult: compilationInterface, web3: Web3, with
const params = '' // we suppose that the test contract does not have any param in the constructor
execution.txFormat.encodeConstructorCallAndDeployLibraries(contractName, contract.raw, compileResult, params, funAbi, encodeDataFinalCallback, encodeDataStepCallback, encodeDataDeployLibraryCallback)
}, function (err) {
if (err) next(err)
if (err) return next(err)
next(null, contracts)
})
}

@ -63,9 +63,16 @@ export function runTestFiles (filepath: string, isDirectory: boolean, web3: Web3
}
deployAll(compilationResult, web3, false, (err, contracts) => {
if (err) {
next(err)
}
next(null, compilationResult, contracts)
// If contract deployment fails because of 'Out of Gas' error, try again with double gas
// This is temporary, should be removed when remix-tests will have a dedicated UI to
// accept deployment params from UI
if (err.message.includes('The contract code couldn\'t be stored, please check your gas limit')) {
deployAll(compilationResult, web3, true, (error, contracts) => {
if (error) next([{ message: 'contract deployment failed after trying twice: ' + error.message, severity: 'error' }]) // IDE expects errors in array
else next(null, compilationResult, contracts)
})
} else { next([{ message: 'contract deployment failed: ' + err.message, severity: 'error' }]) } // IDE expects errors in array
} else { next(null, compilationResult, contracts) }
})
},
function determineTestContractsToRun (compilationResult: compilationInterface, contracts: any, next) {

@ -238,7 +238,7 @@ export function runTest (testName: string, testObject: any, contractDetails: Com
sender = opts.accounts[sender]
}
}
let sendParams: Record<string, string> | null = null
let sendParams: Record<string, any> | null = null
if (sender) sendParams = { from: sender }
if (func.inputs && func.inputs.length > 0) { return resultsCallback(new Error(`Method '${func.name}' can not have parameters inside a test contract`), { passingNum, failureNum, timePassed }) }
const method = testObject.methods[func.name].apply(testObject.methods[func.name], [])
@ -280,6 +280,8 @@ export function runTest (testName: string, testObject: any, contractDetails: Com
else sendParams = { value }
}
}
if (!sendParams) sendParams = {}
sendParams.gas = 10000000 * 8
method.send(sendParams).on('receipt', (receipt) => {
try {
const time: number = (Date.now() - startTime) / 1000.0

@ -174,7 +174,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
setState(prevState => {
return {
...prevState,
validationError: 'The Kovan network is unfortunately not supported.'
validationError: 'Unfortunately, the Kovan network is not supported.'
}
})
return
@ -208,7 +208,8 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
}
return null
},
debugWithGeneratedSources: state.opt.debugWithGeneratedSources
debugWithGeneratedSources: state.opt.debugWithGeneratedSources,
fork: 'berlin'
})
debuggerInstance.debug(blockNumber, txNumber, tx, () => {
@ -282,7 +283,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
}} type="checkbox" title="Debug with generated sources" />
<label data-id="debugGeneratedSourcesLabel" className="form-check-label custom-control-label" htmlFor="debugGeneratedSourcesInput">Use generated sources (from Solidity v0.7.2)</label>
</div>
{ (state.validationError && !state.txNumberIsEmpty) && <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>
<TxBrowser requestDebug={ requestDebug } unloadRequested={ unloadRequested } updateTxNumberFlag={ updateTxNumberFlag } transactionNumber={ state.txNumber } debugging={ state.debugging } />
{ state.debugging && <StepManager stepManager={ stepManager } /> }

@ -2,6 +2,12 @@ import React from 'react'
import { File } from '../types'
import { extractNameFromKey, extractParentFromKey } from '../utils'
const queuedEvents = []
const pendingEvents = {}
let provider = null
let plugin = null
let dispatch: React.Dispatch<any> = null
export const fetchDirectoryError = (error: any) => {
return {
type: 'FETCH_DIRECTORY_ERROR',
@ -183,35 +189,25 @@ export const fileRenamedSuccess = (path: string, removePath: string, files) => {
}
}
export const init = (provider, workspaceName: string, plugin, registry) => (dispatch: React.Dispatch<any>) => {
export const init = (fileProvider, filePanel, registry) => (reducerDispatch: React.Dispatch<any>) => {
provider = fileProvider
plugin = filePanel
dispatch = reducerDispatch
if (provider) {
provider.event.on('fileAdded', async (filePath) => {
if (extractParentFromKey(filePath) === '/.workspaces') return
const path = extractParentFromKey(filePath) || provider.workspace || provider.type || ''
const data = await fetchDirectoryContent(provider, path)
dispatch(fileAddedSuccess(path, data))
if (filePath.includes('_test.sol')) {
plugin.emit('newTestFileCreated', filePath)
}
await executeEvent('fileAdded', filePath)
})
provider.event.on('folderAdded', async (folderPath) => {
if (extractParentFromKey(folderPath) === '/.workspaces') return
const path = extractParentFromKey(folderPath) || provider.workspace || provider.type || ''
const data = await fetchDirectoryContent(provider, path)
dispatch(folderAddedSuccess(path, data))
await executeEvent('folderAdded', folderPath)
})
provider.event.on('fileRemoved', async (removePath) => {
const path = extractParentFromKey(removePath) || provider.workspace || provider.type || ''
dispatch(fileRemovedSuccess(path, removePath))
await executeEvent('fileRemoved', removePath)
})
provider.event.on('fileRenamed', async (oldPath) => {
const path = extractParentFromKey(oldPath) || provider.workspace || provider.type || ''
const data = await fetchDirectoryContent(provider, path)
dispatch(fileRenamedSuccess(path, oldPath, data))
await executeEvent('fileRenamed', oldPath)
})
provider.event.on('rootFolderChanged', async () => {
await executeEvent('rootFolderChanged')
})
provider.event.on('fileExternallyChanged', async (path: string, file: { content: string }) => {
const config = registry.get('config').api
@ -232,10 +228,6 @@ export const init = (provider, workspaceName: string, plugin, registry) => (disp
provider.event.on('fileRenamedError', async () => {
dispatch(displayNotification('File Renamed Failed', '', 'Ok', 'Cancel'))
})
provider.event.on('rootFolderChanged', async () => {
workspaceName = provider.workspace || provider.type || ''
fetchDirectory(provider, workspaceName)(dispatch)
})
dispatch(fetchProviderSuccess(provider))
} else {
dispatch(fetchProviderError('No provider available'))
@ -294,3 +286,99 @@ export const hideNotification = () => {
export const closeNotificationModal = () => (dispatch: React.Dispatch<any>) => {
dispatch(hideNotification())
}
const fileAdded = async (filePath: string) => {
if (extractParentFromKey(filePath) === '/.workspaces') return
const path = extractParentFromKey(filePath) || provider.workspace || provider.type || ''
const data = await fetchDirectoryContent(provider, path)
await dispatch(fileAddedSuccess(path, data))
if (filePath.includes('_test.sol')) {
plugin.emit('newTestFileCreated', filePath)
}
}
const folderAdded = async (folderPath: string) => {
if (extractParentFromKey(folderPath) === '/.workspaces') return
const path = extractParentFromKey(folderPath) || provider.workspace || provider.type || ''
const data = await fetchDirectoryContent(provider, path)
await dispatch(folderAddedSuccess(path, data))
}
const fileRemoved = async (removePath: string) => {
const path = extractParentFromKey(removePath) || provider.workspace || provider.type || ''
await dispatch(fileRemovedSuccess(path, removePath))
}
const fileRenamed = async (oldPath: string) => {
const path = extractParentFromKey(oldPath) || provider.workspace || provider.type || ''
const data = await fetchDirectoryContent(provider, path)
await dispatch(fileRenamedSuccess(path, oldPath, data))
}
const rootFolderChanged = async () => {
const workspaceName = provider.workspace || provider.type || ''
await fetchDirectory(provider, workspaceName)(dispatch)
}
const executeEvent = async (eventName: 'fileAdded' | 'folderAdded' | 'fileRemoved' | 'fileRenamed' | 'rootFolderChanged', path?: string) => {
if (Object.keys(pendingEvents).length) {
return queuedEvents.push({ eventName, path })
}
pendingEvents[eventName + path] = { eventName, path }
switch (eventName) {
case 'fileAdded':
await fileAdded(path)
delete pendingEvents[eventName + path]
if (queuedEvents.length) {
const next = queuedEvents.pop()
await executeEvent(next.eventName, next.path)
}
break
case 'folderAdded':
await folderAdded(path)
delete pendingEvents[eventName + path]
if (queuedEvents.length) {
const next = queuedEvents.pop()
await executeEvent(next.eventName, next.path)
}
break
case 'fileRemoved':
await fileRemoved(path)
delete pendingEvents[eventName + path]
if (queuedEvents.length) {
const next = queuedEvents.pop()
await executeEvent(next.eventName, next.path)
}
break
case 'fileRenamed':
await fileRenamed(path)
delete pendingEvents[eventName + path]
if (queuedEvents.length) {
const next = queuedEvents.pop()
await executeEvent(next.eventName, next.path)
}
break
case 'rootFolderChanged':
await rootFolderChanged()
delete pendingEvents[eventName + path]
if (queuedEvents.length) {
const next = queuedEvents.pop()
await executeEvent(next.eventName, next.path)
}
break
}
}

@ -143,7 +143,7 @@ export const FileExplorer = (props: FileExplorerProps) => {
const editRef = useRef(null)
useEffect(() => {
init(props.filesProvider, props.name, props.plugin, props.registry)(dispatch)
init(props.filesProvider, props.plugin, props.registry)(dispatch)
}, [])
useEffect(() => {

@ -66,13 +66,16 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
}, [])
useEffect(() => {
setWarningState([])
if (autoRun) {
if (state.data !== null) {
run(state.data, state.source, state.file)
}
} else {
props.event.trigger('staticAnaysisWarning', [])
}
return () => { }
}, [autoRun, categoryIndex, state])
}, [state])
const message = (name, warning, more, fileName, locationString) : string => {
return (`
@ -302,7 +305,7 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
label="Autorun"
onChange={() => {}}
/>
<Button buttonText="Run" onClick={() => run(state.data, state.source, state.file)} disabled={state.data === null}/>
<Button buttonText="Run" onClick={() => run(state.data, state.source, state.file)} disabled={state.data === null || categoryIndex.length === 0 }/>
</div>
</div>
<div id="staticanalysismodules" className="list-group list-group-flush">
@ -323,22 +326,22 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
{state.file}
</span>
</div>
{ categoryIndex.length > 0 && Object.entries(warningState).length > 0 &&
{Object.entries(warningState).length > 0 &&
<div id='staticanalysisresult' >
<div className="mb-4">
{
(Object.entries(warningState).map((element) => (
<>
(Object.entries(warningState).map((element, index) => (
<div key={index}>
<span className="text-dark h6">{element[0]}</span>
{element[1].map(x => (
{element[1].map((x, i) => (
x.hasWarning ? (
<div id={`staticAnalysisModule${element[1].warningModuleName}`}>
<div id={`staticAnalysisModule${element[1].warningModuleName}`} key={i}>
<ErrorRenderer message={x.msg} opt={x.options} warningErrors={ x.warningErrors} editor={props.analysisModule}/>
</div>
) : null
))}
</>
</div>
)))
}
</div>

@ -6,7 +6,7 @@ import { Toaster } from '@remix-ui/toaster'// eslint-disable-line
/* eslint-disable-next-line */
export interface WorkspaceProps {
setWorkspace: ({ name: string, isLocalhost: boolean }) => void,
setWorkspace: ({ name: string, isLocalhost: boolean }, setEvent: boolean) => void,
createWorkspace: (name: string) => void,
renameWorkspace: (oldName: string, newName: string) => void
workspaceRenamed: ({ name: string }) => void,
@ -53,13 +53,13 @@ export const Workspace = (props: WorkspaceProps) => {
return setWorkspace(workspaceName)
}
props.request.createNewFile = () => {
if (!state.workspaces.length) createNewWorkspace('default_workspace')
props.request.createNewFile = async () => {
if (!state.workspaces.length) await createNewWorkspace('default_workspace')
props.plugin.resetNewFile()
}
props.request.uploadFile = (target) => {
if (!state.workspaces.length) createNewWorkspace('default_workspace')
props.request.uploadFile = async (target) => {
if (!state.workspaces.length) await createNewWorkspace('default_workspace')
setState(prevState => {
return { ...prevState, uploadFileEvent: target }
@ -71,12 +71,13 @@ export const Workspace = (props: WorkspaceProps) => {
}
useEffect(() => {
const getWorkspaces = async () => {
let getWorkspaces = async () => {
if (props.workspaces && Array.isArray(props.workspaces)) {
if (props.workspaces.length > 0 && state.currentWorkspace === NO_WORKSPACE) {
props.workspace.setWorkspace(props.workspaces[0])
const currentWorkspace = props.workspace.getWorkspace() ? props.workspace.getWorkspace() : props.workspaces[0]
await props.workspace.setWorkspace(currentWorkspace)
setState(prevState => {
return { ...prevState, workspaces: props.workspaces, currentWorkspace: props.workspaces[0] }
return { ...prevState, workspaces: props.workspaces, currentWorkspace }
})
} else {
setState(prevState => {
@ -87,6 +88,10 @@ export const Workspace = (props: WorkspaceProps) => {
}
getWorkspaces()
return () => {
getWorkspaces = async () => {}
}
}, [props.workspaces])
const localhostDisconnect = () => {
@ -236,14 +241,15 @@ export const Workspace = (props: WorkspaceProps) => {
}
const setWorkspace = async (name) => {
await props.setWorkspace({ name, isLocalhost: name === LOCALHOST })
await props.fileManager.closeAllFiles()
if (name === LOCALHOST) {
props.workspace.clearWorkspace()
} else if (name === NO_WORKSPACE) {
props.workspace.clearWorkspace()
} else {
props.workspace.setWorkspace(name)
await props.workspace.setWorkspace(name)
}
await props.setWorkspace({ name, isLocalhost: name === LOCALHOST }, !(name === LOCALHOST || name === NO_WORKSPACE))
props.plugin.getWorkspaces()
setState(prevState => {
return { ...prevState, currentWorkspace: name }

@ -1,6 +1,6 @@
{
"name": "@remix-project/remix-url-resolver",
"version": "0.0.20",
"version": "0.0.23",
"description": "Solidity import url resolver engine",
"main": "index.js",
"types": "./index.d.ts",
@ -42,5 +42,5 @@
"typescript": "^3.1.6"
},
"typings": "index.d.ts",
"gitHead": "a0db8476e581f72690f735891fdf1282a97ec100"
"gitHead": "50b32cc20d2d4dcc793bf7de955957e073e5b5b8"
}

@ -1,6 +1,6 @@
{
"name": "@remix-project/remixd",
"version": "0.3.5",
"version": "0.4.1",
"description": "remix server: allow accessing file system from remix.ethereum.org and start a dev environment (see help section)",
"main": "index.js",
"types": "./index.d.ts",

@ -36,12 +36,21 @@ const ports = {
}
const killCallBack: Array<Function> = []
function startService<S extends 'git' | 'hardhat' | 'folder'> (service: S, callback: (ws: WS, sharedFolderClient: servicesList.Sharedfolder) => void) {
function startService<S extends 'git' | 'hardhat' | 'folder'> (service: S, callback: (ws: WS, sharedFolderClient: servicesList.Sharedfolder, error?:Error) => void) {
const socket = new WebSocket(ports[service], { remixIdeUrl: program.remixIde }, () => services[service](program.readOnly || false))
socket.start(callback)
killCallBack.push(socket.close.bind(socket))
}
function errorHandler (error: any, service: string) {
const port = ports[service]
if (error.code && error.code === 'EADDRINUSE') {
console.log('\x1b[31m%s\x1b[0m', `[ERR] There is already a client running on port ${port}!`)
} else {
console.log('\x1b[31m%s\x1b[0m', '[ERR]', error)
}
}
(async () => {
const { version } = require('../package.json')
program.version(version, '-v, --version')
@ -76,7 +85,11 @@ function startService<S extends 'git' | 'hardhat' | 'folder'> (service: S, callb
console.log('\x1b[33m%s\x1b[0m', '[WARN] Any application that runs on your computer can potentially read from and write to all files in the directory.')
console.log('\x1b[33m%s\x1b[0m', '[WARN] Symbolic links are not forwarded to Remix IDE\n')
try {
startService('folder', (ws: WS, sharedFolderClient: servicesList.Sharedfolder) => {
startService('folder', (ws: WS, sharedFolderClient: servicesList.Sharedfolder, error: any) => {
if (error) {
errorHandler(error, 'hardhat')
return false
}
sharedFolderClient.setWebSocket(ws)
sharedFolderClient.setupNotifications(program.sharedFolder)
sharedFolderClient.sharedFolder(program.sharedFolder)
@ -85,7 +98,11 @@ function startService<S extends 'git' | 'hardhat' | 'folder'> (service: S, callb
const hardhatConfigFilePath = absolutePath('./', program.sharedFolder) + '/hardhat.config.js'
const isHardhatProject = fs.existsSync(hardhatConfigFilePath)
if (isHardhatProject) {
startService('hardhat', (ws: WS, sharedFolderClient: servicesList.Sharedfolder) => {
startService('hardhat', (ws: WS, sharedFolderClient: servicesList.Sharedfolder, error: Error) => {
if (error) {
errorHandler(error, 'hardhat')
return false
}
sharedFolderClient.setWebSocket(ws)
sharedFolderClient.sharedFolder(program.sharedFolder)
})

@ -9,7 +9,7 @@ export default class WebSocket {
constructor (public port: number, public opt: WebsocketOpt, public getclient: () => ServiceClient) {} //eslint-disable-line
start (callback?: (ws: WS, client: ServiceClient) => void): void {
start (callback?: (ws: WS, client: ServiceClient, error?: Error) => void): void {
this.server = http.createServer((request, response) => {
console.log((new Date()) + ' Received request for ' + request.url)
response.writeHead(404)
@ -21,9 +21,15 @@ export default class WebSocket {
65521: 'git',
65522: 'hardhat'
}
this.server.on('error', (error: Error) => {
if (callback)callback(null, null, error)
})
this.server.listen(this.port, loopback, () => {
console.log('\x1b[32m%s\x1b[0m', `[INFO] ${new Date()} ${listeners[this.port]} is listening on ${loopback}:${this.port}`)
})
this.wsServer = new WS.Server({
server: this.server,
verifyClient: (info, done) => {

5272
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -1,6 +1,6 @@
{
"name": "remix-project",
"version": "0.12.0-dev",
"version": "0.13.0-dev",
"license": "MIT",
"description": "Ethereum Remix Monorepo",
"keywords": [
@ -44,13 +44,13 @@
"lint:libs": "nx run-many --target=lint --projects=remix-analyzer,remix-astwalker,remix-debug,remix-lib,remix-simulator,remix-solidity,remix-tests,remix-url-resolver,remixd,remix-ui-tree-view,remix-ui-modal-dialog,remix-ui-toaster,remix-ui-file-explorer,remix-ui-debugger-ui,remix-ui-workspace,remix-ui-static-analyser,remix-ui-checkbox",
"build:libs": "nx run-many --target=build --parallel=false --with-deps=true --projects=remix-analyzer,remix-astwalker,remix-debug,remix-lib,remix-simulator,remix-solidity,remix-tests,remix-url-resolver,remixd",
"test:libs": "nx run-many --target=test --projects=remix-analyzer,remix-astwalker,remix-debug,remix-lib,remix-simulator,remix-solidity,remix-tests,remix-url-resolver,remixd",
"publish:libs": "npm run build:libs & lerna publish --skip-git & npm run bumpVersion:libs",
"publish:libs": "npm run build:libs && lerna publish --skip-git && npm run bumpVersion:libs",
"build:e2e": "tsc -p apps/remix-ide-e2e/tsconfig.e2e.json",
"watch:e2e": "nodemon",
"bumpVersion:libs": "gulp & gulp syncLibVersions;",
"browsertest": "sleep 5 && npm run nightwatch_local",
"csslint": "csslint --ignore=order-alphabetical --errors='errors,duplicate-properties,empty-rules' --exclude-list='apps/remix-ide/src/assets/css/font-awesome.min.css' apps/remix-ide/src/assets/css/",
"downloadsolc_assets": "wget --no-check-certificate https://binaries.soliditylang.org/bin/soljson-v0.8.4+commit.c7e474f2.js -O ./apps/remix-ide/src/assets/js/soljson.js",
"downloadsolc_assets": "wget --no-check-certificate https://binaries.soliditylang.org/wasm/soljson-v0.8.4+commit.c7e474f2.js -O ./apps/remix-ide/src/assets/js/soljson.js",
"make-mock-compiler": "node apps/remix-ide/ci/makeMockCompiler.js",
"minify": "uglifyjs --in-source-map inline --source-map-inline -c warnings=false",
"nightwatch_parallel": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js --env=chrome,firefox",
@ -130,10 +130,10 @@
},
"dependencies": {
"@erebos/bzz-node": "^0.13.0",
"@ethereumjs/block": "^3.2.1",
"@ethereumjs/common": "^2.2.0",
"@ethereumjs/tx": "^3.1.3",
"@ethereumjs/vm": "^5.3.2",
"@ethereumjs/block": "^3.3.0",
"@ethereumjs/common": "^2.3.1",
"@ethereumjs/tx": "^3.2.1",
"@ethereumjs/vm": "^5.4.1",
"@remixproject/engine": "^0.3.17",
"@remixproject/engine-web": "^0.3.17",
"@remixproject/plugin": "^0.3.17",
@ -173,6 +173,7 @@
"selenium": "^2.20.0",
"signale": "^1.4.0",
"time-stamp": "^2.2.0",
"tslib": "^2.3.0",
"web3": "1.2.4",
"winston": "^3.3.3",
"ws": "^7.3.0"

Loading…
Cancel
Save