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

@ -74,7 +74,10 @@ export const DebuggerApiMixin = (Base) => class extends Base {
const targetAddress = target || receipt.contractAddress || receipt.to const targetAddress = target || receipt.contractAddress || receipt.to
const codeAtAddress = await this._web3.eth.getCode(targetAddress) const codeAtAddress = await this._web3.eth.getCode(targetAddress)
const output = await this.call('fetchAndCompile', 'resolve', targetAddress, codeAtAddress, 'browser/.debug') 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 () { async getDebugWeb3 () {
@ -108,7 +111,8 @@ export const DebuggerApiMixin = (Base) => class extends Base {
} }
return null return null
}, },
debugWithGeneratedSources: false debugWithGeneratedSources: false,
fork: 'berlin'
}) })
return await debug.debugger.traceManager.getTrace(hash) 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() 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 browser
.url(url || 'http://127.0.0.1:8080') .url(url || 'http://127.0.0.1:8080')
.pause(5000) .pause(5000)
.switchBrowserTab(0) .switchBrowserTab(0)
.perform((done) => {
if (closeWorkspaceAlert) {
browser.waitForElementVisible('*[data-id="modalDialogModalBody"]', 60000)
.modalFooterOKClick()
}
done()
})
.fullscreenWindow(() => { .fullscreenWindow(() => {
if (preloadPlugins) { if (preloadPlugins) {
initModules(browser, () => { initModules(browser, () => {

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

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

@ -178,7 +178,7 @@ module.exports = {
.click('*[data-id="debuggerTransactionStartButton"]') // start debugging .click('*[data-id="debuggerTransactionStartButton"]') // start debugging
.pause(2000) .pause(2000)
.getEditorValue((content) => { .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"]') .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. 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)
},
'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() .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': { '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 () => { const jsGetTrace = `(async () => {
try { try {
const result = await remix.call('debugger', 'getTrace', '0xbf309c0d71579d595f04a42e89d66d1ec17523dd3edea710b03f46a9b82ee0af') const result = await remix.call('debugger', 'getTrace', '0xa38bff6f06e7c4fc91df1db6aa31a69ab5d5882faa953b1e7a88bfa523268ed7')
console.log('result ', result) console.log('result ', result)
} catch (e) { } catch (e) {
console.log(e.message) console.log(e.message)
@ -376,7 +432,7 @@ const jsGetTrace = `(async () => {
const jsDebug = `(async () => { const jsDebug = `(async () => {
try { try {
const result = await remix.call('debugger', 'debug', '0xbf309c0d71579d595f04a42e89d66d1ec17523dd3edea710b03f46a9b82ee0af') const result = await remix.call('debugger', 'debug', '0xa38bff6f06e7c4fc91df1db6aa31a69ab5d5882faa953b1e7a88bfa523268ed7')
console.log('result ', result) console.log('result ', result)
} catch (e) { } catch (e) {
console.log(e.message) console.log(e.message)

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

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

@ -62,7 +62,7 @@ module.exports = {
'Call web3.eth.getAccounts() using JavaScript VM': function (browser: NightwatchBrowser) { 'Call web3.eth.getAccounts() using JavaScript VM': function (browser: NightwatchBrowser) {
browser browser
.executeScript('web3.eth.getAccounts()') .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) { 'Call web3.eth.getAccounts() using Web3 Provider': function (browser: NightwatchBrowser) {

@ -152,6 +152,21 @@ module.exports = {
.journalLastChildIncludes('Parameters:') .journalLastChildIncludes('Parameters:')
.journalLastChildIncludes('2,3,error_string_2') .journalLastChildIncludes('2,3,error_string_2')
.journalLastChildIncludes('Debug the transaction to get more information.') .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() .end()
} }
} }

@ -10,7 +10,7 @@ const sources = [
module.exports = { module.exports = {
before: function (browser: NightwatchBrowser, done: VoidFunction) { 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 () { '@sources': function () {
@ -19,6 +19,11 @@ module.exports = {
'Should load the code from URL params': function (browser: NightwatchBrowser) { 'Should load the code from URL params': function (browser: NightwatchBrowser) {
browser 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) => { .getEditorValue((content) => {
browser.assert.ok(content.indexOf( browser.assert.ok(content.indexOf(
'https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol') !== -1, '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) { 'Should load using URL compiler params': function (browser: NightwatchBrowser) {
browser 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('#versionSelector option[selected="selected"]', '0.7.4+commit.3f05b770')
.assert.containsText('#evmVersionSelector option[selected="selected"]', 'istanbul') .assert.containsText('#evmVersionSelector option[selected="selected"]', 'istanbul')
.verify.elementPresent('#optimize:checked') .verify.elementPresent('#optimize:checked')

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

@ -1,6 +1,7 @@
import isElectron from 'is-electron' import isElectron from 'is-electron'
import { WebsocketPlugin } from '@remixproject/engine-web' import { WebsocketPlugin } from '@remixproject/engine-web'
import * as packageJson from '../../../../../package.json' import * as packageJson from '../../../../../package.json'
import { version as remixdVersion } from '../../../../../libs/remixd/package.json'
var yo = require('yo-yo') var yo = require('yo-yo')
var modalDialog = require('../ui/modaldialog') var modalDialog = require('../ui/modaldialog')
var modalDialogCustom = require('../ui/modal-dialog-custom') var modalDialogCustom = require('../ui/modal-dialog-custom')
@ -18,6 +19,7 @@ var css = csjs`
word-break: break-word; word-break: break-word;
} }
` `
const LOCALHOST = ' - connect to localhost - '
const profile = { const profile = {
name: 'remixd', name: 'remixd',
@ -82,7 +84,9 @@ export class RemixdHandle extends WebsocketPlugin {
this.canceled() this.canceled()
} }
}, 3000) }, 3000)
this.localhostProvider.init(() => {}) this.localhostProvider.init(() => {
this.call('filePanel', 'setWorkspace', { name: LOCALHOST, isLocalhost: true }, true)
})
this.call('manager', 'activatePlugin', 'hardhat') this.call('manager', 'activatePlugin', 'hardhat')
} }
} }
@ -130,25 +134,28 @@ export class RemixdHandle extends WebsocketPlugin {
} }
function remixdDialog () { 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` return yo`
<div class=${css.dialog}> <div class=${css.dialog}>
<div class=${css.dialogParagraph}> <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>
<div class=${css.dialogParagraph}>If you are just looking for the remixd command here it is: <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> <br><br><b>${commandText}</b>
<span class="">${copyToClipboard(() => commandText)}</span> <span class="">${copyToClipboard(() => commandText)}</span>
</div> </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> <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>
<br>To see that a connection has been made, check that there is a localhost section in the Files Explorer 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>
<div class=${css.dialogParagraph}>Please make sure your system is secured enough (port 65520 should not be opened nor forwarded). <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 you to keep a copy of the shared folder. This feature is still in Alpha, so we recommend to keep a copy of the shared folder.
</div> </div>
<div class=${css.dialogParagraph}> <div class=${css.dialogParagraph}>
<h6 class="text-danger"> <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> </h6>
</div> </div>
</div> </div>

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

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

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

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

@ -98,11 +98,15 @@ class SettingsUI {
</label> </label>
<div class="${css.environment}"> <div class="${css.environment}">
<select id="selectExEnvOptions" data-id="settingsSelectEnvOptions" class="form-control ${css.select} custom-select"> <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." 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>
<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." title="Execution environment has been provided by Metamask or similar provider."
value="injected" name="executionContext"> Injected Web3 value="injected" name="executionContext"> Injected Web3
</option> </option>
@ -236,11 +240,14 @@ class SettingsUI {
this.blockchain.event.register('removeProvider', name => removeProvider(name)) this.blockchain.event.register('removeProvider', name => removeProvider(name))
selectExEnv.addEventListener('change', (event) => { selectExEnv.addEventListener('change', (event) => {
const context = selectExEnv.options[selectExEnv.selectedIndex].value const provider = selectExEnv.options[selectExEnv.selectedIndex]
this.setExecutionContext(context) 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) { 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 () { setFinalContext () {
// set the final context. Cause it is possible that this is not the one we've originaly selected // 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.event.trigger('clearInstance', [])
this.updatePlusButton() this.updatePlusButton()
} }

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

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

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

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

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

@ -104,6 +104,14 @@ module.exports = {
const hexValue = hash.slice(2, hash.length) const hexValue = hash.slice(2, hash.length)
return this.is0XPrefixed(hash) && /^[0-9a-fA-F]{64}$/.test(hexValue) 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, find: find,
getPathIcon (path) { getPathIcon (path) {
return path.endsWith('.txt') return path.endsWith('.txt')

@ -9,7 +9,7 @@ const _paq = window._paq = window._paq || []
const requiredModules = [ // services + layout views + system views const requiredModules = [ // services + layout views + system views
'manager', 'compilerArtefacts', 'compilerMetadata', 'contextualListener', 'editor', 'offsetToLineColumnConverter', 'network', 'theme', 'manager', 'compilerArtefacts', 'compilerMetadata', 'contextualListener', 'editor', 'offsetToLineColumnConverter', 'network', 'theme',
'fileManager', 'contentImport', 'web3Provider', 'scriptRunner', 'fetchAndCompile', 'mainPanel', 'hiddenPanel', 'sidePanel', 'menuicons', '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) 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", "name": "@remix-project/remix-analyzer",
"version": "0.5.8", "version": "0.5.11",
"description": "Tool to perform static analysis on Solidity smart contracts", "description": "Tool to perform static analysis on Solidity smart contracts",
"main": "index.js", "main": "index.js",
"types": ".index.d.ts", "types": ".index.d.ts",
@ -19,11 +19,15 @@
} }
], ],
"dependencies": { "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-astwalker": "^0.0.26",
"@remix-project/remix-lib": "^0.4.34", "@remix-project/remix-lib": "^0.5.2",
"@ethereumjs/vm": "^5.3.2", "async": "^2.6.2",
"@ethereumjs/block": "^3.2.1", "ethereumjs-util": "^7.0.10",
"@ethereumjs/tx": "^3.1.3" "ethers": "^5.1.4",
"web3": "1.2.4"
}, },
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
@ -46,5 +50,5 @@
"typescript": "^3.7.5" "typescript": "^3.7.5"
}, },
"typings": "index.d.ts", "typings": "index.d.ts",
"gitHead": "a0db8476e581f72690f735891fdf1282a97ec100" "gitHead": "50b32cc20d2d4dcc793bf7de955957e073e5b5b8"
} }

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

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

@ -41,7 +41,7 @@ export class Ethdebugger {
this.opts = opts this.opts = opts
this.event = new EventManager() 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.codeManager = new CodeManager(this.traceManager)
this.solidityProxy = new SolidityProxy({ getCurrentCalledAddressAt: this.traceManager.getCurrentCalledAddressAt.bind(this.traceManager), getCode: this.codeManager.getCode.bind(this.codeManager) }) this.solidityProxy = new SolidityProxy({ getCurrentCalledAddressAt: this.traceManager.getCurrentCalledAddressAt.bind(this.traceManager), getCode: this.codeManager.getCode.bind(this.codeManager) })
this.storageResolver = null this.storageResolver = null
@ -55,7 +55,7 @@ export class Ethdebugger {
} }
setManagers () { 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.codeManager = new CodeManager(this.traceManager)
this.solidityProxy = new SolidityProxy({ getCurrentCalledAddressAt: this.traceManager.getCurrentCalledAddressAt.bind(this.traceManager), getCode: this.codeManager.getCode.bind(this.codeManager) }) this.solidityProxy = new SolidityProxy({ getCurrentCalledAddressAt: this.traceManager.getCurrentCalledAddressAt.bind(this.traceManager), getCode: this.codeManager.getCode.bind(this.codeManager) })
this.storageResolver = null this.storageResolver = null

@ -4,6 +4,7 @@ import { EventManager } from '../eventManager'
import { isContractCreation } from '../trace/traceHelper' import { isContractCreation } from '../trace/traceHelper'
import { findNodeAtInstructionIndex } from '../source/sourceMappingDecoder' import { findNodeAtInstructionIndex } from '../source/sourceMappingDecoder'
import { CodeResolver } from './codeResolver' 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. 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 { export class CodeManager {
event event
isLoading: boolean isLoading: boolean
traceManager traceManager: TraceManager
codeResolver codeResolver
constructor (_traceManager) { constructor (_traceManager) {
@ -32,7 +33,8 @@ export class CodeManager {
return resolve(code) return resolve(code)
}) })
}) })
} },
fork: this.traceManager.getCurrentFork()
}) })
} }

