Merge branch 'master' into errormarkers

pull/2774/head
bunsenstraat 2 years ago committed by GitHub
commit 408b44d1df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 54
      .circleci/config.yml
  2. 2
      README.md
  3. 17
      apps/remix-ide-e2e/src/helpers/init.ts
  4. 32
      apps/remix-ide-e2e/src/tests/ballot.test.ts
  5. 42
      apps/remix-ide-e2e/src/tests/search.test.ts
  6. 4
      apps/remix-ide-e2e/src/tests/terminal.test.ts
  7. 97
      apps/remix-ide-e2e/src/tests/url.test.ts
  8. 96
      apps/remix-ide-e2e/src/tests/vyper_api.ts
  9. 25
      apps/remix-ide/ci/browser_tests_vyper_plugin.sh
  10. 23
      apps/remix-ide/src/app.js
  11. 3
      apps/remix-ide/src/app/tabs/abstract-provider.tsx
  12. 6
      apps/remix-ide/src/remixAppManager.js
  13. 13
      apps/vyper/.babelrc
  14. 16
      apps/vyper/.browserslistrc
  15. 18
      apps/vyper/.eslintrc
  16. 124
      apps/vyper/src/app/app.css
  17. 118
      apps/vyper/src/app/app.tsx
  18. 75
      apps/vyper/src/app/components/CompilerButton.tsx
  19. 36
      apps/vyper/src/app/components/LocalUrl.tsx
  20. 70
      apps/vyper/src/app/components/VyperResult.tsx
  21. 19
      apps/vyper/src/app/components/WarnRemote.tsx
  22. 161
      apps/vyper/src/app/examples/ballot.tsx
  23. 14
      apps/vyper/src/app/logo.svg
  24. 11
      apps/vyper/src/app/star.svg
  25. 139
      apps/vyper/src/app/utils/compiler.tsx
  26. 12
      apps/vyper/src/app/utils/index.ts
  27. 71
      apps/vyper/src/app/utils/remix-client.tsx
  28. 0
      apps/vyper/src/assets/.gitkeep
  29. 3
      apps/vyper/src/environments/environment.prod.ts
  30. 6
      apps/vyper/src/environments/environment.ts
  31. BIN
      apps/vyper/src/favicon.ico
  32. 14
      apps/vyper/src/index.html
  33. 7
      apps/vyper/src/main.tsx
  34. 7
      apps/vyper/src/polyfills.ts
  35. 1
      apps/vyper/src/styles.css
  36. 14
      apps/vyper/tsconfig.app.json
  37. 20
      apps/vyper/tsconfig.json
  38. 2
      libs/remix-core-plugin/src/lib/compiler-metadata.ts
  39. 4
      libs/remix-core-plugin/src/lib/constants/uups.ts
  40. 2
      libs/remix-core-plugin/src/lib/editor-context-listener.ts
  41. 53
      libs/remix-core-plugin/src/lib/openzeppelin-proxy.ts
  42. 1
      libs/remix-tests/package.json
  43. 20
      libs/remix-tests/src/compiler.ts
  44. 6
      libs/remix-tests/src/run.ts
  45. 56
      libs/remix-tests/src/runTestFiles.ts
  46. 4
      libs/remix-tests/tests/examples_0/assert_ok_test.sol
  47. 12
      libs/remix-tests/tests/examples_0/assert_ok_without_console_test.sol
  48. 1532
      libs/remix-tests/tests/examples_0/hardhat/console.sol
  49. 46
      libs/remix-tests/tests/testRunner.cli.spec.ts
  50. 2
      libs/remix-tests/tests/testRunner.spec.ts
  51. 2
      libs/remix-ui/clipboard/src/lib/copy-to-clipboard/copy-to-clipboard.tsx
  52. 29
      libs/remix-ui/editor/src/lib/remix-ui-editor.tsx
  53. 13
      libs/remix-ui/run-tab/src/lib/actions/events.ts
  54. 25
      libs/remix-ui/run-tab/src/lib/components/contractGUI.tsx
  55. 2
      libs/remix-ui/settings/src/lib/constants.ts
  56. 26
      libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx
  57. 10
      libs/remix-ui/solidity-compiler/src/lib/contract-selection.tsx
  58. 2
      libs/remix-ui/solidity-compiler/src/lib/solidity-compiler.tsx
  59. 1
      libs/remix-ui/solidity-compiler/src/lib/types/index.ts
  60. 2
      libs/remix-ui/terminal/src/lib/utils/wrapScript.ts
  61. 5
      libs/remix-ui/workspace/src/lib/actions/workspace.ts
  62. 69
      libs/remixd/README.md
  63. 2
      libs/remixd/package.json
  64. 44
      nx.json
  65. 1
      package.json
  66. 498
      workspace.json
  67. 137
      yarn.lock

@ -35,10 +35,10 @@ jobs:
key: v1-deps-{{ checksum "yarn.lock" }}
paths:
- node_modules
- run: yarn run downloadsolc_assets
- run: yarn run downloadsolc_assets
- run: npx nx build remix-ide
- run: npx nx build remix-ide-e2e-src-local-plugin
- run: yarn run build:libs
- run: mkdir persist && zip -r persist/dist.zip dist
- persist_to_workspace:
@ -266,6 +266,50 @@ jobs:
path: ./reports/tests
- store_artifacts:
path: ./reports/screenshots
remix-ide-vyper-plugin:
docker:
# specify the version you desire here
- image: cimg/node:14.17.6-browsers
# Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images
# documented at https://circleci.com/docs/2.0/circleci-images/
resource_class: xlarge
# - image: circleci/mongo:3.4.4
environment:
- COMMIT_AUTHOR_EMAIL: "yann@ethereum.org"
- COMMIT_AUTHOR: "Circle CI"
working_directory: ~/remix-project
parallelism: 10
steps:
- browser-tools/install-chrome
- browser-tools/install-chromedriver
- run:
command: |
google-chrome --version
chromedriver --version
java -jar /usr/local/bin/selenium.jar --version
name: Check install
- checkout
- checkout
- attach_workspace:
at: .
- run: unzip ./persist/dist.zip
- restore_cache:
keys:
- v1-deps-{{ checksum "yarn.lock" }}
- run: yarn install
- run:
name: Start Selenium
command: java -jar /usr/local/bin/selenium.jar
background: true
- run: npx nx build vyper
- run: ./apps/remix-ide/ci/browser_tests_vyper_plugin.sh
- store_test_results:
path: ./reports/tests
- store_artifacts:
path: ./reports/screenshots
remix-ide-plugin-api:
docker:
@ -420,6 +464,9 @@ workflows:
- remix-ide-plugin-api:
requires:
- build
- remix-ide-vyper-plugin:
requires:
- build
- remix-ide-chrome:
requires:
- build
@ -433,6 +480,7 @@ workflows:
- remix-ide-chrome
- remix-ide-firefox
- remix-ide-plugin-api
- remix-ide-vyper-plugin
filters:
branches:
only: remix_live
@ -443,6 +491,7 @@ workflows:
- remix-ide-chrome
- remix-ide-firefox
- remix-ide-plugin-api
- remix-ide-vyper-plugin
filters:
branches:
only: master
@ -453,6 +502,7 @@ workflows:
- remix-ide-chrome
- remix-ide-firefox
- remix-ide-plugin-api
- remix-ide-vyper-plugin
filters:
branches:
only: remix_beta

