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. 50
      .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. 40
      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. 4
      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. 2
      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. 42
      nx.json
  65. 1
      package.json
  66. 496
      workspace.json
  67. 137
      yarn.lock

@ -267,6 +267,50 @@ jobs:
- 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:
# specify the version you desire here
@ -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()
}
}
@ -388,3 +405,18 @@ const configFile = `
}
}
`
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)

@ -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 {
}
}
}
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)
}

@ -103,3 +103,5 @@ export const UUPSupgradeAbi = {
"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
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
## HELP SECTION
The remixd command is:
```
> remixd
```
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.
```
Usage: remixd -s <shared folder>
> remixd -h
Usage: remixd [options]
Provide a two-way connection between the local computer and Remix IDE
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": {

@ -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