@ -6,12 +6,14 @@ export class CodeResolver {
bytecodeByAddress bytecodeByAddress
instructionsByAddress instructionsByAddress
instructionsIndexByBytesOffset instructionsIndexByBytesOffset
fork
constructor ({ getCode }) { constructor ({ getCode, fork }) {
this.getCode = getCode this.getCode = getCode
this.bytecodeByAddress = {} // bytes code by contract addesses this.bytecodeByAddress = {} // bytes code by contract addesses
this.instructionsByAddress = {} // assembly items instructions list by contract addesses this.instructionsByAddress = {} // assembly items instructions list by contract addesses
this.instructionsIndexByBytesOffset = {} // mapping between bytes offset and instructions index. this.instructionsIndexByBytesOffset = {} // mapping between bytes offset and instructions index.
this.fork = fork
} }
clear () { clear () {
@ -39,7 +41,7 @@ export class CodeResolver {
} }
formatCode (hexCode) { 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 } return { code, instructionsIndexByBytesOffset }
} }

@ -1,14 +1,24 @@
'use strict' '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 = '' let pushData = ''
const codeMap = {} const codeMap = {}
const code = [] const code = []
for (let i = 0; i < raw.length; i++) { for (let i = 0; i < raw.length; i++) {
const pc = 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 codeMap[i] = code.length
// no destinations into the middle of PUSH // no destinations into the middle of PUSH
if (curOpCode.slice(0, 4) === 'PUSH') { if (curOpCode.slice(0, 4) === 'PUSH') {
@ -25,17 +35,35 @@ export function nameOpCodes (raw) {
return [code, codeMap] 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 * Parses code as a list of integers into a list of objects containing
* information about the opcode. * information about the opcode.
*/ */
export function parseCode (raw) { export function parseCode (raw) {
const common = new Common({ chain: 'mainnet', hardfork: 'berlin' })
const opcodes = getOpcodesForHF(common)
const code = [] const code = []
for (let i = 0; i < raw.length; i++) { 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') { if (opcode.name.slice(0, 4) === 'PUSH') {
const length = raw[i] - 0x5f 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 // in case pushdata extends beyond code
if (i + 1 + length > raw.length) { if (i + 1 + length > raw.length) {
for (let j = opcode['pushData'].length; j < length; j++) { for (let j = opcode['pushData'].length; j < length; j++) {
@ -60,5 +88,5 @@ export function log (num, base) {
} }
export function roundLog (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 (index < 0) return
if (this.stepManager.currentStepIndex !== index) return if (this.stepManager.currentStepIndex !== index) return
if (!this.storageResolver) return if (!this.storageResolver) return
@ -201,11 +201,13 @@ export class VmDebuggerLogic {
for (var k in this.addresses) { for (var k in this.addresses) {
var address = this.addresses[k] var address = this.addresses[k]
var storageViewer = new StorageViewer({ stepIndex: this.stepManager.currentStepIndex, tx: this.tx, address: address }, this.storageResolver, this._traceManager) var storageViewer = new StorageViewer({ stepIndex: this.stepManager.currentStepIndex, tx: this.tx, address: address }, this.storageResolver, this._traceManager)
storageViewer.storageRange().then((result) => { try {
storageJSON[address] = result storageJSON[address] = await storageViewer.storageRange()
this.event.trigger('traceStorageUpdate', [storageJSON]) } catch (e) {
}) console.error(e)
}
} }
this.event.trigger('traceStorageUpdate', [storageJSON])
}) })
} }

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

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

@ -26,6 +26,27 @@ export function extractContractDefinitions (sourcesList) {
return ret 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 * returns the linearized base contracts of the contract @arg id
* *
@ -54,7 +75,7 @@ export function extractStateDefinitions (contractName, sourcesList, contracts) {
if (!node) { if (!node) {
return null return null
} }
const stateItems = [] const stateItems = extractOrphanDefinitions(sourcesList)
const stateVar = [] const stateVar = []
const baseContracts = getLinearizedBaseContracts(node.id, contracts.contractsById) const baseContracts = getLinearizedBaseContracts(node.id, contracts.contractsById)
baseContracts.reverse() baseContracts.reverse()

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

@ -15,11 +15,11 @@ export function resolveCalledAddress (vmTraceIndex, trace) {
} }
export function isCallInstruction (step) { 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) { export function isCreateInstruction (step) {
return step.op === 'CREATE' return step.op === 'CREATE' || step.op === 'CREATE2'
} }
export function isReturnInstruction (step) { export function isReturnInstruction (step) {
@ -47,7 +47,7 @@ export function isSHA3Instruction (step) {
} }
export function newContextStorage (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) { export function isCallToPrecompiledContract (index, trace) {

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -1,6 +1,6 @@
{ {
"name": "@remix-project/remix-tests", "name": "@remix-project/remix-tests",
"version": "0.1.38", "version": "0.2.2",
"description": "Tool to test Solidity smart contracts", "description": "Tool to test Solidity smart contracts",
"main": "src/index.js", "main": "src/index.js",
"types": "./src/index.d.ts", "types": "./src/index.d.ts",
@ -35,9 +35,13 @@
}, },
"homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-tests#readme", "homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-tests#readme",
"dependencies": { "dependencies": {
"@remix-project/remix-lib": "../remix-lib", "@ethereumjs/block": "^3.3.0",
"@remix-project/remix-simulator": "../remix-simulator", "@ethereumjs/common": "^2.2.0",
"@remix-project/remix-solidity": "../remix-solidity", "@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", "ansi-gray": "^0.1.1",
"async": "^2.6.0", "async": "^2.6.0",
"axios": ">=0.21.1", "axios": ">=0.21.1",
@ -45,13 +49,13 @@
"color-support": "^1.1.3", "color-support": "^1.1.3",
"colors": "^1.1.2", "colors": "^1.1.2",
"commander": "^2.13.0", "commander": "^2.13.0",
"@ethereumjs/vm": "^5.3.2", "ethereumjs-util": "^7.0.10",
"@ethereumjs/block": "^3.2.1", "ethers": "^5.1.4",
"@ethereumjs/tx": "^3.1.3",
"express-ws": "^4.0.0", "express-ws": "^4.0.0",
"merge": "^1.2.0", "merge": "^1.2.0",
"signale": "^1.4.0", "signale": "^1.4.0",
"time-stamp": "^2.2.0", "time-stamp": "^2.2.0",
"tslib": "^2.3.0",
"web3": "^1.2.4", "web3": "^1.2.4",
"winston": "^3.0.0" "winston": "^3.0.0"
}, },
@ -72,5 +76,5 @@
"typescript": "^3.3.1" "typescript": "^3.3.1"
}, },
"typings": "src/index.d.ts", "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 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) execution.txFormat.encodeConstructorCallAndDeployLibraries(contractName, contract.raw, compileResult, params, funAbi, encodeDataFinalCallback, encodeDataStepCallback, encodeDataDeployLibraryCallback)
}, function (err) { }, function (err) {
if (err) next(err) if (err) return next(err)
next(null, contracts) next(null, contracts)
}) })
} }

@ -63,9 +63,16 @@ export function runTestFiles (filepath: string, isDirectory: boolean, web3: Web3
} }
deployAll(compilationResult, web3, false, (err, contracts) => { deployAll(compilationResult, web3, false, (err, contracts) => {
if (err) { if (err) {
next(err) // 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
next(null, compilationResult, contracts) // 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) { function determineTestContractsToRun (compilationResult: compilationInterface, contracts: any, next) {

@ -238,7 +238,7 @@ export function runTest (testName: string, testObject: any, contractDetails: Com
sender = opts.accounts[sender] sender = opts.accounts[sender]
} }
} }
let sendParams: Record<string, string> | null = null let sendParams: Record<string, any> | null = null
if (sender) sendParams = { from: sender } 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 }) } 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], []) 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 } else sendParams = { value }
} }
} }
if (!sendParams) sendParams = {}
sendParams.gas = 10000000 * 8
method.send(sendParams).on('receipt', (receipt) => { method.send(sendParams).on('receipt', (receipt) => {
try { try {
const time: number = (Date.now() - startTime) / 1000.0 const time: number = (Date.now() - startTime) / 1000.0

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

@ -2,6 +2,12 @@ import React from 'react'
import { File } from '../types' import { File } from '../types'
import { extractNameFromKey, extractParentFromKey } from '../utils' 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) => { export const fetchDirectoryError = (error: any) => {
return { return {
type: 'FETCH_DIRECTORY_ERROR', 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) { if (provider) {
provider.event.on('fileAdded', async (filePath) => { provider.event.on('fileAdded', async (filePath) => {
if (extractParentFromKey(filePath) === '/.workspaces') return await executeEvent('fileAdded', filePath)
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)
}
}) })
provider.event.on('folderAdded', async (folderPath) => { provider.event.on('folderAdded', async (folderPath) => {
if (extractParentFromKey(folderPath) === '/.workspaces') return await executeEvent('folderAdded', folderPath)
const path = extractParentFromKey(folderPath) || provider.workspace || provider.type || ''
const data = await fetchDirectoryContent(provider, path)
dispatch(folderAddedSuccess(path, data))
}) })
provider.event.on('fileRemoved', async (removePath) => { provider.event.on('fileRemoved', async (removePath) => {
const path = extractParentFromKey(removePath) || provider.workspace || provider.type || '' await executeEvent('fileRemoved', removePath)
dispatch(fileRemovedSuccess(path, removePath))
}) })
provider.event.on('fileRenamed', async (oldPath) => { provider.event.on('fileRenamed', async (oldPath) => {
const path = extractParentFromKey(oldPath) || provider.workspace || provider.type || '' await executeEvent('fileRenamed', oldPath)
const data = await fetchDirectoryContent(provider, path) })
provider.event.on('rootFolderChanged', async () => {
dispatch(fileRenamedSuccess(path, oldPath, data)) await executeEvent('rootFolderChanged')
}) })
provider.event.on('fileExternallyChanged', async (path: string, file: { content: string }) => { provider.event.on('fileExternallyChanged', async (path: string, file: { content: string }) => {
const config = registry.get('config').api const config = registry.get('config').api
@ -232,10 +228,6 @@ export const init = (provider, workspaceName: string, plugin, registry) => (disp
provider.event.on('fileRenamedError', async () => { provider.event.on('fileRenamedError', async () => {
dispatch(displayNotification('File Renamed Failed', '', 'Ok', 'Cancel')) dispatch(displayNotification('File Renamed Failed', '', 'Ok', 'Cancel'))
}) })
provider.event.on('rootFolderChanged', async () => {
workspaceName = provider.workspace || provider.type || ''
fetchDirectory(provider, workspaceName)(dispatch)
})
dispatch(fetchProviderSuccess(provider)) dispatch(fetchProviderSuccess(provider))
} else { } else {
dispatch(fetchProviderError('No provider available')) dispatch(fetchProviderError('No provider available'))
@ -294,3 +286,99 @@ export const hideNotification = () => {
export const closeNotificationModal = () => (dispatch: React.Dispatch<any>) => { export const closeNotificationModal = () => (dispatch: React.Dispatch<any>) => {
dispatch(hideNotification()) 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) const editRef = useRef(null)
useEffect(() => { useEffect(() => {
init(props.filesProvider, props.name, props.plugin, props.registry)(dispatch) init(props.filesProvider, props.plugin, props.registry)(dispatch)
}, []) }, [])
useEffect(() => { useEffect(() => {

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

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

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

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

@ -36,12 +36,21 @@ const ports = {
} }
const killCallBack: Array<Function> = [] 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)) const socket = new WebSocket(ports[service], { remixIdeUrl: program.remixIde }, () => services[service](program.readOnly || false))
socket.start(callback) socket.start(callback)
killCallBack.push(socket.close.bind(socket)) 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 () => { (async () => {
const { version } = require('../package.json') const { version } = require('../package.json')
program.version(version, '-v, --version') 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] 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') console.log('\x1b[33m%s\x1b[0m', '[WARN] Symbolic links are not forwarded to Remix IDE\n')
try { 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.setWebSocket(ws)
sharedFolderClient.setupNotifications(program.sharedFolder) sharedFolderClient.setupNotifications(program.sharedFolder)
sharedFolderClient.sharedFolder(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 hardhatConfigFilePath = absolutePath('./', program.sharedFolder) + '/hardhat.config.js'
const isHardhatProject = fs.existsSync(hardhatConfigFilePath) const isHardhatProject = fs.existsSync(hardhatConfigFilePath)
if (isHardhatProject) { 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.setWebSocket(ws)
sharedFolderClient.sharedFolder(program.sharedFolder) 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 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) => { this.server = http.createServer((request, response) => {
console.log((new Date()) + ' Received request for ' + request.url) console.log((new Date()) + ' Received request for ' + request.url)
response.writeHead(404) response.writeHead(404)
@ -21,9 +21,15 @@ export default class WebSocket {
65521: 'git', 65521: 'git',
65522: 'hardhat' 65522: 'hardhat'
} }
this.server.on('error', (error: Error) => {
if (callback)callback(null, null, error)
})
this.server.listen(this.port, loopback, () => { 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}`) console.log('\x1b[32m%s\x1b[0m', `[INFO] ${new Date()} ${listeners[this.port]} is listening on ${loopback}:${this.port}`)
}) })
this.wsServer = new WS.Server({ this.wsServer = new WS.Server({
server: this.server, server: this.server,
verifyClient: (info, done) => { 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", "name": "remix-project",
"version": "0.12.0-dev", "version": "0.13.0-dev",
"license": "MIT", "license": "MIT",
"description": "Ethereum Remix Monorepo", "description": "Ethereum Remix Monorepo",
"keywords": [ "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", "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", "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", "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", "build:e2e": "tsc -p apps/remix-ide-e2e/tsconfig.e2e.json",
"watch:e2e": "nodemon", "watch:e2e": "nodemon",
"bumpVersion:libs": "gulp & gulp syncLibVersions;", "bumpVersion:libs": "gulp & gulp syncLibVersions;",
"browsertest": "sleep 5 && npm run nightwatch_local", "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/", "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", "make-mock-compiler": "node apps/remix-ide/ci/makeMockCompiler.js",
"minify": "uglifyjs --in-source-map inline --source-map-inline -c warnings=false", "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", "nightwatch_parallel": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js --env=chrome,firefox",
@ -130,10 +130,10 @@
}, },
"dependencies": { "dependencies": {
"@erebos/bzz-node": "^0.13.0", "@erebos/bzz-node": "^0.13.0",
"@ethereumjs/block": "^3.2.1", "@ethereumjs/block": "^3.3.0",
"@ethereumjs/common": "^2.2.0", "@ethereumjs/common": "^2.3.1",
"@ethereumjs/tx": "^3.1.3", "@ethereumjs/tx": "^3.2.1",
"@ethereumjs/vm": "^5.3.2", "@ethereumjs/vm": "^5.4.1",
"@remixproject/engine": "^0.3.17", "@remixproject/engine": "^0.3.17",
"@remixproject/engine-web": "^0.3.17", "@remixproject/engine-web": "^0.3.17",
"@remixproject/plugin": "^0.3.17", "@remixproject/plugin": "^0.3.17",
@ -173,6 +173,7 @@
"selenium": "^2.20.0", "selenium": "^2.20.0",
"signale": "^1.4.0", "signale": "^1.4.0",
"time-stamp": "^2.2.0", "time-stamp": "^2.2.0",
"tslib": "^2.3.0",
"web3": "1.2.4", "web3": "1.2.4",
"winston": "^3.3.3", "winston": "^3.3.3",
"ws": "^7.3.0" "ws": "^7.3.0"

Loading…
Cancel
Save