@ -213,7 +213,7 @@ To do this you need to:
browser.waitForElementPresent('*[data-id="verticalIconsKindfilePanel"]')
```
- add '@disable': true to the test file you want to split:
- add '@disabled': true to the test file you want to split:
```
module.exports = {

@ -2,13 +2,28 @@ import { NightwatchBrowser } from 'nightwatch'
require('dotenv').config()
export default function (browser: NightwatchBrowser, callback: VoidFunction, url?: string, preloadPlugins = true): void {
type LoadPlugin = {
name: string
url: string
}
export default function (browser: NightwatchBrowser, callback: VoidFunction, url?: string, preloadPlugins = true, loadPlugin?: LoadPlugin): void {
browser
.url(url || 'http://127.0.0.1:8080')
.pause(6000)
.switchBrowserTab(0)
.waitForElementVisible('[id="remixTourSkipbtn"]')
.click('[id="remixTourSkipbtn"]')
.perform((done) => {
if (!loadPlugin) return done()
browser.execute(function (loadPlugin) { // override a plugin url for testing purpose
localStorage.setItem('test-plugin-name', loadPlugin.name)
localStorage.setItem('test-plugin-url', loadPlugin.url)
}, [loadPlugin])
.refresh()
.pause(6000)
.perform(done)
})
.maximizeWindow()
.fullscreenWindow(() => {
if (preloadPlugins) {

@ -134,6 +134,23 @@ module.exports = {
.sendKeys('*[data-id$="scConfigFilePathInput"]', browser.Keys.ENTER)
.openFile('Untitled.sol')
.verifyContracts(['Ballot'], {wait: 2000, runs: '300'})
},
'Compile and deploy sample yul file': function (browser: NightwatchBrowser) {
browser
.addFile('sample.yul', {content: yulSample})
.clickLaunchIcon('solidity')
.waitForElementVisible('*[data-id="scConfigExpander"]')
.click('*[data-id="scManualConfiguration"]')
.waitForElementVisible('select[id="compilierLanguageSelector"]', 10000)
.click('select[id="compilierLanguageSelector"]')
.click('select[id="compilierLanguageSelector"] option[value=Yul]')
.waitForElementContainsText('[data-id="compiledContracts"]', 'Contract', 60000)
.clickLaunchIcon('udapp')
.click('*[data-id="Deploy - transact (not payable)"]')
.waitForElementPresent('*[data-id="universalDappUiContractActionWrapper"]', 60000)
.journalLastChildIncludes('Contract.(constructor)')
.journalLastChildIncludes('data: 0x602...0565b')
.end()
}
}
@ -387,4 +404,19 @@ const configFile = `
"evmVersion": "byzantium"
}
}
`
const yulSample = `
object "Contract" {
code {
function power(base, exponent) -> result
{
result := 1
for { let i := 0 } lt(i, exponent) { i := add(i, 1) }
{
result := mul(result, base)
}
}
}
}
`

@ -4,10 +4,11 @@ import { NightwatchBrowser } from 'nightwatch'
import init from '../helpers/init'
module.exports = {
'@disabled': true,
before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done, 'http://127.0.0.1:8080', true)
},
'Should find text': function (browser: NightwatchBrowser) {
'Should find text #group1': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="remixIdeSidePanel"]')
.click('*[plugin="search"]').waitForElementVisible('*[id="search_input"]')
.waitForElementVisible('*[id="search_include"]')
@ -26,7 +27,7 @@ module.exports = {
Array.isArray(res.value) && browser.assert.equal(res.value.length, 6)
})
},
'Should find text with exclude': function (browser: NightwatchBrowser) {
'Should find text with exclude #group1': function (browser: NightwatchBrowser) {
browser
.clearValue('*[id="search_input"]')
.setValue('*[id="search_input"]', 'contract').pause(1000)
@ -42,7 +43,7 @@ module.exports = {
.clearValue('*[id="search_include"]').setValue('*[id="search_include"]', '*.sol, *.js, *.txt')
.clearValue('*[id="search_exclude"]').setValue('*[id="search_exclude"]', '.*/**/*')
},
'Should find regex': function (browser: NightwatchBrowser) {
'Should find regex #group1': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[id="search_input"]')
.clearValue('*[id="search_input"]').pause(2000)
@ -57,7 +58,7 @@ module.exports = {
Array.isArray(res.value) && browser.assert.equal(res.value.length, 4)
})
},
'Should find matchcase': function (browser: NightwatchBrowser) {
'Should find matchcase #group1': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="search_use_regex"]').click('*[data-id="search_use_regex"]')
.waitForElementVisible('*[data-id="search_case_sensitive"]').click('*[data-id="search_case_sensitive"]').pause(4000)
@ -71,7 +72,7 @@ module.exports = {
})
.waitForElementContainsText('*[data-id="search_results"]', 'STORAGE.TEST.JS', 60000)
},
'Should find matchword': function (browser: NightwatchBrowser) {
'Should find matchword #group1': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="search_case_sensitive"]').click('*[data-id="search_case_sensitive"]')
.waitForElementVisible('*[data-id="search_whole_word"]').click('*[data-id="search_whole_word"]').pause(2000)
@ -81,7 +82,7 @@ module.exports = {
Array.isArray(res.value) && browser.assert.equal(res.value.length, 15)
})
},
'Should replace text': function (browser: NightwatchBrowser) {
'Should replace text #group1': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="toggle_replace"]').click('*[data-id="toggle_replace"]')
.waitForElementVisible('*[id="search_replace"]')
@ -95,7 +96,7 @@ module.exports = {
browser.assert.ok(content.includes('replacing deployer for a constructor'), 'should replace text ok')
})
},
'Should replace text without confirmation': function (browser: NightwatchBrowser) {
'Should replace text without confirmation #group1': function (browser: NightwatchBrowser) {
browser.click('*[data-id="confirm_replace_label"]').pause(500)
.clearValue('*[id="search_input"]')
.setValue('*[id="search_input"]', 'replacing').sendKeys('*[id="search_input"]', browser.Keys.ENTER).pause(1000)
@ -108,7 +109,7 @@ module.exports = {
browser.assert.ok(content.includes('replacing2 deployer for a constructor'), 'should replace text ok')
})
},
'Should replace all & undo': function (browser: NightwatchBrowser) {
'Should replace all & undo #group1': function (browser: NightwatchBrowser) {
browser
.clearValue('*[id="search_input"]')
.setValue('*[id="search_input"]', 'storage').sendKeys('*[id="search_input"]', browser.Keys.ENTER)
@ -127,7 +128,7 @@ module.exports = {
browser.assert.ok(content.includes('title Storage'), 'should undo text ok')
})
},
'Should replace all & undo & switch between files': function (browser: NightwatchBrowser) {
'Should replace all & undo & switch between files #group1': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[id="search_input"]')
.clearValue('*[id="search_input"]')
.setValue('*[id="search_input"]', 'storage').sendKeys('*[id="search_input"]', browser.Keys.ENTER)
@ -163,14 +164,20 @@ module.exports = {
browser.assert.ok(content.includes("Storage' contract"), 'should replace text ok')
})
},
'Should hide button when edited content is the same': function (browser: NightwatchBrowser) {
'Should hide button when edited content is the same #group2': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="remixIdeSidePanel"]')
.addFile('test.sol', { content: '123' })
.click('*[plugin="search"]').waitForElementVisible('*[id="search_input"]')
.click('*[plugin="search"]')
.waitForElementVisible('*[id="search_input"]')
.waitForElementVisible('*[data-id="toggle_replace"]')
.click('*[data-id="toggle_replace"]')
.clearValue('*[id="search_input"]')
.setValue('*[id="search_input"]', '123').sendKeys('*[id="search_input"]', browser.Keys.ENTER)
.setValue('*[id="search_input"]', '123')
.sendKeys('*[id="search_input"]', browser.Keys.ENTER)
.waitForElementVisible('*[id="search_replace"]')
.clearValue('*[id="search_replace"]')
.setValue('*[id="search_replace"]', '456').pause(1000)
.click('*[data-id="confirm_replace_label"]').pause(500)
.waitForElementVisible('*[data-id="replace-all-test.sol"]')
.click('*[data-id="replace-all-test.sol"]').pause(2000)
.getEditorValue((content) => {
@ -181,13 +188,14 @@ module.exports = {
.getEditorValue((content) => {
browser.assert.ok(content.includes('123'), 'should have text ok')
}
).pause(1000)
).pause(5000)
.waitForElementNotPresent('*[data-id="undo-replace-test.sol"]')
},
'Should disable/enable button when edited content changed': function (browser: NightwatchBrowser) {
'Should disable/enable button when edited content changed #group2': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[id="search_input"]')
.clearValue('*[id="search_input"]')
.clearValue('*[id="search_input"]')
.setValue('*[id="search_input"]', '123').sendKeys('*[id="search_input"]', browser.Keys.ENTER)
.clearValue('*[id="search_replace"]')
.setValue('*[id="search_replace"]', 'replaced').pause(1000)
@ -201,7 +209,7 @@ module.exports = {
.getEditorValue((content) => {
browser.assert.ok(content.includes('changed'), 'should have text ok')
}
).pause(1000)
).pause(5000)
.waitForElementVisible('*[data-id="undo-replace-test.sol"]')
.getAttribute('[data-id="undo-replace-test.sol"]', 'disabled', (result) => {
browser.assert.equal(result.value, 'true', 'should be disabled')
@ -222,7 +230,7 @@ module.exports = {
.waitForElementNotPresent('*[data-id="undo-replace-test.sol"]')
},
'should clear search': function (browser: NightwatchBrowser) {
'should clear search #group2': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[id="search_input"]')
.setValue('*[id="search_input"]', 'nodata').sendKeys('*[id="search_input"]', browser.Keys.ENTER).pause(1000)
@ -230,4 +238,4 @@ module.exports = {
Array.isArray(res.value) && browser.assert.equal(res.value.length, 0)
})
}
}
}

@ -7,7 +7,6 @@ module.exports = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done, 'http://127.0.0.1:8080?plugins=solidity,udapp', false)
},
'Should execution a simple console command #group1 #group999': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="terminalCli"]', 10000)
@ -164,10 +163,9 @@ module.exports = {
'Should print hardhat logs #group4': function (browser: NightwatchBrowser) {
browser
.click('*[data-id="terminalClearConsole"]') // clear the terminal
.addFile('printHardhatlog.sol', { content: hardhatLog })
.clickLaunchIcon('solidity')
.waitForElementVisible('[for="autoCompile"]')
.click('[for="autoCompile"]')
.clickLaunchIcon('udapp')
.testContracts('printHardhatlog.sol', { content: hardhatLog }, ['OwnerTest'])
.clickLaunchIcon('udapp')
.click('*[data-id="deployAndRunClearInstances"]')

@ -5,7 +5,39 @@ import init from '../helpers/init'
import examples from '../examples/example-contracts'
const sources = [
{ 'Untitled.sol': { content: examples.ballot.content } }
{ 'Untitled.sol': { content: examples.ballot.content } },
{
'myTokenV1.sol': {
content: `
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
contract MyToken is Initializable, ERC721Upgradeable, OwnableUpgradeable, UUPSUpgradeable {
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
function initialize() initializer public {
__ERC721_init("MyToken", "MTK");
__Ownable_init();
__UUPSUpgradeable_init();
}
function _authorizeUpgrade(address newImplementation)
internal
onlyOwner
override
{}
}
`
}
}
]
module.exports = {
@ -76,7 +108,58 @@ module.exports = {
.openFile('ethereum/remix-project/apps/remix-ide/contracts/app/solidity/mode.sol')
},
'Should load using URL compiler params': function (browser: NightwatchBrowser) {
'Should load the code from language & code params': function (browser: NightwatchBrowser) {
browser
.pause(5000)
.url('http://127.0.0.1:8080/#language=yul&version=soljson-v0.8.7+commit.e28d00a7.js&code=Ly8gQSBjb250cmFjdCBjb25zaXN0cyBvZiBhIHNpbmdsZSBvYmplY3Qgd2l0aCBzdWItb2JqZWN0cyByZXByZXNlbnRpbmcKLy8gdGhlIGNvZGUgdG8gYmUgZGVwbG95ZWQgb3Igb3RoZXIgY29udHJhY3RzIGl0IGNhbiBjcmVhdGUuCi8vIFRoZSBzaW5nbGUgImNvZGUiIG5vZGUgaXMgdGhlIGV4ZWN1dGFibGUgY29kZSBvZiB0aGUgb2JqZWN0LgovLyBFdmVyeSAob3RoZXIpIG5hbWVkIG9iamVjdCBvciBkYXRhIHNlY3Rpb24gaXMgc2VyaWFsaXplZCBhbmQKLy8gbWFkZSBhY2Nlc3NpYmxlIHRvIHRoZSBzcGVjaWFsIGJ1aWx0LWluIGZ1bmN0aW9ucyBkYXRhY29weSAvIGRhdGFvZmZzZXQgLyBkYXRhc2l6ZQovLyBUaGUgY3VycmVudCBvYmplY3QsIHN1Yi1vYmplY3RzIGFuZCBkYXRhIGl0ZW1zIGluc2lkZSB0aGUgY3VycmVudCBvYmplY3QKLy8gYXJlIGluIHNjb3BlLgpvYmplY3QgIkNvbnRyYWN0MSIgewogICAgLy8gVGhpcyBpcyB0aGUgY29uc3RydWN0b3IgY29kZSBvZiB0aGUgY29udHJhY3QuCiAgICBjb2RlIHsKICAgICAgICBmdW5jdGlvbiBhbGxvY2F0ZShzaXplKSAtPiBwdHIgewogICAgICAgICAgICBwdHIgOj0gbWxvYWQoMHg0MCkKICAgICAgICAgICAgaWYgaXN6ZXJvKHB0cikgeyBwdHIgOj0gMHg2MCB9CiAgICAgICAgICAgIG1zdG9yZSgweDQwLCBhZGQocHRyLCBzaXplKSkKICAgICAgICB9CgogICAgICAgIC8vIGZpcnN0IGNyZWF0ZSAiQ29udHJhY3QyIgogICAgICAgIGxldCBzaXplIDo9IGRhdGFzaXplKCJDb250cmFjdDIiKQogICAgICAgIGxldCBvZmZzZXQgOj0gYWxsb2NhdGUoc2l6ZSkKICAgICAgICAvLyBUaGlzIHdpbGwgdHVybiBpbnRvIGNvZGVjb3B5IGZvciBFVk0KICAgICAgICBkYXRhY29weShvZmZzZXQsIGRhdGFvZmZzZXQoIkNvbnRyYWN0MiIpLCBzaXplKQogICAgICAgIC8vIGNvbnN0cnVjdG9yIHBhcmFtZXRlciBpcyBhIHNpbmdsZSBudW1iZXIgMHgxMjM0CiAgICAgICAgbXN0b3JlKGFkZChvZmZzZXQsIHNpemUpLCAweDEyMzQpCiAgICAgICAgcG9wKGNyZWF0ZShvZmZzZXQsIGFkZChzaXplLCAzMiksIDApKQoKICAgICAgICAvLyBub3cgcmV0dXJuIHRoZSBydW50aW1lIG9iamVjdCAodGhlIGN1cnJlbnRseQogICAgICAgIC8vIGV4ZWN1dGluZyBjb2RlIGlzIHRoZSBjb25zdHJ1Y3RvciBjb2RlKQogICAgICAgIHNpemUgOj0gZGF0YXNpemUoIkNvbnRyYWN0MV9kZXBsb3llZCIpCiAgICAgICAgb2Zmc2V0IDo9IGFsbG9jYXRlKHNpemUpCiAgICAgICAgLy8gVGhpcyB3aWxsIHR1cm4gaW50byBhIG1lbW9yeS0+bWVtb3J5IGNvcHkgZm9yIEV3YXNtIGFuZAogICAgICAgIC8vIGEgY29kZWNvcHkgZm9yIEVWTQogICAgICAgIGRhdGFjb3B5KG9mZnNldCwgZGF0YW9mZnNldCgiQ29udHJhY3QxX2RlcGxveWVkIiksIHNpemUpCiAgICAgICAgcmV0dXJuKG9mZnNldCwgc2l6ZSkKICAgIH0KCiAgICBkYXRhICJUYWJsZTIiIGhleCI0MTIzIgoKICAgIG9iamVjdCAiQ29udHJhY3QxX2RlcGxveWVkIiB7CiAgICAgICAgY29kZSB7CiAgICAgICAgICAgIGZ1bmN0aW9uIGFsbG9jYXRlKHNpemUpIC0+IHB0ciB7CiAgICAgICAgICAgICAgICBwdHIgOj0gbWxvYWQoMHg0MCkKICAgICAgICAgICAgICAgIGlmIGlzemVybyhwdHIpIHsgcHRyIDo9IDB4NjAgfQogICAgICAgICAgICAgICAgbXN0b3JlKDB4NDAsIGFkZChwdHIsIHNpemUpKQogICAgICAgICAgICB9CgogICAgICAgICAgICAvLyBydW50aW1lIGNvZGUKCiAgICAgICAgICAgIG1zdG9yZSgwLCAiSGVsbG8sIFdvcmxkISIpCiAgICAgICAgICAgIHJldHVybigwLCAweDIwKQogICAgICAgIH0KICAgIH0KCiAgICAvLyBFbWJlZGRlZCBvYmplY3QuIFVzZSBjYXNlIGlzIHRoYXQgdGhlIG91dHNpZGUgaXMgYSBmYWN0b3J5IGNvbnRyYWN0LAogICAgLy8gYW5kIENvbnRyYWN0MiBpcyB0aGUgY29kZSB0byBiZSBjcmVhdGVkIGJ5IHRoZSBmYWN0b3J5CiAgICBvYmplY3QgIkNvbnRyYWN0MiIgewogICAgICAgIGNvZGUgewogICAgICAgICAgICAvLyBjb2RlIGhlcmUgLi4uCiAgICAgICAgfQoKICAgICAgICBvYmplY3QgIkNvbnRyYWN0Ml9kZXBsb3llZCIgewogICAgICAgICAgICBjb2RlIHsKICAgICAgICAgICAgICAgIC8vIGNvZGUgaGVyZSAuLi4KICAgICAgICAgICAgfQogICAgICAgIH0KCiAgICAgICAgZGF0YSAiVGFibGUxIiBoZXgiNDEyMyIKICAgIH0KfQ&optimize=false&runs=200&evmVersion=null')
.refresh()
.pause(5000)
.clickLaunchIcon('filePanel')
.currentWorkspaceIs('code-sample')
.waitForElementVisible('*[data-id="treeViewLitreeViewItemcontract-eaa022e37e.yul"]', 6000)
.openFile('contract-eaa022e37e.yul')
.getEditorValue((content) => {
browser.assert.ok(content && content.indexOf(
'object "Contract1" {') !== -1)
})
},
'Should select deploy with proxy option from URL params': function (browser: NightwatchBrowser) {
browser
.url('http://127.0.0.1:8080/#optimize=false&runs=200&deployProxy=true')
.refresh()
.pause(5000)
.switchWorkspace('default_workspace')
.addFile('myTokenV1.sol', sources[1]['myTokenV1.sol'])
.clickLaunchIcon('solidity')
.pause(2000)
.click('[data-id="compilerContainerCompileBtn"]')
.waitForElementPresent('select[id="compiledContracts"] option[value=MyToken]', 60000)
.clickLaunchIcon('udapp')
.click('select.udapp_contractNames')
.click('select.udapp_contractNames option[value=MyToken]')
.waitForElementPresent('[data-id="contractGUIDeployWithProxyLabel"]')
.expect.element('[data-id="contractGUIDeployWithProxy"]').to.be.selected
},
'Should select upgrade with proxy option from URL params': function (browser: NightwatchBrowser) {
browser
.url('http://127.0.0.1:8080/#optimize=false&runs=200&upgradeProxy=true')
.refresh()
.pause(5000)
.openFile('myTokenV1.sol')
.clickLaunchIcon('solidity')
.pause(2000)
.click('[data-id="compilerContainerCompileBtn"]')
.waitForElementPresent('select[id="compiledContracts"] option[value=MyToken]', 60000)
.clickLaunchIcon('udapp')
.click('select.udapp_contractNames')
.click('select.udapp_contractNames option[value=MyToken]')
.waitForElementPresent('[data-id="contractGUIUpgradeImplementationLabel"]')
.expect.element('[data-id="contractGUIUpgradeImplementation"]').to.be.selected
},
'Should load using various URL compiler params': function (browser: NightwatchBrowser) {
browser
.pause(5000)
.url('http://127.0.0.1:8080/#optimize=true&runs=300&autoCompile=true&evmVersion=istanbul&version=soljson-v0.7.4+commit.3f05b770.js&language=Yul')
@ -90,6 +173,16 @@ module.exports = {
.verify.elementPresent('#optimize:checked')
.verify.elementPresent('#autoCompile:checked')
.verify.attributeEquals('#runs', 'value', '300')
.url('http://127.0.0.1:8080/#version=0.8.7')
.refresh()
.pause(5000)
.clickLaunchIcon('solidity')
.assert.containsText('#versionSelector option[data-id="selected"]', '0.8.7+commit.e28d00a7')
.url('http://127.0.0.1:8080/#version=0.8.15+commit.e14f2714')
.refresh()
.pause(5000)
.clickLaunchIcon('solidity')
.assert.containsText('#versionSelector option[data-id="selected"]', '0.8.15+commit.e14f2714')
},
'Should load using compiler from link passed in remix URL': function (browser: NightwatchBrowser) {

@ -0,0 +1,96 @@
'use strict'
import { NightwatchBrowser } from 'nightwatch'
import init from '../helpers/init'
declare global {
interface Window { testplugin: { name: string, url: string }; }
}
module.exports = {
'@disabled': true,
before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done, null, true, { name: 'vyper', url: 'http://127.0.0.1:5002'})
},
'Should connect to vyper plugin #group1': function (browser: NightwatchBrowser) {
browser.clickLaunchIcon('pluginManager')
.scrollAndClick('[data-id="pluginManagerComponentActivateButtonvyper"]')
.clickLaunchIcon('vyper')
.pause(5000)
// @ts-ignore
.frame(0)
},
'Should add the Ballot.vy #group1': function (browser: NightwatchBrowser) {
browser.click('button[data-id="add-ballot"]')
.frameParent()
.openFile('ballot.vy')
},
'Compile ballot.vy should error #group1': function (browser: NightwatchBrowser) {
browser.clickLaunchIcon('vyper')
// @ts-ignore
.frame(0)
.click('[data-id="remote-compiler"]')
.click('[data-id="compile"]')
.assert.containsText('[data-id="error-message"]', 'unexpected indent')
},
'Compile test contract should success #group1': function (browser: NightwatchBrowser) {
let contractAddress
browser
.frameParent()
.addFile('test.vy', { content: testContract })
.clickLaunchIcon('vyper')
// @ts-ignore
.frame(0)
.click('[data-id="compile"]')
.frameParent()
.clickLaunchIcon('udapp')
.createContract('')
.clickInstance(0)
.clickFunction('totalPokemonCount - call')
.getAddressAtPosition(0, (address) => {
console.log('Vyper contract ' + address)
contractAddress = address
})
.perform((done) => {
browser.verifyCallReturnValue(contractAddress, ['0:uint256: 0'])
.perform(() => done())
})
}
}
const testContract = `
# @version >=0.2.4 <0.3.0
DNA_DIGITS: constant(uint256) = 16
DNA_MODULUS: constant(uint256) = 10 ** DNA_DIGITS
# add HP_LIMIT
struct Pokemon:
name: String[32]
dna: uint256
HP: uint256
matches: uint256
wins: uint256
totalPokemonCount: public(uint256)
pokemonList: HashMap[uint256, Pokemon]
@pure
@internal
def _generateRandomDNA(_name: String[32]) -> uint256:
random: uint256 = convert(keccak256(_name), uint256)
return random % DNA_MODULUS
# modify _createPokemon
@internal
def _createPokemon(_name: String[32], _dna: uint256, _HP: uint256):
self.pokemonList[self.totalPokemonCount] = Pokemon({
name: _name,
dna: _dna,
HP: _HP,
matches: 0,
wins: 0
})
self.totalPokemonCount += 1`

@ -0,0 +1,25 @@
#!/usr/bin/env bash
set -e
BUILD_ID=${CIRCLE_BUILD_NUM:-${TRAVIS_JOB_NUMBER}}
echo "$BUILD_ID"
TEST_EXITCODE=0
yarn run serve:production &
npx nx serve vyper &
sleep 5
yarn run build:e2e
TESTFILES=$(grep -IRiL "\'@disabled\': \?true" "dist/apps/remix-ide-e2e/src/tests" | grep "vyper_api" | sort | circleci tests split )
for TESTFILE in $TESTFILES; do
npx nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js $TESTFILE --env=chrome || TEST_EXITCODE=1
done
echo "$TEST_EXITCODE"
if [ "$TEST_EXITCODE" -eq 1 ]
then
exit 1
fi

@ -410,6 +410,29 @@ class AppComponent {
this.appManager.call(...callDetails).catch(console.error)
}
}
if (params.calls) {
const calls = params.calls.split("///");
// call all functions in the list, one after the other
for (const call of calls) {
const callDetails = call.split("//");
if (callDetails.length > 1) {
this.appManager.call(
"notification",
"toast",
`initiating ${callDetails[0]} ...`
);
// @todo(remove the timeout when activatePlugin is on 0.3.0)
try {
await this.appManager.call(...callDetails)
} catch (e) {
console.error(e)
}
}
}
}
})
.catch(console.error)
}

@ -101,7 +101,7 @@ export abstract class AbstractProvider extends Plugin {
reject('Unable to connect')
}
}, 2000)
await this.provider.ready
await this.provider.detectNetwork() // this throws if the network cannot be detected
this.connected = true
} catch (e) {
this.switchAway(true)
@ -116,6 +116,7 @@ export abstract class AbstractProvider extends Plugin {
}
private async switchAway (showError) {
if (!this.provider) return
this.provider = null
this.blocked = true
this.connected = false

@ -146,9 +146,11 @@ export class RemixAppManager extends PluginManager {
}
}
}
return plugins.map(plugin => {
const testPluginName = localStorage.getItem('test-plugin-name')
const testPluginUrl = localStorage.getItem('test-plugin-url')
return plugins.map(plugin => {
if (plugin.name === testPluginName) plugin.url = testPluginUrl
return new IframePlugin(plugin)
// return new IframeReactPlugin(plugin)
})
}

@ -0,0 +1,13 @@
{
"presets": [
[
"@nrwl/react/babel", {
"runtime": "automatic"
}
]
],
"plugins": [
]
}

@ -0,0 +1,16 @@
# This file is used by:
# 1. autoprefixer to adjust CSS to support the below specified browsers
# 2. babel preset-env to adjust included polyfills
#
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
#
# If you need to support different browsers in production, you may tweak the list below.
last 1 Chrome version
last 1 Firefox version
last 2 Edge major versions
last 2 Safari major version
last 2 iOS major versions
Firefox ESR
not IE 9-11 # For IE 9-11 support, remove 'not'.

@ -0,0 +1,18 @@
{
"env": {
"browser": true,
"es6": true
},
"extends": "../../.eslintrc.json",
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parserOptions": {
"ecmaVersion": 11,
"sourceType": "module"
},
"rules": {
"standard/no-callback-literal": "off"
}
}

@ -0,0 +1,124 @@
* {
box-sizing: border-box;
}
html, body, #root, main {
height: 100%;
margin: 0;
}
#vyper-plugin header {
display: flex;
justify-content: space-between;
align-items: center;
height: 50px;
padding: 5px 15px;
}
#vyper-plugin header a,
#vyper-plugin header .title {
display: flex;
height: 60%;
}
#vyper-plugin header svg,
#vyper-plugin header img {
width: auto;
height: 100%;
}
#vyper-plugin section {
display: flex;
flex-direction: column;
align-items: center;
height: calc(100% - 50px);
}
.btn-group-toggle {
width: 90%;
text-transform: uppercase;
margin: 10px 0;
}
.btn-group-toggle .btn {
cursor: pointer;
font-size: 0.8rem;
}
#local-url {
width: 90%;
}
#compile-btn {
width: 90%;
}
#compile-btn * {
width: 100%;
}
#result {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
padding-top: 10px;
overflow: hidden;
}
#result.error {
justify-content: flex-start;
}
#result.error svg {
width: 40px;
height: 40px;
margin: 10px;
}
#result nav {
width: 100%;
flex-wrap: nowrap;
justify-content: space-evenly;
}
#result nav a {
padding: 0.5rem 1rem;
font-size: 0.8rem;
}
#result .tab-content {
flex: 1;
width: 100%;
padding: 15px;
overflow: auto;
}
#result .tab-pane.active {
height: 100%;
display: flex;
flex-direction: column;
align-items: stretch;
}
#result .copy {
padding: 5x;
font-size: 0.8rem;
margin: 10px;
}
#result p {
text-align: center;
width: 100% !important;
}
#result .react-json-view {
overflow-y: auto;
}
#result textarea {
border: none;
height: 100%;
width: 100%;
}

@ -0,0 +1,118 @@
import React, { useState, useEffect } from 'react'
import { VyperCompilationOutput, remixClient } from './utils'
import { CompilationResult } from '@remixproject/plugin-api'
// Components
import CompilerButton from './components/CompilerButton'
import WarnRemote from './components/WarnRemote'
import VyperResult from './components/VyperResult'
import LocalUrlInput from './components/LocalUrl'
import ToggleButtonGroup from 'react-bootstrap/ToggleButtonGroup'
import ToggleButton from 'react-bootstrap/ToggleButton'
import vyperLogo from './logo.svg'
import './app.css'
interface AppState {
status: 'idle' | 'inProgress'
environment: 'remote' | 'local'
compilationResult?: CompilationResult
localUrl: string
}
interface OutputMap {
[fileName: string]: VyperCompilationOutput
}
const App: React.FC = () => {
const [contract, setContract] = useState<string>()
const [output, setOutput] = useState<OutputMap>({})
const [state, setState] = useState<AppState>({
status: 'idle',
environment: 'local',
localUrl: 'http://localhost:8000/compile'
})
useEffect(() => {
async function start() {
try {
await remixClient.loaded()
remixClient.onFileChange(name => setContract(name))
const name = await remixClient.getContractName()
setContract(name)
} catch (err) {
console.log(err)
}
}
start()
}, [])
/** Update the environment state value */
function setEnvironment(environment: 'local' | 'remote') {
setState({ ...state, environment })
}
function setLocalUrl(url: string) {
setState({ ...state, localUrl: url })
}
function compilerUrl() {
return state.environment === 'remote'
? 'https://vyper.live/compile'
: state.localUrl
}
return (
<main id="vyper-plugin">
<header className="bg-light">
<div className="title">
<img src={vyperLogo} alt="Vyper logo" />
<h4>yper Compiler</h4>
</div>
<a
rel="noopener noreferrer"
href="https://github.com/GrandSchtroumpf/vyper-remix"
target="_blank"
>
<i className="fab fa-github"></i>
</a>
</header>
<section>
<ToggleButtonGroup
name="remote"
onChange={setEnvironment}
type="radio"
value={state.environment}
>
<ToggleButton data-id="remote-compiler" variant="secondary" name="remote" value="remote">
Remote Compiler
</ToggleButton>
<ToggleButton data-id="local-compiler" variant="secondary" name="local" value="local">
Local Compiler
</ToggleButton>
</ToggleButtonGroup>
<LocalUrlInput
url={state.localUrl}
setUrl={setLocalUrl}
environment={state.environment}
/>
<WarnRemote environment={state.environment} />
<div id="compile-btn">
<CompilerButton
compilerUrl={compilerUrl()}
contract={contract}
setOutput={(name, update) =>
setOutput({ ...output, [name]: update })
}
/>
</div>
<article id="result">
<VyperResult output={contract ? output[contract] : undefined} />
</article>
</section>
</main>
)
}
export default App

@ -0,0 +1,75 @@
import React from 'react'
import {
isVyper,
compile,
toStandardOutput,
VyperCompilationOutput,
isCompilationError,
remixClient
} from '../utils'
import Button from 'react-bootstrap/Button'
interface Props {
compilerUrl: string
contract?: string,
setOutput: (name: string, output: VyperCompilationOutput) => void
}
function CompilerButton({ contract, setOutput, compilerUrl }: Props) {
if (!contract || !contract) {
return <Button disabled>No contract selected</Button>
}
if (!isVyper(contract)) {
return <Button disabled>Not a vyper contract</Button>
}
/** Compile a Contract */
async function compileContract() {
try {
const _contract = await remixClient.getContract()
remixClient.changeStatus({
key: 'loading',
type: 'info',
title: 'Compiling'
})
const output = await compile(compilerUrl, _contract)
setOutput(_contract.name, output)
// ERROR
if (isCompilationError(output)) {
const line = output.line
const lineColumnPos = {
start: { line: line - 1 },
end: { line: line - 1 }
}
remixClient.highlight(lineColumnPos as any, _contract.name, '#e0b4b4')
throw new Error(output.message)
}
// SUCCESS
remixClient.discardHighlight()
remixClient.changeStatus({
key: 'succeed',
type: 'success',
title: 'succeed'
})
const data = toStandardOutput(_contract.name, output)
remixClient.compilationFinish(_contract.name, _contract.content, data)
} catch (err: any) {
remixClient.changeStatus({
key: 'failed',
type: 'error',
title: err.message
})
console.error(err)
}
}
return (
<Button data-id="compile" onClick={compileContract} variant="primary">
Compile {contract}
</Button>
)
}
export default CompilerButton

@ -0,0 +1,36 @@
import React from 'react'
import Form from 'react-bootstrap/Form'
interface Props {
url: string
setUrl: (url: string) => void,
environment: 'remote' | 'local'
}
function LocalUrlInput({ url, setUrl, environment }: Props) {
if (environment === 'remote') {
return <></>
}
function updateUrl(event: React.FocusEvent<HTMLInputElement>) {
setUrl(event.target.value)
}
return (
<Form id="local-url">
<Form.Group controlId="localUrl">
<Form.Label>Local Compiler Url</Form.Label>
<Form.Control onBlur={updateUrl}
defaultValue={url}
type="email"
placeholder="eg http://localhost:8000/compile" />
<Form.Text className="text-muted">
The url to your local compiler
</Form.Text>
</Form.Group>
</Form>
)
}
export default LocalUrlInput;

@ -0,0 +1,70 @@
import React, { useState } from 'react';
import {
VyperCompilationResult,
VyperCompilationOutput,
isCompilationError,
remixClient
} from '../utils';
import Tabs from 'react-bootstrap/Tabs'
import Tab from 'react-bootstrap/Tab'
import { Ballot } from '../examples/ballot';
import Button from 'react-bootstrap/Button';
import JSONTree from 'react-json-view'
import { CopyToClipboard } from '@remix-ui/clipboard'
interface VyperResultProps {
output?: VyperCompilationOutput;
}
function VyperResult({ output }: VyperResultProps) {
const [ active, setActive ] = useState<keyof VyperCompilationResult>('abi');
if (!output) return (
<div id="result">
<p>No contract compiled yet.</p>
<Button data-id="add-ballot" variant="info" onClick={() => remixClient.loadContract(Ballot)}>
Create Ballot.vy example
</Button>
</div>
)
if (isCompilationError(output)) {
return (
<div id="result" className="error">
<i className="fas fa-exclamation-circle text-danger"></i>
<p data-id="error-message" className="alert alert-danger">{output.message}</p>
</div>)
}
return (
<Tabs id="result" activeKey={active} onSelect={(key: any) => setActive(key)}>
<Tab eventKey="abi" title="ABI">
<CopyToClipboard getContent={() => JSON.stringify(output.abi)}>
<Button variant="info" className="copy">Copy ABI</Button>
</CopyToClipboard>
<JSONTree src={output.abi} />
</Tab>
<Tab eventKey="bytecode" title="Bytecode">
<CopyToClipboard getContent={() => output.bytecode}>
<Button variant="info" className="copy">Copy Bytecode</Button>
</CopyToClipboard>
<textarea defaultValue={output.bytecode}></textarea>
</Tab>
<Tab eventKey="bytecode_runtime" title="Runtime Bytecode">
<CopyToClipboard getContent={() => output.bytecode_runtime}>
<Button variant="info" className="copy">Copy Runtime Bytecode</Button>
</CopyToClipboard>
<textarea defaultValue={output.bytecode_runtime}></textarea>
</Tab>
<Tab eventKey="ir" title="LLL">
<CopyToClipboard getContent={() => output.ir}>
<Button variant="info" className="copy">Copy LLL Code</Button>
</CopyToClipboard>
<textarea defaultValue={output.ir}></textarea>
</Tab>
</Tabs>
);
}
export default VyperResult;

@ -0,0 +1,19 @@
import React from 'react'
interface Props {
environment: 'remote' | 'local'
}
function WarnRemoteLabel({ environment }: Props) {
if (environment === 'local') {
return <></>
}
return (
<div className="alert alert-warning">It is really important to not use the remote compiler for production environment.
Please only use it for testing purpose and prefer to using a local compiler for production like environment.</div>
)
}
export default WarnRemoteLabel;

@ -0,0 +1,161 @@
export const Ballot = {
name: 'browser/ballot.vy',
content: `# Voting with delegation.
# Information about voters
struct Voter:
# weight is accumulated by delegation
weight: int128
# if true, that person already voted (which includes voting by delegating)
voted: bool
# person delegated to
delegate: address
# index of the voted proposal, which is not meaningful unless 'voted' is True.
vote: int128
# Users can create proposals
struct Proposal:
# short name (up to 32 bytes)
name: bytes32
# number of accumulated votes
voteCount: int128
voters: public(map(address, Voter))
proposals: public(map(int128, Proposal))
voterCount: public(int128)
chairperson: public(address)
int128Proposals: public(int128)
@public
@constant
def delegated(addr: address) -> bool:
return self.voters[addr].delegate != ZERO_ADDRESS
@public
@constant
def directlyVoted(addr: address) -> bool:
return self.voters[addr].voted and (self.voters[addr].delegate == ZERO_ADDRESS)
# Setup global variables
@public
def __init__(_proposalNames: bytes32[2]):
self.chairperson = msg.sender
self.voterCount = 0
for i in range(2):
self.proposals[i] = Proposal({
name: _proposalNames[i],
voteCount: 0
})
self.int128Proposals += 1
# Give a 'voter' the right to vote on this ballot.
# This may only be called by the 'chairperson'.
@public
def giveRightToVote(voter: address):
# Throws if the sender is not the chairperson.
assert msg.sender == self.chairperson
# Throws if the voter has already voted.
assert not self.voters[voter].voted
# Throws if the voter's voting weight isn't 0.
assert self.voters[voter].weight == 0
self.voters[voter].weight = 1
self.voterCount += 1
# Used by 'delegate' below, and can be called by anyone.
@public
def forwardWeight(delegate_with_weight_to_forward: address):
assert self.delegated(delegate_with_weight_to_forward)
# Throw if there is nothing to do:
assert self.voters[delegate_with_weight_to_forward].weight > 0
target: address = self.voters[delegate_with_weight_to_forward].delegate
for i in range(4):
if self.delegated(target):
target = self.voters[target].delegate
# The following effectively detects cycles of length <= 5,
# in which the delegation is given back to the delegator.
# This could be done for any int128ber of loops,
# or even infinitely with a while loop.
# However, cycles aren't actually problematic for correctness;
# they just result in spoiled votes.
# So, in the production version, this should instead be
# the responsibility of the contract's client, and this
# check should be removed.
assert target != delegate_with_weight_to_forward
else:
# Weight will be moved to someone who directly voted or
# hasn't voted.
break
weight_to_forward: int128 = self.voters[delegate_with_weight_to_forward].weight
self.voters[delegate_with_weight_to_forward].weight = 0
self.voters[target].weight += weight_to_forward
if self.directlyVoted(target):
self.proposals[self.voters[target].vote].voteCount += weight_to_forward
self.voters[target].weight = 0
# To reiterate: if target is also a delegate, this function will need
# to be called again, similarly to as above.
# Delegate your vote to the voter 'to'.
@public
def delegate(to: address):
# Throws if the sender has already voted
assert not self.voters[msg.sender].voted
# Throws if the sender tries to delegate their vote to themselves or to
# the default address value of 0x0000000000000000000000000000000000000000
# (the latter might not be problematic, but I don't want to think about it).
assert to != msg.sender
assert to != ZERO_ADDRESS
self.voters[msg.sender].voted = True
self.voters[msg.sender].delegate = to
# This call will throw if and only if this delegation would cause a loop
# of length <= 5 that ends up delegating back to the delegator.
self.forwardWeight(msg.sender)
# Give your vote (including votes delegated to you)
# to proposal 'proposals[proposal].name'.
@public
def vote(proposal: int128):
# can't vote twice
assert not self.voters[msg.sender].voted
# can only vote on legitimate proposals
assert proposal < self.int128Proposals
self.voters[msg.sender].vote = proposal
self.voters[msg.sender].voted = True
# transfer msg.sender's weight to proposal
self.proposals[proposal].voteCount += self.voters[msg.sender].weight
self.voters[msg.sender].weight = 0
# Computes the winning proposal taking all
# previous votes into account.
@public
@constant
def winningProposal() -> int128:
winning_vote_count: int128 = 0
winning_proposal: int128 = 0
for i in range(2):
if self.proposals[i].voteCount > winning_vote_count:
winning_vote_count = self.proposals[i].voteCount
winning_proposal = i
return winning_proposal
# Calls winningProposal() function to get the index
# of the winner contained in the proposals array and then
# returns the name of the winner
@public
@constant
def winnerName() -> bytes32:
return self.proposals[self.winningProposal()].name
`
}

@ -0,0 +1,14 @@
<svg id="Flat_Logo" data-name="Flat Logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2048 1773.62">
<title>vyper-logo-flat</title>
/*add logo description for screen readers*/
<desc>The vyper logo, consists of a letter V superimposed on the letter A, in shades of gray, black and white.</desc>
<polyline points="1024 886.81 768 1330.22 1024 1773.62 1280 1330.22 1024 886.81" style="fill:#333"/>
<polyline points="1280 443.41 1024 886.81 1280 1330.22 1536 886.81 1280 443.41" style="fill:#666"/>
<polyline points="768 443.41 512 886.81 768 1330.22 1024 886.81 768 443.41" style="fill:#666"/>
<polyline points="1536 0 1280 443.41 1536 886.81 1792 443.41 1536 0" style="fill:#8c8c8c"/>
<polyline points="1152 221.7 896 221.7 768 443.41 1024 886.81 1280 443.41 1152 221.7" style="fill:#8c8c8c"/>
<polyline points="512 0 256 443.41 512 886.81 768 443.41 512 0" style="fill:#8c8c8c"/>
<polyline points="2048 0 1536 0 1792 443.4 2048 0" style="fill:#b2b2b2"/>
<polyline points="512 0 0 0 256 443.4 512 0" style="fill:#b2b2b2"/>
/*Author: @yudetamago, last update: @evertonmelo*/
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg
className="material-icons"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
<path d="M0 0h24v24H0z" fill="none" />
<path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z" />
</svg>

After

Width:  |  Height:  |  Size: 347 B

@ -0,0 +1,139 @@
import { CompilationResult, ABIDescription } from "@remixproject/plugin-api";
export interface Contract {
name: string;
content: string;
}
export interface VyperCompilationResult {
status: 'success',
bytecode: string,
bytecode_runtime: string,
abi: ABIDescription[],
ir: string,
method_identifiers: {
[method: string]: string
}
}
export interface VyperCompilationError {
status: 'failed'
column: number
line: number
message: string
}
export type VyperCompilationOutput = VyperCompilationResult | VyperCompilationError
/** Check if the output is an error */
export function isCompilationError(output: VyperCompilationOutput): output is VyperCompilationError {
return output.status === 'failed'
}
/**
* Compile the a contract
* @param url The url of the compiler
* @param contract The name and content of the contract
*/
export async function compile(url: string, contract: Contract): Promise<VyperCompilationOutput> {
if (!contract.name) {
throw new Error('Set your Vyper contract file.')
}
const extension = contract.name.split('.')[1]
if (extension !== 'vy') {
throw new Error('Use extension .vy for Vyper.')
}
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ code: contract.content })
})
if (response.status === 404) {
throw new Error(`Vyper compiler not found at "${url}".`)
}
/*if (response.status === 400) {
throw new Error(`Vyper compilation failed: ${response.statusText}`)
}*/
return response.json()
}
/**
* Transform Vyper Output to Solidity-like Compiler output
* @param name Name of the contract file
* @param compilationResult Result returned by the compiler
*/
export function toStandardOutput(fileName: string, compilationResult: VyperCompilationResult): CompilationResult {
const contractName = fileName.split('/').slice(-1)[0].split('.')[0];
const methodIdentifiers = JSON.parse(JSON.stringify(compilationResult['method_identifiers']).replace(/0x/g,''));
return {
sources: {
[fileName]: {
id: 1,
ast: {} as any,
legacyAST: {} as any
}
},
contracts: {
[fileName]: {
// If the language used has no contract names, this field should equal to an empty string
[contractName]: {
// The Ethereum Contract ABI. If empty, it is represented as an empty array.
// See https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI
abi: compilationResult['abi'],
evm: {
bytecode: {
linkReferences: {},
object: compilationResult['bytecode'].replace('0x',''),
opcodes: ""
},
deployedBytecode: {
linkReferences: {},
object: compilationResult['bytecode_runtime'].replace('0x',''),
opcodes: ""
},
methodIdentifiers: methodIdentifiers
}
}
} as any
}
};
}
/*
export function createCompilationResultMessage(name: string, result: any) {
if(result.status == 'success') {
return {
bytecode: this.state.compilationResult['bytecode'],
bytecode_runtime: this.state.compilationResult['bytecode_runtime'],
abi: JSON.stringify(this.state.compilationResult['abi'], null , "\t"),
ir: this.state.compilationResult['ir']
}
} else if(result.status == 'failed' && result.column && result.line) {
const header = `${name}:${result.line}:${result.column}`
const body = this.state.compilationResult.message.split(/\r\n|\r|\n/)
const arr = [header].concat(body).join("\n")
return {
bytecode: arr,
bytecode_runtime: arr,
abi: arr,
ir: arr
}
} else if(result.status == 'failed') {
const message = this.state.compilationResult.message
return {
bytecode: message,
bytecode_runtime: message,
abi: message,
ir: message
}
}
return {
bytecode: "",
bytecode_runtime: "",
abi: "",
ir: ""
}
}
*/

@ -0,0 +1,12 @@
export * from './compiler'
export * from './remix-client'
export function contractName(fileName: string): string {
const parts = fileName.split('/')
return parts[parts.length - 1]
}
export function isVyper(name: string): boolean {
const parts = name.split('.')
return parts[parts.length - 1] === 'vy'
}

@ -0,0 +1,71 @@
import { HighlightPosition, CompilationResult, RemixApi } from '@remixproject/plugin-api';
import { Api, Status } from '@remixproject/plugin-utils';
import { createClient } from '@remixproject/plugin-webview'
import { PluginClient } from '@remixproject/plugin';
import { Contract } from './compiler';
export class RemixClient extends PluginClient {
private client = createClient<Api, Readonly<RemixApi>>(this);
loaded() {
return this.client.onload()
}
/** Emit an event when file changed */
async onFileChange(cb: (contract: string) => any) {
this.client.on('fileManager', 'currentFileChanged', async (name: string) => {
if (!name) return
cb(name)
})
}
/** Load Ballot contract example into the file manager */
async loadContract({name, content}: Contract) {
try {
await this.client.call('fileManager', 'setFile', name, content)
await this.client.call('fileManager', 'switchFile', name)
} catch (err) {
console.log(err)
}
}
/** Update the status of the plugin in remix */
changeStatus(status: Status) {
this.client.emit('statusChanged', status);
}
/** Highlight a part of the editor */
highlight(lineColumnPos: HighlightPosition, name: string, color: string) {
return this.client.call('editor', 'highlight', lineColumnPos, name, color)
}
/** Remove current Hightlight */
discardHighlight() {
return this.client.call('editor', 'discardHighlight')
}
/** Get the name of the current contract */
async getContractName(): Promise<string> {
await this.client.onload()
return this.client.call('fileManager', 'getCurrentFile')
}
/** Get the current contract file */
async getContract(): Promise<Contract> {
const name = await this.getContractName()
if (!name) throw new Error('No contract selected yet')
const content = await this.client.call('fileManager', 'getFile', name)
return {
name,
content,
}
}
/** Emit an event to Remix with compilation result */
compilationFinish(title: string, content: string, data: CompilationResult) {
this.client.emit('compilationFinished', title, content, 'vyper', data);
}
}
export const remixClient = new RemixClient()
// export const RemixClientContext = React.createContext(new RemixClient())

@ -0,0 +1,3 @@
export const environment = {
production: true
};

@ -0,0 +1,6 @@
// This file can be replaced during build by using the `fileReplacements` array.
// When building for production, this file is replaced with `environment.prod.ts`.
export const environment = {
production: false
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Vyper</title>
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/x-icon" href="favicon.ico" />
</head>
<body>
<div id="root"></div>
</body>
</html>

@ -0,0 +1,7 @@
import { StrictMode } from 'react';
import * as ReactDOM from 'react-dom';
import App from './app/app';
ReactDOM.render(<StrictMode><App /></StrictMode>, document.getElementById('root'));

@ -0,0 +1,7 @@
/**
* Polyfill stable language features. These imports will be optimized by `@babel/preset-env`.
*
* See: https://github.com/zloirock/core-js#babel
*/
import 'core-js/stable';
import 'regenerator-runtime/runtime';

@ -0,0 +1 @@
/* You can add global styles to this file, and also import other style files */

@ -0,0 +1,14 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"types": ["node"]
},
"files": [
"../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
"../../node_modules/@nrwl/react/typings/image.d.ts"
],
"exclude": ["**/*.spec.ts", "**/*.spec.tsx"],
"include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
}

@ -0,0 +1,20 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"jsx": "react-jsx",
"allowJs": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.app.json"
}
]
}

@ -87,7 +87,7 @@ export class CompilerMetadata extends Plugin {
let parsedMetadata
try {
parsedMetadata = JSON.parse(contract.object.metadata)
parsedMetadata = contract.object && contract.object.metadata ? JSON.parse(contract.object.metadata) : null
} catch (e) {
console.log(e)
}

@ -102,4 +102,6 @@ export const UUPSupgradeAbi = {
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
}
export const EnableProxyURLParam = 'deployProxy'
export const EnableUpgradeURLParam = 'upgradeProxy'

@ -92,7 +92,7 @@ export class EditorContextListener extends Plugin {
this._stopHighlighting()
this.currentPosition = cursorPosition
this.currentFile = file
if (compilationResult && compilationResult.data && compilationResult.data.sources[file]) {
if (compilationResult && compilationResult.data && compilationResult.data.sources && compilationResult.data.sources[file]) {
const nodes = sourceMappingDecoder.nodesAtPosition(null, cursorPosition, compilationResult.data.sources[file])
this.nodes = nodes
if (nodes && nodes.length && nodes[nodes.length - 1]) {

@ -1,6 +1,6 @@
import { Plugin } from '@remixproject/engine'
import { ContractAST, ContractSources, DeployOptions } from '../types/contract'
import { UUPS, UUPSABI, UUPSBytecode, UUPSfunAbi, UUPSupgradeAbi } from './constants/uups'
import { EnableProxyURLParam, EnableUpgradeURLParam, UUPS, UUPSABI, UUPSBytecode, UUPSfunAbi, UUPSupgradeAbi } from './constants/uups'
const proxyProfile = {
name: 'openzeppelin-proxy',
@ -33,31 +33,40 @@ export class OpenZeppelinProxy extends Plugin {
async getProxyOptions (data: ContractSources, file: string): Promise<{ [name: string]: DeployOptions }> {
const contracts = data.contracts[file]
const ast = data.sources[file].ast
const inputs = {}
if (this.kind === 'UUPS') {
Object.keys(contracts).map(name => {
if (ast) {
const UUPSSymbol = ast.exportedSymbols[UUPS] ? ast.exportedSymbols[UUPS][0] : null
ast.absolutePath === file && ast.nodes.map((node) => {
if (node.name === name && node.linearizedBaseContracts.includes(UUPSSymbol)) {
const abi = contracts[name].abi
const initializeInput = abi.find(node => node.name === 'initialize')
inputs[name] = {
options: [{ title: 'Deploy with Proxy', active: false }, { title: 'Upgrade with Proxy', active: false }],
initializeOptions: {
inputs: initializeInput,
initializeInputs: initializeInput ? this.blockchain.getInputs(initializeInput) : null
}
const options = await (this.getUUPSContractOptions(contracts, ast, file))
return options
}
}
async getUUPSContractOptions (contracts, ast, file) {
const options = {}
await Promise.all(Object.keys(contracts).map(async (name) => {
if (ast) {
const UUPSSymbol = ast.exportedSymbols[UUPS] ? ast.exportedSymbols[UUPS][0] : null
await Promise.all(ast.absolutePath === file && ast.nodes.map(async (node) => {
if (node.name === name && node.linearizedBaseContracts.includes(UUPSSymbol)) {
const abi = contracts[name].abi
const initializeInput = abi.find(node => node.name === 'initialize')
const isDeployWithProxyEnabled: boolean = await this.call('config', 'getAppParameter', EnableProxyURLParam) || false
const isDeployWithUpgradeEnabled: boolean = await this.call('config', 'getAppParameter', EnableUpgradeURLParam) || false
options[name] = {
options: [{ title: 'Deploy with Proxy', active: isDeployWithProxyEnabled }, { title: 'Upgrade with Proxy', active: isDeployWithUpgradeEnabled }],
initializeOptions: {
inputs: initializeInput,
initializeInputs: initializeInput ? this.blockchain.getInputs(initializeInput) : null
}
}
})
}
})
}
return inputs
}
}))
}
}))
return options
}
async executeUUPSProxy(implAddress: string, args: string | string [] = '', initializeABI, implementationContractObject): Promise<void> {

@ -42,6 +42,7 @@
"@remix-project/remix-lib": "^0.5.15",
"@remix-project/remix-simulator": "^0.2.15",
"@remix-project/remix-solidity": "^0.5.1",
"@remix-project/remix-url-resolver": "^0.0.36",
"ansi-gray": "^0.1.1",
"async": "^2.6.0",
"axios": ">=0.21.1",

@ -4,6 +4,7 @@ import path from 'path'
import deepequal from 'deep-equal'
import Log from './logger'
import { Compiler as RemixCompiler } from '@remix-project/remix-solidity'
import { RemixURLResolver } from '@remix-project/remix-url-resolver'
import { SrcIfc, CompilerConfiguration, CompilationErrors } from './types'
const logger = new Log()
const log = logger.logger
@ -85,6 +86,17 @@ export function compileFileOrFiles (filename: string, isDirectory: boolean, opts
'remix_accounts.sol': { content: writeTestAccountsContract(accounts) }
}
const filepath: string = (isDirectory ? filename : path.dirname(filename))
const importsCallback = (url, cb) => {
try {
if(fs.existsSync(url)) cb(null, fs.readFileSync(url, 'utf-8'))
else {
const urlResolver = new RemixURLResolver()
urlResolver.resolve(url).then((result) => cb(null, result.content)).catch((error) => cb(error.message))
}
} catch (e) {
cb(e.message)
}
}
try {
if (!isDirectory && fs.existsSync(filename)) {
if (filename.split('.').pop() === 'sol') {
@ -114,13 +126,7 @@ export function compileFileOrFiles (filename: string, isDirectory: boolean, opts
} finally {
async.waterfall([
function loadCompiler (next) {
compiler = new RemixCompiler((url, cb) => {
try {
cb(null, fs.readFileSync(url, 'utf-8'))
} catch (e) {
cb(e.message)
}
})
compiler = new RemixCompiler(importsCallback)
if (compilerConfig) {
const { currentCompilerUrl, evmVersion, optimize, runs } = compilerConfig
if (evmVersion) compiler.set('evmVersion', evmVersion)

@ -4,7 +4,7 @@ import path from 'path'
import axios, { AxiosResponse } from 'axios'
import { runTestFiles } from './runTestFiles'
import fs from './fileSystem'
import { Provider } from '@remix-project/remix-simulator'
import { Provider, extend } from '@remix-project/remix-simulator'
import { CompilerConfiguration } from './types'
import Log from './logger'
import colors from 'colors'
@ -68,7 +68,7 @@ commander
}
// Console message
console.log(colors.white('\n\t👁\t:: Running remix-tests - Unit testing for solidity ::\t👁\n'))
console.log(colors.bold('\n\t👁\t:: Running tests using remix-tests ::\t👁\n'))
// Set logger verbosity
if (commander.verbose) {
@ -115,7 +115,7 @@ commander
const provider: any = new Provider()
await provider.init()
web3.setProvider(provider)
extend(web3)
runTestFiles(path.resolve(testsPath), isDirectory, web3, compilerConfig)
})

@ -4,7 +4,7 @@ import { runTest } from './testRunner'
import { TestResultInterface, ResultsInterface, CompilerConfiguration, compilationInterface, ASTInterface, Options, AstNode } from './types'
import colors from 'colors'
import Web3 from 'web3'
import { format } from 'util'
import { compileFileOrFiles } from './compiler'
import { deployAll } from './deployer'
@ -22,6 +22,15 @@ export function runTestFiles (filepath: string, isDirectory: boolean, web3: Web3
opts = opts || {}
compilerConfig = compilerConfig || {} as CompilerConfiguration
const sourceASTs: any = {}
const printLog = (log: string[]) => {
let formattedLog
if (typeof log[0] === 'string' && (log[0].includes('%s') || log[0].includes('%d'))) {
formattedLog = format(log[0], ...log.slice(1))
} else {
formattedLog = log.join(' ')
}
signale.log(formattedLog)
}
const { Signale } = require('signale') // eslint-disable-line
// signale configuration
const options = {
@ -34,6 +43,11 @@ export function runTestFiles (filepath: string, isDirectory: boolean, web3: Web3
name: {
badge: '\n\t◼',
label: '',
color: 'whiteBright'
},
log: {
badge: '\t',
label: '',
color: 'white'
},
error: {
@ -104,17 +118,24 @@ export function runTestFiles (filepath: string, isDirectory: boolean, web3: Web3
let totalPassing = 0
let totalFailing = 0
let totalTime = 0
const errors: any[] = []
const _testCallback = function (err: Error | null | undefined, result: TestResultInterface) {
if (err) throw err
if (result.type === 'contract') {
signale.name(result.value.white)
signale.name(result.value)
console.log('\n')
} else if (result.type === 'testPass') {
signale.result(result.value)
if (result?.hhLogs?.length) result.hhLogs.forEach(printLog)
signale.result(result.value.white)
} else if (result.type === 'testFailure') {
signale.error(result.value.red)
errors.push(result)
if (result?.hhLogs?.length) result.hhLogs.forEach(printLog)
signale.error(result.value.white)
if (result.assertMethod) {
console.log(colors.green('\t Expected value should be ' + result.assertMethod + ' to: ' + result.expected))
console.log(colors.red('\t Received: ' + result.returned))
}
console.log(colors.red('\t Message: ' + result.errMsg))
console.log('\n')
}
}
const _resultsCallback = (_err: Error | null | undefined, result: ResultsInterface, cb) => {
@ -127,7 +148,7 @@ export function runTestFiles (filepath: string, isDirectory: boolean, web3: Web3
async.eachOfLimit(contractsToTest, 1, (contractName: string, index, cb) => {
try {
const fileAST: AstNode = sourceASTs[contracts[contractName]['filename']]
runTest(contractName, contracts[contractName], contractsToTestDetails[index], fileAST, { accounts }, _testCallback, (err, result) => {
runTest(contractName, contracts[contractName], contractsToTestDetails[index], fileAST, { accounts, web3 }, _testCallback, (err, result) => {
if (err) {
console.log(err)
return cb(err)
@ -141,23 +162,16 @@ export function runTestFiles (filepath: string, isDirectory: boolean, web3: Web3
if (err) {
return next(err)
}
console.log('\n')
if (totalPassing > 0) {
console.log(colors.green(totalPassing + ' passing ') + colors.grey('(' + totalTime + 's)'))
console.log(colors.bold.underline('Tests Summary: '))
if (totalPassing >= 0) {
console.log(colors.green('Passed: ' + totalPassing))
}
if (totalFailing > 0) {
console.log(colors.red(totalFailing + ' failing'))
if (totalFailing >= 0) {
console.log(colors.red('Failed: ' + totalFailing))
}
console.log('')
errors.forEach((error, index) => {
console.log(' ' + (index + 1) + ') ' + colors.bold(error.context + ': ') + error.value)
console.log('')
console.log(colors.red('\t error: ' + error.errMsg))
console.log(colors.green('\t expected value to be ' + error.assertMethod + ' to: ' + error.expected))
console.log(colors.red('\t returned: ' + error.returned))
})
console.log(colors.white('Time Taken: ' + totalTime + 's'))
console.log('')
next()

@ -1,7 +1,5 @@
import "remix_tests.sol"; // this import is automatically injected by Remix.
import "./hardhat/console.sol";
import "hardhat/console.sol";
contract AssertOkTest {

@ -1,12 +0,0 @@
import "remix_tests.sol"; // this import is automatically injected by Remix.
contract AssertOkTest {
function okPassTest() public {
Assert.ok(true, "okPassTest passes");
}
function okFailTest() public {
Assert.ok(false, "okFailTest fails");
}
}

File diff suppressed because it is too large Load Diff

@ -42,101 +42,103 @@ Commands:
})
test('remix-tests running a test file', () => {
const res = spawnSync(executablePath, [resolve(__dirname + '/examples_0/assert_ok_without_console_test.sol')])
const res = spawnSync(executablePath, [resolve(__dirname + '/examples_0/assert_ok_test.sol')])
// match initial lines
expect(res.stdout.toString().trim()).toMatch(/:: Running remix-tests - Unit testing for solidity ::/)
expect(res.stdout.toString().trim()).toMatch(/:: Running tests using remix-tests ::/)
expect(res.stdout.toString().trim()).toMatch(/creation of library remix_tests.sol:Assert pending.../)
// match test result
expect(res.stdout.toString().trim()).toMatch(/AssertOkTest/)
expect(res.stdout.toString().trim()).toMatch(/AssertOkTest okPassTest/) // check if console.log is printed
expect(res.stdout.toString().trim()).toMatch(/Ok pass test/)
expect(res.stdout.toString().trim()).toMatch(/AssertOkTest okFailTest/) // check if console.log is printed
expect(res.stdout.toString().trim()).toMatch(/Ok fail test/)
// match fail test details
expect(res.stdout.toString().trim()).toMatch(/error: okFailTest fails/)
expect(res.stdout.toString().trim()).toMatch(/expected value to be ok to: true/)
expect(res.stdout.toString().trim()).toMatch(/returned: false/)
expect(res.stdout.toString().trim()).toMatch(/Expected value should be ok to: true/)
expect(res.stdout.toString().trim()).toMatch(/Received: false/)
expect(res.stdout.toString().trim()).toMatch(/Message: okFailTest fails/)
})
test('remix-tests running a test file with custom compiler version', () => {
const res = spawnSync(executablePath, ['--compiler', '0.7.4', resolve(__dirname + '/examples_0/assert_ok_without_console_test.sol')])
const res = spawnSync(executablePath, ['--compiler', '0.7.4', resolve(__dirname + '/examples_0/assert_ok_test.sol')])
// match initial lines
expect(res.stdout.toString().trim().includes('Compiler version set to 0.7.4. Latest version is')).toBeTruthy()
expect(res.stdout.toString().trim().includes('Loading remote solc version v0.7.4+commit.3f05b770 ...')).toBeTruthy()
expect(res.stdout.toString().trim()).toMatch(/:: Running remix-tests - Unit testing for solidity ::/)
expect(res.stdout.toString().trim()).toMatch(/:: Running tests using remix-tests ::/)
expect(res.stdout.toString().trim()).toMatch(/creation of library remix_tests.sol:Assert pending.../)
// match test result
expect(res.stdout.toString().trim()).toMatch(/Ok pass test/)
expect(res.stdout.toString().trim()).toMatch(/Ok fail test/)
// match fail test details
expect(res.stdout.toString().trim()).toMatch(/error: okFailTest fails/)
expect(res.stdout.toString().trim()).toMatch(/Message: okFailTest fails/)
})
test('remix-tests running a test file with unavailable custom compiler version (should fail)', () => {
const res = spawnSync(executablePath, ['--compiler', '1.10.4', resolve(__dirname + '/examples_0/assert_ok_without_console_test.sol')])
const res = spawnSync(executablePath, ['--compiler', '1.10.4', resolve(__dirname + '/examples_0/assert_ok_test.sol')])
// match initial lines
expect(res.stdout.toString().trim().includes('No compiler found in releases with version 1.10.4')).toBeTruthy()
})
test('remix-tests running a test file with custom EVM', () => {
const res = spawnSync(executablePath, ['--evm', 'petersburg', resolve(__dirname + '/examples_0/assert_ok_without_console_test.sol')])
const res = spawnSync(executablePath, ['--evm', 'petersburg', resolve(__dirname + '/examples_0/assert_ok_test.sol')])
// match initial lines
expect(res.stdout.toString().trim().includes('EVM set to petersburg')).toBeTruthy()
expect(res.stdout.toString().trim()).toMatch(/:: Running remix-tests - Unit testing for solidity ::/)
expect(res.stdout.toString().trim()).toMatch(/:: Running tests using remix-tests ::/)
expect(res.stdout.toString().trim()).toMatch(/creation of library remix_tests.sol:Assert pending.../)
// match test result
expect(res.stdout.toString().trim()).toMatch(/Ok pass test/)
expect(res.stdout.toString().trim()).toMatch(/Ok fail test/)
// match fail test details
expect(res.stdout.toString().trim()).toMatch(/error: okFailTest fails/)
expect(res.stdout.toString().trim()).toMatch(/Message: okFailTest fails/)
})
test('remix-tests running a test file by enabling optimization', () => {
const res = spawnSync(executablePath, ['--optimize', 'true', resolve(__dirname + '/examples_0/assert_ok_without_console_test.sol')])
const res = spawnSync(executablePath, ['--optimize', 'true', resolve(__dirname + '/examples_0/assert_ok_test.sol')])
// match initial lines
expect(res.stdout.toString().trim().includes('Optimization is enabled')).toBeTruthy()
expect(res.stdout.toString().trim()).toMatch(/:: Running remix-tests - Unit testing for solidity ::/)
expect(res.stdout.toString().trim()).toMatch(/:: Running tests using remix-tests ::/)
expect(res.stdout.toString().trim()).toMatch(/creation of library remix_tests.sol:Assert pending.../)
// match test result
expect(res.stdout.toString().trim()).toMatch(/Ok pass test/)
expect(res.stdout.toString().trim()).toMatch(/Ok fail test/)
// match fail test details
expect(res.stdout.toString().trim()).toMatch(/error: okFailTest fails/)
expect(res.stdout.toString().trim()).toMatch(/Message: okFailTest fails/)
})
test('remix-tests running a test file by enabling optimization and setting runs', () => {
const res = spawnSync(executablePath, ['--optimize', 'true', '--runs', '300', resolve(__dirname + '/examples_0/assert_ok_without_console_test.sol')])
const res = spawnSync(executablePath, ['--optimize', 'true', '--runs', '300', resolve(__dirname + '/examples_0/assert_ok_test.sol')])
// match initial lines
expect(res.stdout.toString().trim().includes('Optimization is enabled')).toBeTruthy()
expect(res.stdout.toString().trim().includes('Runs set to 300')).toBeTruthy()
expect(res.stdout.toString().trim()).toMatch(/:: Running remix-tests - Unit testing for solidity ::/)
expect(res.stdout.toString().trim()).toMatch(/:: Running tests using remix-tests ::/)
expect(res.stdout.toString().trim()).toMatch(/creation of library remix_tests.sol:Assert pending.../)
// match test result
expect(res.stdout.toString().trim()).toMatch(/Ok pass test/)
expect(res.stdout.toString().trim()).toMatch(/Ok fail test/)
// match fail test details
expect(res.stdout.toString().trim()).toMatch(/error: okFailTest fails/)
expect(res.stdout.toString().trim()).toMatch(/Message: okFailTest fails/)
})
test('remix-tests running a test file without enabling optimization and setting runs (should fail)', () => {
const res = spawnSync(executablePath, ['--runs', '300', resolve(__dirname + '/examples_0/assert_ok_without_console_test.sol')])
const res = spawnSync(executablePath, ['--runs', '300', resolve(__dirname + '/examples_0/assert_ok_test.sol')])
// match initial lines
expect(res.stdout.toString().trim().includes('Optimization should be enabled for runs')).toBeTruthy()
})
test('remix-tests running a test file with all options', () => {
const res = spawnSync(executablePath, ['--compiler', '0.7.5', '--evm', 'istanbul', '--optimize', 'true', '--runs', '250', resolve(__dirname + '/examples_0/assert_ok_without_console_test.sol')])
const res = spawnSync(executablePath, ['--compiler', '0.7.5', '--evm', 'istanbul', '--optimize', 'true', '--runs', '250', resolve(__dirname + '/examples_0/assert_ok_test.sol')])
// match initial lines
expect(res.stdout.toString().trim().includes('Compiler version set to 0.7.5. Latest version is')).toBeTruthy()
expect(res.stdout.toString().trim().includes('Loading remote solc version v0.7.5+commit.eb77ed08 ...')).toBeTruthy()
expect(res.stdout.toString().trim().includes('EVM set to istanbul')).toBeTruthy()
expect(res.stdout.toString().trim().includes('Optimization is enabled')).toBeTruthy()
expect(res.stdout.toString().trim().includes('Runs set to 250')).toBeTruthy()
expect(res.stdout.toString().trim()).toMatch(/:: Running remix-tests - Unit testing for solidity ::/)
expect(res.stdout.toString().trim()).toMatch(/:: Running tests using remix-tests ::/)
expect(res.stdout.toString().trim()).toMatch(/creation of library remix_tests.sol:Assert pending.../)
// match test result
expect(res.stdout.toString().trim()).toMatch(/Ok pass test/)
expect(res.stdout.toString().trim()).toMatch(/Ok fail test/)
// match fail test details
expect(res.stdout.toString().trim()).toMatch(/error: okFailTest fails/)
expect(res.stdout.toString().trim()).toMatch(/Message: okFailTest fails/)
})
})
})

@ -129,7 +129,7 @@ describe('testRunner', () => {
{ type: 'accountList', value: accounts },
{ type: 'contract', value: 'AssertOkTest', filename: __dirname + '/examples_0/assert_ok_test.sol' },
{ type: 'testPass', debugTxHash: '0x5b665752a4faf83229259b9b2811d3295be0af633b0051d4b90042283ef55707', value: 'Ok pass test', filename: __dirname + '/examples_0/assert_ok_test.sol', context: 'AssertOkTest', hhLogs: hhLogs1 },
{ type: 'testFailure', debugTxHash: '0xa0a30ad042a7fc3495f72be7ba788d705888ffbbec7173f60bb27e07721510f2',value: 'Ok fail test', filename: __dirname + '/examples_0/assert_ok_test.sol', errMsg: 'okFailTest fails', context: 'AssertOkTest', hhLogs: hhLogs2, assertMethod: 'ok', location: '370:36:0', expected: 'true', returned: 'false'},
{ type: 'testFailure', debugTxHash: '0xa0a30ad042a7fc3495f72be7ba788d705888ffbbec7173f60bb27e07721510f2',value: 'Ok fail test', filename: __dirname + '/examples_0/assert_ok_test.sol', errMsg: 'okFailTest fails', context: 'AssertOkTest', hhLogs: hhLogs2, assertMethod: 'ok', location: '366:36:0', expected: 'true', returned: 'false'},
], ['time', 'web3'])
})

@ -36,7 +36,7 @@ export const CopyToClipboard = (props: ICopyToClipboard) => {
}
}
const handleClick = (e) => {
const handleClick = (e: any) => {
if (content) { // module `copy` keeps last copied thing in the memory, so don't show tooltip if nothing is copied, because nothing was added to memory
copyData()
} else {

@ -249,7 +249,6 @@ export const EditorUI = (props: EditorUIProps) => {
defineAndSetTheme(monacoRef.current)
})
useEffect(() => {
if (!editorRef.current || !props.currentFile) return
currentFileRef.current = props.currentFile
@ -471,6 +470,8 @@ export const EditorUI = (props: EditorUIProps) => {
(window as any).addRemixBreakpoint(e.target.position)
}
})
// zoomin zoomout
editor.addCommand(monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyCode.US_EQUAL, () => {
editor.updateOptions({ fontSize: editor.getOption(43).fontSize + 1 })
})
@ -478,6 +479,32 @@ export const EditorUI = (props: EditorUIProps) => {
editor.updateOptions({ fontSize: editor.getOption(43).fontSize - 1 })
})
// add context menu items
const zoominAction = {
id: "zoomIn",
label: "Zoom In",
contextMenuOrder: 0, // choose the order
contextMenuGroupId: "zooming", // create a new grouping
keybindings: [
// eslint-disable-next-line no-bitwise
monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyCode.Equal,
],
run: () => { editor.updateOptions({ fontSize: editor.getOption(43).fontSize + 1 }) },
}
const zoomOutAction = {
id: "zoomOut",
label: "Zoom Out",
contextMenuOrder: 0, // choose the order
contextMenuGroupId: "zooming", // create a new grouping
keybindings: [
// eslint-disable-next-line no-bitwise
monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyCode.Minus,
],
run: () => { editor.updateOptions({ fontSize: editor.getOption(43).fontSize - 1 }) },
}
editor.addAction(zoomOutAction)
editor.addAction(zoominAction)
const editorService = editor._codeEditorService;
const openEditorBase = editorService.openCodeEditor.bind(editorService);
editorService.openCodeEditor = async (input, source) => {

@ -95,17 +95,20 @@ export const setupEvents = (plugin: RunTab, dispatch: React.Dispatch<any>) => {
const broadcastCompilationResult = async (plugin: RunTab, dispatch: React.Dispatch<any>, file, source, languageVersion, data, input?) => {
// TODO check whether the tab is configured
const compiler = new CompilerAbstract(languageVersion, data, source, input)
plugin.compilersArtefacts[languageVersion] = compiler
plugin.compilersArtefacts.__last = compiler
const contracts = getCompiledContracts(compiler).map((contract) => {
return { name: languageVersion, alias: contract.name, file: contract.file, compiler }
})
const index = contracts.findIndex(contract => contract.alias === plugin.REACT_API.contracts.currentContract)
if ((index < 0) && (contracts.length > 0)) dispatch(setCurrentContract(contracts[0].alias))
const isUpgradeable = await plugin.call('openzeppelin-proxy', 'isConcerned', data.sources[file] ? data.sources[file].ast : {})
if ((contracts.length > 0)) {
const contractsInCompiledFile = contracts.filter(obj => obj.file === file)
let currentContract
if (contractsInCompiledFile.length) currentContract = contractsInCompiledFile[0].alias
else currentContract = contracts[0].alias
dispatch(setCurrentContract(currentContract))
}
const isUpgradeable = await plugin.call('openzeppelin-proxy', 'isConcerned', data.sources && data.sources[file] ? data.sources[file].ast : {})
if (isUpgradeable) {
const options = await plugin.call('openzeppelin-proxy', 'getProxyOptions', data, file)

@ -24,6 +24,13 @@ export function ContractGUI (props: ContractGUIProps) {
const initializeFields = useRef<Array<HTMLInputElement | null>>([])
const basicInputRef = useRef<HTMLInputElement>()
useEffect(() => {
if (props.deployOption && Array.isArray(props.deployOption)) {
if (props.deployOption[0] && props.deployOption[0].title === 'Deploy with Proxy' && props.deployOption[0].active) handleDeployProxySelect(true)
else if (props.deployOption[1] && props.deployOption[1].title === 'Upgrade with Proxy' && props.deployOption[1].active) handleUpgradeImpSelect(true)
}
}, [props.deployOption])
useEffect(() => {
if (props.title) {
setTitle(props.title)
@ -131,12 +138,10 @@ export function ContractGUI (props: ContractGUIProps) {
}
const makeMultiVal = () => {
let inputString = basicInput
const inputString = basicInput
if (inputString) {
inputString = inputString.replace(/(^|,\s+|,)(\d+)(\s+,|,|$)/g, '$1"$2"$3') // replace non quoted number by quoted number
inputString = inputString.replace(/(^|,\s+|,)(0[xX][0-9a-fA-F]+)(\s+,|,|$)/g, '$1"$2"$3') // replace non quoted hex string by quoted hex string
const inputJSON = JSON.parse('[' + inputString + ']')
const inputJSON = remixLib.execution.txFormat.parseFunctionParams(inputString)
const multiInputs = multiFields.current
for (let k = 0; k < multiInputs.length; k++) {
@ -179,9 +184,7 @@ export function ContractGUI (props: ContractGUIProps) {
setToggleDeployProxy(!toggleDeployProxy)
}
const handleDeployProxySelect = (e) => {
const value = e.target.checked
const handleDeployProxySelect = (value: boolean) => {
if (value) setToggleUpgradeImp(false)
setToggleDeployProxy(value)
setDeployState({ upgrade: false, deploy: value })
@ -191,9 +194,7 @@ export function ContractGUI (props: ContractGUIProps) {
setToggleUpgradeImp(!toggleUpgradeImp)
}
const handleUpgradeImpSelect = (e) => {
const value = e.target.checked
const handleUpgradeImpSelect = (value: boolean) => {
setToggleUpgradeImp(value)
if (value) {
setToggleDeployProxy(false)
@ -264,7 +265,7 @@ export function ContractGUI (props: ContractGUIProps) {
data-id="contractGUIDeployWithProxy"
className="form-check-input custom-control-input"
type="checkbox"
onChange={handleDeployProxySelect}
onChange={(e) => handleDeployProxySelect(e.target.checked)}
checked={deployState.deploy}
/>
<label
@ -307,7 +308,7 @@ export function ContractGUI (props: ContractGUIProps) {
data-id="contractGUIUpgradeImplementation"
className="form-check-input custom-control-input"
type="checkbox"
onChange={handleUpgradeImpSelect}
onChange={(e) => handleUpgradeImpSelect(e.target.checked)}
checked={deployState.upgrade}
/>
<label

@ -6,7 +6,7 @@ export const warnText = 'Be sure the endpoint is opened before enabling it. \nTh
export const gitAccessTokenTitle = 'GitHub Access Token'
export const gitAccessTokenText = 'Manage the access token used to publish to Gist and retrieve GitHub contents.'
export const gitAccessTokenText2 = 'Go to github token page (link below) to create a new token and save it in Remix. Make sure this token has only \'create gist\' permission.'
export const gitAccessTokenLink = 'https://github.com/settings/tokens'
export const gitAccessTokenLink = 'https://github.com/settings/tokens/new?scopes=gist,repo&description=Remix%20IDE%20Token'
export const etherscanTokenTitle = 'EtherScan Access Token'
export const etherscanTokenLink = 'https://etherscan.io/myapikey'
export const etherscanAccessTokenText = 'Manage the api key used to interact with Etherscan.'

@ -309,12 +309,22 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
allVersions = [...allVersions, ...versions]
selectedVersion = state.defaultVersion
if (api.getCompilerParameters().version) selectedVersion = api.getCompilerParameters().version
// Check if version is a URL and corresponding filename starts with 'soljson'
if (selectedVersion.startsWith('https://')) {
const urlArr = selectedVersion.split('/')
if (urlArr[urlArr.length - 1].startsWith('soljson')) isURL = true
if (api.getCompilerParameters().version) {
const versionFromURL = api.getCompilerParameters().version
// Check if version is a URL and corresponding filename starts with 'soljson'
if (versionFromURL.startsWith('https://')) {
const urlArr = versionFromURL.split('/')
if (urlArr[urlArr.length - 1].startsWith('soljson')) {
isURL = true
selectedVersion = versionFromURL
}
} else {
// URL version can be like 0.8.7+commit.e28d00a7, 0.8.7 or soljson-v0.8.7+commit.e28d00a7.js
const selectedVersionArr = versions.filter(obj => obj.path === versionFromURL || obj.longVersion === versionFromURL || obj.version === versionFromURL)
// for version like 0.8.15, there will be more than one elements in the array
// In that case too, index 0 will have non-nightly version object
if (selectedVersionArr.length) selectedVersion = selectedVersionArr[0].path
}
}
if (wasmRes.event.type !== 'error') {
allVersionsWasm = JSON.parse(wasmRes.json).builds.slice().reverse()
@ -765,7 +775,7 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
<div className={`px-4 pb-4 border-bottom flex-column ${toggleExpander ? "d-flex" : "d-none"}`}>
<div className="d-flex pb-1 remixui_compilerConfig custom-control custom-radio">
<input className="custom-control-input" type="radio" name="configradio" value="manual" onChange={toggleConfigType} checked={!state.useFileConfiguration} id="scManualConfig" />
<label className="form-check-label custom-control-label" htmlFor="scManualConfig">Compiler configuration</label>
<label className="form-check-label custom-control-label" htmlFor="scManualConfig" data-id="scManualConfiguration">Compiler configuration</label>
</div>
<div className={`flex-column 'd-flex'}`}>
<div className="mb-2 ml-4">
@ -813,7 +823,7 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
<input
ref={configFilePathInput}
className={`py-0 my-0 form-control ${showFilePathInput ? "d-flex" : "d-none"}`}
placeholder={"Enter the new path"}
placeholder={"/folder_path/file_name.json"}
title="If the file you entered does not exist you will be able to create one in the next step."
disabled={!state.useFileConfiguration}
data-id="scConfigFilePathInput"

@ -7,12 +7,18 @@ import { CopyToClipboard } from '@remix-ui/clipboard' // eslint-disable-line
import './css/style.css'
export const ContractSelection = (props: ContractSelectionProps) => {
const { api, contractsDetails, contractList, modal } = props
const { api, compiledFileName, contractsDetails, contractList, modal } = props
const [selectedContract, setSelectedContract] = useState('')
const [storage, setStorage] = useState(null)
useEffect(() => {
if (contractList.length) setSelectedContract(contractList[0].name)
if (contractList.length) {
const compiledPathArr = compiledFileName.split('/')
const compiledFile = compiledPathArr[compiledPathArr.length - 1]
const contractsInCompiledFile = contractList.filter(obj => obj.file === compiledFile)
if (contractsInCompiledFile.length) setSelectedContract(contractsInCompiledFile[0].name)
else setSelectedContract(contractList[0].name)
}
}, [contractList])
const resetStorage = () => {

@ -183,7 +183,7 @@ export const SolidityCompiler = (props: SolidityCompilerProps) => {
configFilePath={state.configFilePath}
setConfigFilePath={setConfigFilePath}
/>
{ contractsFile[currentFile] && contractsFile[currentFile].contractsDetails && <ContractSelection api={api} contractsDetails={contractsFile[currentFile].contractsDetails} contractList={contractsFile[currentFile].contractList} modal={modal} /> }
{ contractsFile[currentFile] && contractsFile[currentFile].contractsDetails && <ContractSelection api={api} compiledFileName={currentFile} contractsDetails={contractsFile[currentFile].contractsDetails} contractList={contractsFile[currentFile].contractList} modal={modal} /> }
{ compileErrors[currentFile] &&
<div className="remixui_errorBlobs p-4" data-id="compiledErrors">
<span data-id={`compilationFinishedWith_${currentVersion}`}></span>

@ -23,6 +23,7 @@ export interface CompilerContainerProps {
}
export interface ContractSelectionProps {
api: ICompilerApi,
compiledFileName: string,
contractList: { file: string, name: string }[],
modal: (title: string, message: string | JSX.Element, okLabel: string, okFn: () => void, cancelLabel?: string, cancelFn?: () => void) => void,
contractsDetails: Record<string, any>

@ -1,5 +1,5 @@
export const wrapScript = (script) => {
const isKnownScript = ['remix.', 'git'].some(prefix => script.trim().startsWith(prefix))
const isKnownScript = ['remix.', 'console.', 'git'].some(prefix => script.trim().startsWith(prefix))
if (isKnownScript) return script
return `
try {

@ -75,7 +75,8 @@ export const createWorkspaceTemplate = async (workspaceName: string, template: W
export type UrlParametersType = {
gist: string,
code: string,
url: string
url: string,
language: string
}
export const loadWorkspacePreset = async (template: WorkspaceTemplate = 'remixDefault') => {
@ -91,7 +92,7 @@ export const loadWorkspacePreset = async (template: WorkspaceTemplate = 'remixDe
if (params.code) {
const hash = bufferToHex(keccakFromString(params.code))
path = 'contract-' + hash.replace('0x', '').substring(0, 10) + '.sol'
path = 'contract-' + hash.replace('0x', '').substring(0, 10) + (params.language && params.language.toLowerCase() === 'yul' ? '.yul': '.sol')
content = atob(params.code)
await workspaceProvider.set(path, content)
}

@ -1,59 +1,88 @@
# Remixd
`remixd` is a tool that intend to be used with [Remix IDE](https://github.com/ethereum/remix-project) (aka. Browser-Solidity). It allows a websocket connection between
`Remix IDE` (web application) and the local computer.
[![npm version](https://badge.fury.io/js/%40remix-project%2Fremixd.svg)](https://www.npmjs.com/package/@remix-project/remixd)
[![npm](https://img.shields.io/npm/dt/@remix-project/remixd.svg?label=Total%20Downloads&logo=npm)](https://www.npmjs.com/package/@remix-project/remixd)
[![npm](https://img.shields.io/npm/dw/@remix-project/remixd.svg?logo=npm)](https://www.npmjs.com/package/@remix-project/remixd)
Practically Remix IDE makes available a folder shared by `remixd`.
More details are explained in this [tutorial](https://remix-ide.readthedocs.io/en/latest/remixd.html).
`@remix-project/remixd` is an NPM module that intends to be used with [Remix IDE](https://remix.ethereum.org/) web and desktop applications. It establishes a two-way websocket connection between the local computer and Remix IDE for a particular project directory.
Alternatively `remixd` can be used to setup a development environment that can be used with other popular frameworks like Embark, Truffle, Ganache, etc..
`remixd` can be used to setup a development environment with other popular frameworks like Hardhat, Truffle, Slither etc.
`remixd` needs `npm` and `node`
More details are explained in the [documentation](https://remix-ide.readthedocs.io/en/latest/remixd.html).
## INSTALLATION
## Installation
`yarn global add @remix-project/remixd`
`npm install -g @remix-project/remixd`
### Warning for old users
NOTE: When the remixd NPM module is installed, it also installs [Slither](https://github.com/crytic/slither), [solc-select](https://github.com/crytic/solc-select#quickstart) and sets [solc](https://docs.soliditylang.org/en/latest/installing-solidity.html) to latest version i.e. 0.8.15 currently.
ALSO NOTE: Python3.6+ (pip3) needs to already be installed on the System. In case of any discrepany, Slither can also installed along with other dependencies using command:
```
> remixd -i slither
```
_(This packaging of Slither with the remixd module is supported since Remixd v0.6.3)_
### Warning for quite old users
There is a new version of remixd with a new npm address: https://npmjs.com/package/@remix-project/remixd
If you were using the old one you need to:
1. uninstall the old one: `npm uninstall -g remixd`
2. install the new: `yarn global add @remix-project/remixd`
2. install the new: `npm install -g @remix-project/remixd`
## remixd command
## HELP SECTION
The remixd command without options shares present working directory and the shared Remix domain will be https://remix.ethereum.org, https://remix-alpha.ethereum.org, or https://remix-beta.ethereum.org
The remixd command is:
```
> remixd
```
Usage: remixd -s <shared folder>
Provide a two-way connection between the local computer and Remix IDE
If you are using Remix from localhost or you are not running the command from your working directory, you’ll need to use the command with flags.
```
> remixd -h
Usage: remixd [options]
Establish a two-way websocket connection between the local computer and Remix IDE for a folder
Options:
-v, --version output the version number
-u, --remix-ide <url> URL of remix instance allowed to connect to this web sockect connection
-s, --shared-folder <path> Folder to share with Remix IDE
-u, --remix-ide <url> URL of remix instance allowed to connect
-s, --shared-folder <path> Folder to share with Remix IDE (Default: CWD)
-i, --install <name> Module name to install locally (Supported: ["slither"])
-r, --read-only Treat shared folder as read-only (experimental)
-h, --help output usage information
Example:
remixd -s ./ -u http://localhost:8080
remixd -s ./shared_project -u http://localhost:8080
```
## SHARE A FOLDER
## Share a project directory
`remixd -s <absolute-path> --remix-ide https://remix.ethereum.org`
`remixd -s ./shared_project -u https://remix.ethereum.org`
The current user should have `read/write` access to the folder (at least `read` access).
It is important to notice that changes made to the current file in `Remix IDE` are automatically saved to the local computer every 5000 ms. There is no `Save` action. But the `Ctrl-Z` (undo) can be used.
Furthermore :
Furthermore:
- No copy of the shared folder are kept in the browser storage.
- It is not possible to create a file from `Remix IDE` (that might change).
- Clicking on the new folder or new file icon under localhost will create a new file or folder in the shared folder.
- If a folder does not contain any file, the folder will not be displayed in the explorer (that might change).
- Symbolic links are not forwarded to Remix IDE.
## Ports Usage
remixd creates a websocket connections with Remix IDE on different ports. Ports are defined according to specific purpose. Port usage details are as:
- **65520** : For `remixd` websocket listener, to share a project from local device with Remix IDE. Shared folder will be loaded in the Remix IDE File Explorer workspace named localhost [See more](https://remix-ide.readthedocs.io/en/latest/remixd.html)
- **65522** : For `Hardhat` websocket listener, to enable the Hardhat Compilation using Remix IDE Solidity Compiler plugin, if shared folder is a Hardhat project [See more](https://remix-ide.readthedocs.io/en/latest/hardhat.html)
- **65523** : For `Slither` websocket listener, to enable the Slither Analysis using Remix IDE Solidity Static Analysis plugin [See more](https://remix-ide.readthedocs.io/en/latest/slither.html)
- **65524** : For `Truffle` websocket listener, to enable the Truffle Compilation using Remix IDE Solidity Compiler plugin, if shared folder is a Truffle project [See more](https://remix-ide.readthedocs.io/en/latest/truffle.html)
Note: Please make sure your system is secured enough and these ports are not opened nor forwarded.

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

@ -15,7 +15,12 @@
"default": {
"runner": "@nrwl/workspace/tasks-runners/default",
"options": {
"cacheableOperations": ["build", "lint", "test", "e2e"]
"cacheableOperations": [
"build",
"lint",
"test",
"e2e"
]
}
}
},
@ -25,27 +30,43 @@
},
"remix-astwalker": {
"tags": [],
"implicitDependencies": ["remix-lib"]
"implicitDependencies": [
"remix-lib"
]
},
"remix-analyzer": {
"tags": [],
"implicitDependencies": ["remix-astwalker", "remix-lib"]
"implicitDependencies": [
"remix-astwalker",
"remix-lib"
]
},
"remix-debug": {
"tags": [],
"implicitDependencies": ["remix-astwalker", "remix-lib"]
"implicitDependencies": [
"remix-astwalker",
"remix-lib"
]
},
"remix-simulator": {
"tags": [],
"implicitDependencies": ["remix-lib"]
"implicitDependencies": [
"remix-lib"
]
},
"remix-solidity": {
"tags": [],
"implicitDependencies": ["remix-lib"]
"implicitDependencies": [
"remix-lib"
]
},
"remix-tests": {
"tags": [],
"implicitDependencies": ["remix-lib", "remix-simulator", "remix-solidity"]
"implicitDependencies": [
"remix-lib",
"remix-simulator",
"remix-solidity"
]
},
"remix-url-resolver": {
"tags": []
@ -69,7 +90,9 @@
},
"remix-ide-e2e": {
"tags": [],
"implicitDependencies": ["remix-ide"]
"implicitDependencies": [
"remix-ide"
]
},
"remixd": {
"tags": []
@ -172,6 +195,9 @@
},
"remix-ui-tooltip-popup": {
"tags": []
},
"vyper": {
"tags": []
}
},
"targetDependencies": {
@ -182,4 +208,4 @@
}
]
}
}
}

@ -199,6 +199,7 @@
"react-bootstrap": "^1.6.4",
"react-dom": "^17.0.2",
"react-draggable": "^4.4.4",
"react-json-view": "^1.21.3",
"react-tabs": "^3.2.2",
"regenerator-runtime": "0.13.7",
"rss-parser": "^3.12.0",

@ -1,5 +1,37 @@
{
"version": 1,
"cli": {
"defaultCollection": "@nrwl/react"
},
"defaultProject": "remix-ide",
"schematics": {
"@nrwl/workspace": {
"library": {
"linter": "eslint"
}
},
"@nrwl/react": {
"application": {
"style": "css",
"linter": "eslint",
"strict": true,
"babel": true
},
"component": {
"style": "css"
},
"library": {
"style": "css",
"linter": "eslint",
"strict": true
}
},
"@nrwl/nx-plugin": {
"plugin": {
"linter": "eslint"
}
}
},
"projects": {
"remix-ide": {
"root": "apps/remix-ide",
@ -75,7 +107,9 @@
"options": {
"linter": "eslint",
"config": "apps/remix-ide/.eslintrc",
"files": ["apps/remix-ide/src/**/*.js"],
"files": [
"apps/remix-ide/src/**/*.js"
],
"exclude": [
"**/node_modules/**",
"apps/remix-ide/src/app/editor/mode-solidity.js",
@ -94,8 +128,13 @@
"builder": "@nrwl/linter:lint",
"options": {
"linter": "eslint",
"tsConfig": ["apps/remix-ide-e2e/tsconfig.e2e.json"],
"exclude": ["**/node_modules/**", "!apps/remix-ide-e2e/**/*"]
"tsConfig": [
"apps/remix-ide-e2e/tsconfig.e2e.json"
],
"exclude": [
"**/node_modules/**",
"!apps/remix-ide-e2e/**/*"
]
}
}
}
@ -111,14 +150,21 @@
"options": {
"linter": "eslint",
"config": "libs/remix-analyzer/.eslintrc",
"tsConfig": ["libs/remix-analyzer/tsconfig.lib.json"],
"exclude": ["**/node_modules/**", "libs/remix-analyzer/test/**/*"]
"tsConfig": [
"libs/remix-analyzer/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"libs/remix-analyzer/test/**/*"
]
}
},
"test": {
"builder": "@nrwl/workspace:run-commands",
"options": {
"commands": ["./../../node_modules/.bin/npm-run-all test"],
"commands": [
"./../../node_modules/.bin/npm-run-all test"
],
"cwd": "libs/remix-analyzer"
}
},
@ -129,7 +175,9 @@
"tsConfig": "libs/remix-analyzer/tsconfig.lib.json",
"packageJson": "libs/remix-analyzer/package.json",
"main": "libs/remix-analyzer/src/index.ts",
"assets": ["libs/remix-analyzer/*.md"]
"assets": [
"libs/remix-analyzer/*.md"
]
}
}
}
@ -145,14 +193,21 @@
"options": {
"linter": "eslint",
"config": "libs/remix-astwalker/.eslintrc",
"tsConfig": ["libs/remix-astwalker/tsconfig.lib.json"],
"exclude": ["**/node_modules/**", "libs/remix-astwalker/tests/**/*"]
"tsConfig": [
"libs/remix-astwalker/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"libs/remix-astwalker/tests/**/*"
]
}
},
"test": {
"builder": "@nrwl/workspace:run-commands",
"options": {
"commands": ["./../../node_modules/.bin/npm-run-all test"],
"commands": [
"./../../node_modules/.bin/npm-run-all test"
],
"cwd": "libs/remix-astwalker"
}
},
@ -163,7 +218,9 @@
"tsConfig": "libs/remix-astwalker/tsconfig.lib.json",
"packageJson": "libs/remix-astwalker/package.json",
"main": "libs/remix-astwalker/src/index.ts",
"assets": ["libs/remix-astwalker/*.md"]
"assets": [
"libs/remix-astwalker/*.md"
]
}
}
}
@ -179,14 +236,21 @@
"options": {
"linter": "eslint",
"config": "libs/remix-debug/.eslintrc",
"tsConfig": ["libs/remix-debug/tsconfig.lib.json"],
"exclude": ["**/node_modules/**", "libs/remix-debug/test/**/*"]
"tsConfig": [
"libs/remix-debug/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"libs/remix-debug/test/**/*"
]
}
},
"test": {
"builder": "@nrwl/workspace:run-commands",
"options": {
"commands": ["./../../node_modules/.bin/npm-run-all test"],
"commands": [
"./../../node_modules/.bin/npm-run-all test"
],
"cwd": "libs/remix-debug"
}
},
@ -224,14 +288,21 @@
"options": {
"linter": "eslint",
"config": "libs/remix-lib/.eslintrc",
"tsConfig": ["libs/remix-lib/tsconfig.lib.json"],
"exclude": ["**/node_modules/**", "libs/remix-lib/test/**/*"]
"tsConfig": [
"libs/remix-lib/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"libs/remix-lib/test/**/*"
]
}
},
"test": {
"builder": "@nrwl/workspace:run-commands",
"options": {
"commands": ["./../../node_modules/.bin/npm-run-all test"],
"commands": [
"./../../node_modules/.bin/npm-run-all test"
],
"cwd": "libs/remix-lib"
}
},
@ -242,7 +313,9 @@
"tsConfig": "libs/remix-lib/tsconfig.lib.json",
"packageJson": "libs/remix-lib/package.json",
"main": "libs/remix-lib/src/index.ts",
"assets": ["libs/remix-lib/*.md"]
"assets": [
"libs/remix-lib/*.md"
]
}
}
}
@ -258,14 +331,21 @@
"options": {
"linter": "eslint",
"config": "libs/remix-simulator/.eslintrc",
"tsConfig": ["libs/remix-simulator/tsconfig.lib.json"],
"exclude": ["**/node_modules/**", "libs/remix-simulator/test/**/*"]
"tsConfig": [
"libs/remix-simulator/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"libs/remix-simulator/test/**/*"
]
}
},
"test": {
"builder": "@nrwl/workspace:run-commands",
"options": {
"commands": ["./../../node_modules/.bin/npm-run-all test"],
"commands": [
"./../../node_modules/.bin/npm-run-all test"
],
"cwd": "libs/remix-simulator"
}
},
@ -303,8 +383,12 @@
"options": {
"linter": "eslint",
"config": "libs/remix-solidity/.eslintrc",
"tsConfig": ["libs/remix-solidity/tsconfig.lib.json"],
"exclude": ["**/node_modules/**"]
"tsConfig": [
"libs/remix-solidity/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**"
]
}
},
"test": {
@ -321,7 +405,9 @@
"tsConfig": "libs/remix-solidity/tsconfig.lib.json",
"packageJson": "libs/remix-solidity/package.json",
"main": "libs/remix-solidity/src/index.ts",
"assets": ["libs/remix-solidity/*.md"]
"assets": [
"libs/remix-solidity/*.md"
]
}
}
}
@ -337,7 +423,9 @@
"options": {
"linter": "eslint",
"config": "libs/remix-tests/.eslintrc",
"tsConfig": ["libs/remix-tests/tsconfig.lib.json"],
"tsConfig": [
"libs/remix-tests/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"libs/remix-tests/tests/**/*",
@ -386,7 +474,9 @@
"options": {
"linter": "eslint",
"config": "libs/remix-url-resolver/.eslintrc",
"tsConfig": ["libs/remix-url-resolver/tsconfig.lib.json"],
"tsConfig": [
"libs/remix-url-resolver/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"libs/remix-url-resolver/tests/**/*"
@ -396,7 +486,9 @@
"test": {
"builder": "@nrwl/workspace:run-commands",
"options": {
"commands": ["./../../node_modules/.bin/npm-run-all test"],
"commands": [
"./../../node_modules/.bin/npm-run-all test"
],
"cwd": "libs/remix-url-resolver"
}
},
@ -407,7 +499,9 @@
"tsConfig": "libs/remix-url-resolver/tsconfig.lib.json",
"packageJson": "libs/remix-url-resolver/package.json",
"main": "libs/remix-url-resolver/src/index.ts",
"assets": ["libs/remix-url-resolver/*.md"]
"assets": [
"libs/remix-url-resolver/*.md"
]
}
}
}
@ -426,7 +520,10 @@
"libs/remixd/tsconfig.lib.json",
"libs/remixd/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**", "!libs/remixd/**/*"]
"exclude": [
"**/node_modules/**",
"!libs/remixd/**/*"
]
}
},
"test": {
@ -444,7 +541,10 @@
"tsConfig": "libs/remixd/tsconfig.lib.json",
"packageJson": "libs/remixd/package.json",
"main": "libs/remixd/src/index.ts",
"assets": ["libs/remixd/*.md", "libs/remixd/src/origins.json"]
"assets": [
"libs/remixd/*.md",
"libs/remixd/src/origins.json"
]
}
}
}
@ -463,7 +563,10 @@
"libs/remix-ui/tree-view/tsconfig.lib.json",
"libs/remix-ui/tree-view/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**", "!libs/remix-ui/tree-view/**/*"]
"exclude": [
"**/node_modules/**",
"!libs/remix-ui/tree-view/**/*"
]
}
},
"test": {
@ -490,7 +593,10 @@
"libs/remix-ui/debugger-ui/tsconfig.lib.json",
"libs/remix-ui/debugger-ui/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**", "!libs/remix-ui/debugger-ui/**/*"]
"exclude": [
"**/node_modules/**",
"!libs/remix-ui/debugger-ui/**/*"
]
}
},
"test": {
@ -517,7 +623,10 @@
"libs/remix-ui/utils/tsconfig.lib.json",
"libs/remix-ui/utils/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**", "!libs/remix-ui/utils/**/*"]
"exclude": [
"**/node_modules/**",
"!libs/remix-ui/utils/**/*"
]
}
},
"test": {
@ -544,7 +653,10 @@
"libs/remix-ui/clipboard/tsconfig.lib.json",
"libs/remix-ui/clipboard/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**", "!libs/remix-ui/clipboard/**/*"]
"exclude": [
"**/node_modules/**",
"!libs/remix-ui/clipboard/**/*"
]
}
},
"test": {
@ -597,8 +709,13 @@
"builder": "@nrwl/linter:lint",
"options": {
"linter": "eslint",
"tsConfig": ["libs/remix-ui/toaster/tsconfig.lib.json"],
"exclude": ["**/node_modules/**", "!libs/remix-ui/toaster/**/*"]
"tsConfig": [
"libs/remix-ui/toaster/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"!libs/remix-ui/toaster/**/*"
]
}
}
}
@ -666,8 +783,13 @@
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": ["apps/debugger/tsconfig.app.json"],
"exclude": ["**/node_modules/**", "!apps/debugger/**/*"]
"tsConfig": [
"apps/debugger/tsconfig.app.json"
],
"exclude": [
"**/node_modules/**",
"!apps/debugger/**/*"
]
}
}
}
@ -682,8 +804,13 @@
"builder": "@nrwl/linter:lint",
"options": {
"linter": "eslint",
"tsConfig": ["libs/remix-ui/workspace/tsconfig.lib.json"],
"exclude": ["**/node_modules/**", "!libs/remix-ui/workspace/**/*"]
"tsConfig": [
"libs/remix-ui/workspace/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"!libs/remix-ui/workspace/**/*"
]
}
}
}
@ -698,8 +825,13 @@
"builder": "@nrwl/linter:lint",
"options": {
"linter": "eslint",
"tsConfig": ["libs/remix-ui/settings/tsconfig.lib.json"],
"exclude": ["**/node_modules/**", "!libs/remix-ui/settings/**/*"]
"tsConfig": [
"libs/remix-ui/settings/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"!libs/remix-ui/settings/**/*"
]
}
}
}
@ -714,7 +846,9 @@
"builder": "@nrwl/linter:lint",
"options": {
"linter": "eslint",
"tsConfig": ["libs/remix-ui/static-analyser/tsconfig.lib.json"],
"tsConfig": [
"libs/remix-ui/static-analyser/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"!libs/remix-ui/static-analyser/**/*"
@ -733,8 +867,13 @@
"builder": "@nrwl/linter:lint",
"options": {
"linter": "eslint",
"tsConfig": ["libs/remix-ui/checkbox/tsconfig.lib.json"],
"exclude": ["**/node_modules/**", "!libs/remix-ui/checkbox/**/*"]
"tsConfig": [
"libs/remix-ui/checkbox/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"!libs/remix-ui/checkbox/**/*"
]
}
}
}
@ -749,8 +888,13 @@
"builder": "@nrwl/linter:lint",
"options": {
"linter": "eslint",
"tsConfig": ["libs/remix-ui/terminal/tsconfig.lib.json"],
"exclude": ["**/node_modules/**", "!libs/remix-ui/terminal/**/*"]
"tsConfig": [
"libs/remix-ui/terminal/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"!libs/remix-ui/terminal/**/*"
]
}
}
}
@ -765,7 +909,9 @@
"builder": "@nrwl/linter:lint",
"options": {
"linter": "eslint",
"tsConfig": ["libs/remix-ui/plugin-manager/tsconfig.lib.json"],
"tsConfig": [
"libs/remix-ui/plugin-manager/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"!libs/remix-ui/plugin-manager/**/*"
@ -784,8 +930,13 @@
"builder": "@nrwl/linter:lint",
"options": {
"linter": "eslint",
"tsConfig": ["libs/remix-core-plugin/tsconfig.lib.json"],
"exclude": ["**/node_modules/**", "!libs/remix-core-plugin/**/*"]
"tsConfig": [
"libs/remix-core-plugin/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"!libs/remix-core-plugin/**/*"
]
}
},
"build": {
@ -809,7 +960,9 @@
"builder": "@nrwl/linter:lint",
"options": {
"linter": "eslint",
"tsConfig": ["libs/remix-ui/solidity-compiler/tsconfig.lib.json"],
"tsConfig": [
"libs/remix-ui/solidity-compiler/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"!libs/remix-ui/solidity-compiler/**/*"
@ -828,7 +981,9 @@
"builder": "@nrwl/linter:lint",
"options": {
"linter": "eslint",
"tsConfig": ["libs/remix-ui/publish-to-storage/tsconfig.lib.json"],
"tsConfig": [
"libs/remix-ui/publish-to-storage/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"!libs/remix-ui/publish-to-storage/**/*"
@ -847,8 +1002,13 @@
"builder": "@nrwl/linter:lint",
"options": {
"linter": "eslint",
"tsConfig": ["libs/remix-ui/renderer/tsconfig.lib.json"],
"exclude": ["**/node_modules/**", "!libs/remix-ui/renderer/**/*"]
"tsConfig": [
"libs/remix-ui/renderer/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"!libs/remix-ui/renderer/**/*"
]
}
}
}
@ -872,7 +1032,9 @@
"apps/solidity-compiler/src/assets",
"apps/solidity-compiler/src/index.html"
],
"styles": ["apps/solidity-compiler/src/styles.css"],
"styles": [
"apps/solidity-compiler/src/styles.css"
],
"scripts": [],
"webpackConfig": "apps/solidity-compiler/webpack.config.js",
"maxWorkers": 2
@ -917,8 +1079,13 @@
"builder": "@nrwl/linter:lint",
"options": {
"linter": "eslint",
"tsConfig": ["apps/solidity-compiler/tsconfig.app.json"],
"exclude": ["**/node_modules/**", "!apps/solidity-compiler/**/*"]
"tsConfig": [
"apps/solidity-compiler/tsconfig.app.json"
],
"exclude": [
"**/node_modules/**",
"!apps/solidity-compiler/**/*"
]
}
}
}
@ -941,7 +1108,9 @@
"apps/remix-ide-e2e/src/local-plugin/src/favicon.ico",
"apps/remix-ide-e2e/src/local-plugin/src/assets"
],
"styles": ["apps/remix-ide-e2e/src/local-plugin/src/styles.css"],
"styles": [
"apps/remix-ide-e2e/src/local-plugin/src/styles.css"
],
"scripts": [],
"webpackConfig": "@nrwl/react/plugins/webpack"
},
@ -1007,8 +1176,13 @@
"builder": "@nrwl/linter:lint",
"options": {
"linter": "eslint",
"tsConfig": ["libs/remix-ui/home-tab/tsconfig.lib.json"],
"exclude": ["**/node_modules/**", "!libs/remix-ui/home-tab/**/*"]
"tsConfig": [
"libs/remix-ui/home-tab/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"!libs/remix-ui/home-tab/**/*"
]
}
}
}
@ -1023,8 +1197,13 @@
"builder": "@nrwl/linter:lint",
"options": {
"linter": "eslint",
"tsConfig": ["libs/remix-ui/editor/tsconfig.lib.json"],
"exclude": ["**/node_modules/**", "!libs/remix-ui/editor/**/*"]
"tsConfig": [
"libs/remix-ui/editor/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"!libs/remix-ui/editor/**/*"
]
}
}
}
@ -1038,8 +1217,13 @@
"builder": "@nrwl/linter:lint",
"options": {
"linter": "eslint",
"tsConfig": ["libs/remix-ui/editor/tsconfig.lib.json"],
"exclude": ["**/node_modules/**", "!libs/remix-ui/editor/**/*"]
"tsConfig": [
"libs/remix-ui/editor/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"!libs/remix-ui/editor/**/*"
]
}
}
}
@ -1054,8 +1238,13 @@
"builder": "@nrwl/linter:lint",
"options": {
"linter": "eslint",
"tsConfig": ["libs/remix-ui/app/tsconfig.lib.json"],
"exclude": ["**/node_modules/**", "!libs/remix-ui/app/**/*"]
"tsConfig": [
"libs/remix-ui/app/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"!libs/remix-ui/app/**/*"
]
}
}
}
@ -1070,8 +1259,13 @@
"builder": "@nrwl/linter:lint",
"options": {
"linter": "eslint",
"tsConfig": ["libs/remix-ui/helper/tsconfig.lib.json"],
"exclude": ["**/node_modules/**", "!libs/remix-ui/helper/**/*"]
"tsConfig": [
"libs/remix-ui/helper/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"!libs/remix-ui/helper/**/*"
]
}
}
}
@ -1085,8 +1279,13 @@
"builder": "@nrwl/linter:lint",
"options": {
"linter": "eslint",
"tsConfig": ["libs/remix-ui/tabs/tsconfig.lib.json"],
"exclude": ["**/node_modules/**", "!libs/remix-ui/tabs/**/*"]
"tsConfig": [
"libs/remix-ui/tabs/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"!libs/remix-ui/tabs/**/*"
]
}
}
}
@ -1120,8 +1319,13 @@
"builder": "@nrwl/linter:lint",
"options": {
"linter": "eslint",
"tsConfig": ["libs/remix-ui/search/tsconfig.lib.json"],
"exclude": ["**/node_modules/**", "!libs/remix-ui/search/**/*"]
"tsConfig": [
"libs/remix-ui/search/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"!libs/remix-ui/search/**/*"
]
}
}
}
@ -1135,8 +1339,13 @@
"builder": "@nrwl/linter:lint",
"options": {
"linter": "eslint",
"tsConfig": ["libs/remix-ui/panel/tsconfig.lib.json"],
"exclude": ["**/node_modules/**", "!libs/remix-ui/panel/**/*"]
"tsConfig": [
"libs/remix-ui/panel/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"!libs/remix-ui/panel/**/*"
]
}
}
}
@ -1170,7 +1379,9 @@
"builder": "@nrwl/linter:lint",
"options": {
"linter": "eslint",
"tsConfig": ["libs/remix-ui/editor-context-view/tsconfig.lib.json"],
"tsConfig": [
"libs/remix-ui/editor-context-view/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"!libs/remix-ui/editor-context-view/**/*"
@ -1188,7 +1399,9 @@
"builder": "@nrwl/linter:lint",
"options": {
"linter": "eslint",
"tsConfig": ["libs/remix-ui/run-tab/tsconfig.lib.json"],
"tsConfig": [
"libs/remix-ui/run-tab/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"libs/remix-ui/run-tab/**/*.d.ts",
@ -1207,7 +1420,9 @@
"builder": "@nrwl/linter:lint",
"options": {
"linter": "eslint",
"tsConfig": ["libs/remix-ui/permission-handler/tsconfig.lib.json"],
"tsConfig": [
"libs/remix-ui/permission-handler/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"libs/remix-ui/permission-handler/**/*.d.ts",
@ -1227,8 +1442,12 @@
"options": {
"linter": "eslint",
"config": "libs/remix-ws-templates/.eslintrc",
"tsConfig": ["libs/remix-ws-templates/tsconfig.lib.json"],
"exclude": ["**/node_modules/**"]
"tsConfig": [
"libs/remix-ws-templates/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**"
]
}
},
"build": {
@ -1241,7 +1460,9 @@
"assets": [
{
"glob": "templates/**/*",
"ignore": ["templates/**/*/index.ts"],
"ignore": [
"templates/**/*/index.ts"
],
"input": "libs/remix-ws-templates/src/",
"output": "src/"
},
@ -1265,41 +1486,94 @@
"options": {
"linter": "eslint",
"config": "libs/remix-ui/tooltip-popup/.eslintrc.json",
"tsConfig": ["libs/remix-ui/tooltip-popup/tsconfig.lib.json"],
"exclude": ["**/node_modules/**"]
"tsConfig": [
"libs/remix-ui/tooltip-popup/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
}
},
"cli": {
"defaultCollection": "@nrwl/react"
},
"schematics": {
"@nrwl/workspace": {
"library": {
"linter": "eslint"
}
},
"@nrwl/react": {
"application": {
"style": "css",
"linter": "eslint",
"babel": true
},
"component": {
"style": "css"
},
"library": {
"style": "css",
"linter": "eslint"
}
},
"@nrwl/nx-plugin": {
"plugin": {
"linter": "eslint"
"vyper": {
"root": "apps/vyper",
"sourceRoot": "apps/vyper/src",
"projectType": "application",
"architect": {
"build": {
"builder": "@nrwl/web:build",
"outputs": [
"{options.outputPath}"
],
"options": {
"outputPath": "dist/apps/vyper",
"index": "apps/vyper/src/index.html",
"main": "apps/vyper/src/main.tsx",
"polyfills": "apps/vyper/src/polyfills.ts",
"tsConfig": "apps/vyper/tsconfig.app.json",
"assets": [
"apps/vyper/src/favicon.ico",
"apps/vyper/src/assets"
],
"styles": [
"apps/vyper/src/styles.css"
],
"scripts": [],
"webpackConfig": "@nrwl/react/plugins/webpack"
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "apps/vyper/src/environments/environment.ts",
"with": "apps/vyper/src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false,
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
}
]
}
}
},
"serve": {
"builder": "@nrwl/web:dev-server",
"options": {
"buildTarget": "vyper:build",
"port": 5002
},
"configurations": {
"production": {
"buildTarget": "vyper:build:production",
"hmr": false
}
}
},
"lint": {
"builder": "@nrwl/linter:lint",
"options": {
"linter": "eslint",
"config": "apps/vyper/.eslintrc",
"files": [
"apps/vyper/src/**/*.js", "apps/vyper/src/**/*.ts"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
}
},
"defaultProject": "remix-ide"
}
}
}

@ -5359,6 +5359,11 @@ asap@^2.0.0, asap@~2.0.5:
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.5.tgz#522765b50c3510490e52d7dcfe085ef9ba96958f"
integrity sha512-7Ql0Lz9sffzP1jDkhjeju5/6z0LnwZAMZdlJoTe2GghKnYNA+H1rZOD8rWx4b9EBjux0kJq66igvQkANmbWnKg==
asap@~2.0.3:
version "2.0.6"
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==
asn1.js@^5.2.0:
version "5.4.1"
resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07"
@ -6325,6 +6330,11 @@ base-x@^3.0.2, base-x@^3.0.8:
dependencies:
safe-buffer "^5.0.1"
base16@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/base16/-/base16-1.0.0.tgz#e297f60d7ec1014a7a971a39ebc8a98c0b681e70"
integrity sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ==
base64-js@^1.0.2, base64-js@^1.3.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
@ -8385,6 +8395,13 @@ cross-blob@^2.0.1:
blob-polyfill "^5.0.20210201"
fetch-blob "^2.1.2"
cross-fetch@^3.1.5:
version "3.1.5"
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f"
integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==
dependencies:
node-fetch "2.6.7"
cross-spawn-async@^2.1.1:
version "2.2.5"
resolved "https://registry.yarnpkg.com/cross-spawn-async/-/cross-spawn-async-2.2.5.tgz#845ff0c0834a3ded9d160daca6d390906bb288cc"
@ -10577,6 +10594,31 @@ fb-watchman@^2.0.0:
dependencies:
bser "2.1.1"
fbemitter@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/fbemitter/-/fbemitter-3.0.0.tgz#00b2a1af5411254aab416cd75f9e6289bee4bff3"
integrity sha512-KWKaceCwKQU0+HPoop6gn4eOHk50bBv/VxjJtGMfwmJt3D29JpN4H4eisCtIPA+a8GVBam+ldMMpMjJUvpDyHw==
dependencies:
fbjs "^3.0.0"
fbjs-css-vars@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz#216551136ae02fe255932c3ec8775f18e2c078b8"
integrity sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==
fbjs@^3.0.0, fbjs@^3.0.1:
version "3.0.4"
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-3.0.4.tgz#e1871c6bd3083bac71ff2da868ad5067d37716c6"
integrity sha512-ucV0tDODnGV3JCnnkmoszb5lf4bNpzjv80K41wd4k798Etq+UYD0y0TIfalLjZoKgjive6/adkRnszwapiDgBQ==
dependencies:
cross-fetch "^3.1.5"
fbjs-css-vars "^1.0.0"
loose-envify "^1.0.0"
object-assign "^4.1.0"
promise "^7.1.1"
setimmediate "^1.0.5"
ua-parser-js "^0.7.30"
fd-slicer@~1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e"
@ -10864,6 +10906,14 @@ flush-write-stream@^1.0.0, flush-write-stream@^1.0.2:
inherits "^2.0.3"
readable-stream "^2.3.6"
flux@^4.0.1:
version "4.0.3"
resolved "https://registry.yarnpkg.com/flux/-/flux-4.0.3.tgz#573b504a24982c4768fdfb59d8d2ea5637d72ee7"
integrity sha512-yKAbrp7JhZhj6uiT1FTuVMlIAT1J4jqEyBpFApi1kxpGZCvacMVc/t1pMQyotqHhAgvoE3bNvAykhCo2CLjnYw==
dependencies:
fbemitter "^3.0.0"
fbjs "^3.0.1"
fn.name@1.x.x:
version "1.1.0"
resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc"
@ -15082,6 +15132,11 @@ lodash.clonedeep@^4.5.0, lodash.clonedeep@~4.5.0:
resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=
lodash.curry@^4.0.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.curry/-/lodash.curry-4.1.1.tgz#248e36072ede906501d75966200a86dab8b23170"
integrity sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA==
lodash.debounce@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
@ -15097,6 +15152,11 @@ lodash.flattendeep@^4.4.0:
resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2"
integrity sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=
lodash.flow@^3.3.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/lodash.flow/-/lodash.flow-3.5.0.tgz#87bf40292b8cf83e4e8ce1a3ae4209e20071675a"
integrity sha512-ff3BX/tSioo+XojX4MOsOMhJw0nZoUEF011LX8g8d3gvjVbxd89cCio4BCXronjxcTUIJUoqKEUA+n4CqvvRPw==
lodash.get@^4.4.2:
version "4.4.2"
resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
@ -16600,6 +16660,13 @@ node-fetch-npm@^2.0.2:
json-parse-better-errors "^1.0.0"
safe-buffer "^5.1.1"
node-fetch@2.6.7, node-fetch@^2.5.0, node-fetch@^2.6.0, node-fetch@^2.6.1:
version "2.6.7"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
dependencies:
whatwg-url "^5.0.0"
node-fetch@3.0.0-beta.9:
version "3.0.0-beta.9"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.0.0-beta.9.tgz#0a7554cfb824380dd6812864389923c783c80d9b"
@ -16608,13 +16675,6 @@ node-fetch@3.0.0-beta.9:
data-uri-to-buffer "^3.0.1"
fetch-blob "^2.1.1"
node-fetch@^2.5.0, node-fetch@^2.6.0, node-fetch@^2.6.1:
version "2.6.7"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
dependencies:
whatwg-url "^5.0.0"
node-forge@^0.10.0:
version "0.10.0"
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3"
@ -18833,6 +18893,13 @@ promise.series@^0.2.0:
resolved "https://registry.yarnpkg.com/promise.series/-/promise.series-0.2.0.tgz#2cc7ebe959fc3a6619c04ab4dbdc9e452d864bbd"
integrity sha1-LMfr6Vn8OmYZwEq029yeRS2GS70=
promise@^7.1.1:
version "7.3.1"
resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf"
integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==
dependencies:
asap "~2.0.3"
prompts@^2.0.1:
version "2.4.2"
resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069"
@ -19021,6 +19088,11 @@ pupa@^2.1.1:
dependencies:
escape-goat "^2.0.0"
pure-color@^1.2.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/pure-color/-/pure-color-1.3.0.tgz#1fe064fb0ac851f0de61320a8bf796836422f33e"
integrity sha512-QFADYnsVoBMw1srW7OVKEYjG+MbIa49s54w1MA1EDY6r2r/sTcKKYqRX1f4GYvnXP7eN/Pe9HFcX+hwzmrXRHA==
q@^1.1.2, q@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
@ -19202,6 +19274,16 @@ re-emitter@1.1.3:
resolved "https://registry.yarnpkg.com/re-emitter/-/re-emitter-1.1.3.tgz#fa9e319ffdeeeb35b27296ef0f3d374dac2f52a7"
integrity sha1-+p4xn/3u6zWycpbvDz03TawvUqc=
react-base16-styling@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/react-base16-styling/-/react-base16-styling-0.6.0.tgz#ef2156d66cf4139695c8a167886cb69ea660792c"
integrity sha512-yvh/7CArceR/jNATXOKDlvTnPKPmGZz7zsenQ3jUwLzHkNUR0CvY3yGYJbWJ/nnxsL8Sgmt5cO3/SILVuPO6TQ==
dependencies:
base16 "^1.0.0"
lodash.curry "^4.0.1"
lodash.flow "^3.3.0"
pure-color "^1.2.0"
react-beautiful-dnd@^13.1.0:
version "13.1.0"
resolved "https://registry.yarnpkg.com/react-beautiful-dnd/-/react-beautiful-dnd-13.1.0.tgz#ec97c81093593526454b0de69852ae433783844d"
@ -19265,6 +19347,16 @@ react-is@^17.0.1, react-is@^17.0.2:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
react-json-view@^1.21.3:
version "1.21.3"
resolved "https://registry.yarnpkg.com/react-json-view/-/react-json-view-1.21.3.tgz#f184209ee8f1bf374fb0c41b0813cff54549c475"
integrity sha512-13p8IREj9/x/Ye4WI/JpjhoIwuzEgUAtgJZNBJckfzJt1qyh24BdTm6UQNGnyTq9dapQdrqvquZTo3dz1X6Cjw==
dependencies:
flux "^4.0.1"
react-base16-styling "^0.6.0"
react-lifecycles-compat "^3.0.4"
react-textarea-autosize "^8.3.2"
react-lifecycles-compat@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
@ -19314,6 +19406,15 @@ react-tabs@^3.2.2:
clsx "^1.1.0"
prop-types "^15.5.0"
react-textarea-autosize@^8.3.2:
version "8.3.4"
resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-8.3.4.tgz#270a343de7ad350534141b02c9cb78903e553524"
integrity sha512-CdtmP8Dc19xL8/R6sWvtknD/eCXkQr30dtvC4VmGInhRsfF8X/ihXCq6+9l9qbxmKRiq407/7z5fxE7cVWQNgQ==
dependencies:
"@babel/runtime" "^7.10.2"
use-composed-ref "^1.3.0"
use-latest "^1.2.1"
react-transition-group@^4.4.1:
version "4.4.2"
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.2.tgz#8b59a56f09ced7b55cbd53c36768b922890d5470"
@ -22580,6 +22681,11 @@ typescript@^4.4.3:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.4.tgz#2cd01a1a1f160704d3101fd5a58ff0f9fcb8030c"
integrity sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==
ua-parser-js@^0.7.30:
version "0.7.31"
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.31.tgz#649a656b191dffab4f21d5e053e27ca17cbff5c6"
integrity sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==
uglify-js@^2.8.16:
version "2.8.29"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd"
@ -22966,6 +23072,23 @@ url@^0.11.0, url@~0.11.0:
punycode "1.3.2"
querystring "0.2.0"
use-composed-ref@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/use-composed-ref/-/use-composed-ref-1.3.0.tgz#3d8104db34b7b264030a9d916c5e94fbe280dbda"
integrity sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ==
use-isomorphic-layout-effect@^1.1.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz#497cefb13d863d687b08477d9e5a164ad8c1a6fb"
integrity sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==
use-latest@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/use-latest/-/use-latest-1.2.1.tgz#d13dfb4b08c28e3e33991546a2cee53e14038cf2"
integrity sha512-xA+AVm/Wlg3e2P/JiItTziwS7FK92LWrDB0p+hgXloIMuVCeJJ8v6f0eeHyPZaJrM+usM1FkFfbNCrJGs8A/zw==
dependencies:
use-isomorphic-layout-effect "^1.1.1"
use-memo-one@^1.1.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/use-memo-one/-/use-memo-one-1.1.2.tgz#0c8203a329f76e040047a35a1197defe342fab20"

Loading…
Cancel
Save