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

pull/4933/head
bunsenstraat 4 months ago
commit 729c0eef84
  1. 1
      .circleci/config.yml
  2. 32
      .github/ISSUE_TEMPLATE/bug_report.md
  3. 20
      .github/ISSUE_TEMPLATE/feature_request.md
  4. 1
      README.md
  5. 24
      apps/circuit-compiler/src/app/services/circomPluginClient.ts
  6. 2
      apps/remix-ide-e2e/nightwatch-chrome.ts
  7. 2
      apps/remix-ide-e2e/nightwatch-firefox.ts
  8. 2
      apps/remix-ide-e2e/src/commands/addFileSnekmate.ts
  9. 1
      apps/remix-ide-e2e/src/tests/fileExplorer.test.ts
  10. 4
      apps/remix-ide-e2e/src/tests/file_explorer_context_menu.test.ts
  11. 3
      apps/remix-ide-e2e/src/tests/file_explorer_dragdrop.test.ts
  12. 47
      apps/remix-ide-e2e/src/tests/homeTab.test.ts
  13. 54
      apps/remix-ide-e2e/src/tests/importFromGithub.test.ts
  14. 366
      apps/remix-ide-e2e/src/tests/remixd.test.ts
  15. 12
      apps/remix-ide-e2e/src/tests/terminal.test.ts
  16. 3
      apps/remix-ide-e2e/src/tests/url.test.ts
  17. 90
      apps/remix-ide-e2e/src/tests/vyper_api.test.ts
  18. 7
      apps/remix-ide-e2e/src/tests/workspace_git.test.ts
  19. 3
      apps/remix-ide/contracts/ballot.sol
  20. 398
      apps/remix-ide/contracts/foundry/cache/solidity-files-cache.json
  21. 6
      apps/remix-ide/contracts/foundry/foundry.toml
  22. 385
      apps/remix-ide/contracts/foundry/out/Counter.s.sol/CounterScript.json
  23. 377
      apps/remix-ide/contracts/foundry/out/Counter.sol/Counter.json
  24. 1865
      apps/remix-ide/contracts/foundry/out/Counter.t.sol/CounterTest.json
  25. 3729
      apps/remix-ide/contracts/foundry/out/Script.sol/Script.json
  26. 38186
      apps/remix-ide/contracts/foundry/out/Test.sol/Test.json
  27. 37620
      apps/remix-ide/contracts/foundry/out/Test.sol/stdError.json
  28. 37347
      apps/remix-ide/contracts/foundry/out/Test.sol/stdMath.json
  29. 37499
      apps/remix-ide/contracts/foundry/out/Test.sol/stdStorage.json
  30. 10800
      apps/remix-ide/contracts/foundry/out/Vm.sol/Vm.json
  31. 110867
      apps/remix-ide/contracts/foundry/out/console.sol/console.json
  32. 110866
      apps/remix-ide/contracts/foundry/out/console2.sol/console2.json
  33. 23713
      apps/remix-ide/contracts/foundry/out/test.sol/DSTest.json
  34. 14
      apps/remix-ide/contracts/foundry/src/Counter.sol
  35. 12
      apps/remix-ide/src/app.js
  36. 1
      apps/remix-ide/src/app/components/preload.tsx
  37. 2
      apps/remix-ide/src/app/components/side-panel.tsx
  38. 105
      apps/remix-ide/src/app/components/status-bar.tsx
  39. 10
      apps/remix-ide/src/app/files/dgitProvider.ts
  40. 2
      apps/remix-ide/src/app/files/fileManager.ts
  41. 4
      apps/remix-ide/src/app/panels/file-panel.js
  42. 9
      apps/remix-ide/src/app/panels/tab-proxy.js
  43. 1
      apps/remix-ide/src/app/plugins/compile-details.tsx
  44. 224
      apps/remix-ide/src/app/plugins/remixGuide.tsx
  45. 47
      apps/remix-ide/src/app/plugins/remixGuideData.json
  46. 12
      apps/remix-ide/src/app/plugins/solcoderAI.tsx
  47. 2
      apps/remix-ide/src/app/tabs/locales/en/editor.json
  48. 2
      apps/remix-ide/src/app/tabs/locales/en/filePanel.json
  49. 16
      apps/remix-ide/src/app/tabs/locales/en/home.json
  50. 13
      apps/remix-ide/src/app/tabs/locales/en/solidity.json
  51. 9
      apps/remix-ide/src/app/tabs/locales/en/udapp.json
  52. 1
      apps/remix-ide/src/app/tabs/settings-tab.tsx
  53. 1
      apps/remix-ide/src/app/udapp/run-tab.js
  54. BIN
      apps/remix-ide/src/assets/img/solidityScanLogo.webp
  55. BIN
      apps/remix-ide/src/assets/img/staticAnalysisColorBlue.webp
  56. BIN
      apps/remix-ide/src/assets/img/swarmColor.webp
  57. 6
      apps/remix-ide/src/remixAppManager.js
  58. 2
      apps/remix-ide/src/remixEngine.js
  59. 19
      apps/remix-ide/src/types/index.d.ts
  60. 2
      apps/remix-ide/webpack.config.js
  61. 31
      apps/vyper/src/app/app.tsx
  62. 23
      apps/vyper/src/app/utils/compiler.tsx
  63. 25
      apps/vyper/src/app/utils/remix-client.tsx
  64. 46
      build-qa-doc.js
  65. 59
      libs/remix-simulator/src/methods/accounts.ts
  66. 12
      libs/remix-simulator/src/provider.ts
  67. 36
      libs/remix-simulator/test/accounts.ts
  68. 13
      libs/remix-ui/app/src/lib/remix-app/components/dragbar/dragbar.tsx
  69. 5
      libs/remix-ui/app/src/lib/remix-app/remix-app.tsx
  70. 6
      libs/remix-ui/app/src/lib/remix-app/style/remix-app.css
  71. 37
      libs/remix-ui/editor/src/lib/providers/documentationProvider.ts
  72. 65
      libs/remix-ui/editor/src/lib/remix-ui-editor.tsx
  73. 9
      libs/remix-ui/grid-view/src/lib/components/customCheckbox.tsx
  74. 4
      libs/remix-ui/grid-view/src/lib/filtersContext.tsx
  75. 6
      libs/remix-ui/grid-view/src/lib/remix-ui-grid-cell.css
  76. 42
      libs/remix-ui/grid-view/src/lib/remix-ui-grid-cell.tsx
  77. 5
      libs/remix-ui/grid-view/src/lib/remix-ui-grid-section.tsx
  78. 69
      libs/remix-ui/grid-view/src/lib/remix-ui-grid-view.tsx
  79. 27
      libs/remix-ui/home-tab/src/lib/components/homeTabFeatured.tsx
  80. 2
      libs/remix-ui/home-tab/src/lib/components/homeTabFeaturedPlugins.tsx
  81. 277
      libs/remix-ui/home-tab/src/lib/components/homeTabFile.tsx
  82. 162
      libs/remix-ui/home-tab/src/lib/components/homeTabGetStarted.tsx
  83. 136
      libs/remix-ui/home-tab/src/lib/components/homeTabTitle.tsx
  84. 6
      libs/remix-ui/home-tab/src/lib/components/homeTablangOptions.tsx
  85. 39
      libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.css
  86. 27
      libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.tsx
  87. 4
      libs/remix-ui/panel/src/lib/plugins/panel.css
  88. 20
      libs/remix-ui/panel/src/lib/types/index.ts
  89. 11
      libs/remix-ui/run-tab/src/lib/actions/account.ts
  90. 4
      libs/remix-ui/run-tab/src/lib/actions/deploy.ts
  91. 20
      libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx
  92. 10
      libs/remix-ui/run-tab/src/lib/components/contractGUI.tsx
  93. 6
      libs/remix-ui/run-tab/src/lib/components/environment.tsx
  94. 2
      libs/remix-ui/run-tab/src/lib/components/gasLimit.tsx
  95. 6
      libs/remix-ui/run-tab/src/lib/components/instanceContainerUI.tsx
  96. 6
      libs/remix-ui/run-tab/src/lib/components/recorderCardUI.tsx
  97. 118
      libs/remix-ui/run-tab/src/lib/components/universalDappUI.tsx
  98. 3
      libs/remix-ui/run-tab/src/lib/css/run-tab.css
  99. 23
      libs/remix-ui/run-tab/src/lib/run-tab.tsx
  100. 14
      libs/remix-ui/run-tab/src/lib/types/index.ts
  101. Some files were not shown because too many files have changed in this diff Show More

@ -364,6 +364,7 @@ jobs:
- run: yarn install --cwd ./apps/remix-ide-e2e --modules-folder ../../node_modules || yarn install --cwd ./apps/remix-ide-e2e --modules-folder ../../node_modules - run: yarn install --cwd ./apps/remix-ide-e2e --modules-folder ../../node_modules || yarn install --cwd ./apps/remix-ide-e2e --modules-folder ../../node_modules
- run: mkdir node_modules/hardhat && wget https://unpkg.com/hardhat/console.sol -O node_modules/hardhat/console.sol - run: mkdir node_modules/hardhat && wget https://unpkg.com/hardhat/console.sol -O node_modules/hardhat/console.sol
- run: ls -la ./dist/apps/remix-ide/assets/js - run: ls -la ./dist/apps/remix-ide/assets/js
- run: sudo apt update && sudo apt install python3-pip -y
- when: - when:
condition: condition:
equal: [ "chrome", << parameters.browser >> ] equal: [ "chrome", << parameters.browser >> ]

@ -0,0 +1,32 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. Windows, Linux or MacOS]
- Browser [e.g. chrome, firefox]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

@ -32,7 +32,6 @@
![Remix screenshot](https://github.com/ethereum/remix-project/raw/master/apps/remix-ide/remix-screenshot-400h.png) ![Remix screenshot](https://github.com/ethereum/remix-project/raw/master/apps/remix-ide/remix-screenshot-400h.png)
**VSCode extension**, see: [Ethereum-Remix](https://marketplace.visualstudio.com/items?itemName=RemixProject.ethereum-remix)
## Remix libraries ## Remix libraries
Remix libraries are essential for Remix IDE's native plugins. Read more about libraries [here](libs/README.md) Remix libraries are essential for Remix IDE's native plugins. Read more about libraries [here](libs/README.md)

@ -10,6 +10,8 @@ import * as compilerV215 from 'circom_wasm/v2.1.5'
import { extractNameFromKey, extractParentFromKey } from '@remix-ui/helper' import { extractNameFromKey, extractParentFromKey } from '@remix-ui/helper'
import { CompilationConfig, CompilerReport, PrimeValue, ResolverOutput } from '../types' import { CompilationConfig, CompilerReport, PrimeValue, ResolverOutput } from '../types'
// @ts-ignore
const _paq = (window._paq = window._paq || [])
export class CircomPluginClient extends PluginClient { export class CircomPluginClient extends PluginClient {
public internalEvents: EventManager public internalEvents: EventManager
private _compilationConfig: CompilationConfig = { private _compilationConfig: CompilationConfig = {
@ -119,6 +121,7 @@ export class CircomPluginClient extends PluginClient {
} else { } else {
// @ts-ignore // @ts-ignore
await this.call('editor', 'clearErrorMarkers', [path]) await this.call('editor', 'clearErrorMarkers', [path])
this.emit('statusChanged', { key: 'none' })
} }
} }
@ -130,6 +133,7 @@ export class CircomPluginClient extends PluginClient {
async compile(path: string, compilationConfig?: CompilationConfig): Promise<void> { async compile(path: string, compilationConfig?: CompilationConfig): Promise<void> {
this.internalEvents.emit('circuit_compiling_start') this.internalEvents.emit('circuit_compiling_start')
this.emit('statusChanged', { key: 'loading', title: 'Compiling...', type: 'info' })
// @ts-ignore // @ts-ignore
this.call('terminal', 'log', { type: 'log', value: 'Compiling ' + path }) this.call('terminal', 'log', { type: 'log', value: 'Compiling ' + path })
const [parseErrors, filePathToId] = await this.parse(path) const [parseErrors, filePathToId] = await this.parse(path)
@ -145,6 +149,7 @@ export class CircomPluginClient extends PluginClient {
} }
} else { } else {
this.internalEvents.emit('circuit_parsing_done', parseErrors, filePathToId) this.internalEvents.emit('circuit_parsing_done', parseErrors, filePathToId)
this.emit('statusChanged', { key: 'succeed', title: 'circuit compiled successfully', type: 'success' })
} }
if (compilationConfig) { if (compilationConfig) {
const { prime, version } = compilationConfig const { prime, version } = compilationConfig
@ -159,6 +164,7 @@ export class CircomPluginClient extends PluginClient {
const circuitErrors = circuitApi.report() const circuitErrors = circuitApi.report()
this.logCompilerReport(circuitErrors) this.logCompilerReport(circuitErrors)
_paq.push(['trackEvent', 'circuit-compiler', 'compile', 'Compilation failed'])
throw new Error(circuitErrors) throw new Error(circuitErrors)
} else { } else {
this.lastCompiledFile = path this.lastCompiledFile = path
@ -178,6 +184,7 @@ export class CircomPluginClient extends PluginClient {
} else { } else {
this.internalEvents.emit('circuit_compiling_done', []) this.internalEvents.emit('circuit_compiling_done', [])
} }
_paq.push(['trackEvent', 'circuit-compiler', 'compile', 'Compilation successful'])
circuitApi.log().map(log => { circuitApi.log().map(log => {
log && this.call('terminal', 'log', { type: 'log', value: log }) log && this.call('terminal', 'log', { type: 'log', value: log })
}) })
@ -188,6 +195,7 @@ export class CircomPluginClient extends PluginClient {
async generateR1cs (path: string, compilationConfig?: CompilationConfig): Promise<void> { async generateR1cs (path: string, compilationConfig?: CompilationConfig): Promise<void> {
this.internalEvents.emit('circuit_generating_r1cs_start') this.internalEvents.emit('circuit_generating_r1cs_start')
this.emit('statusChanged', { key: 'loading', title: 'Generating...', type: 'info' })
// @ts-ignore // @ts-ignore
this.call('terminal', 'log', { type: 'log', value: 'Generating R1CS for ' + path }) this.call('terminal', 'log', { type: 'log', value: 'Generating R1CS for ' + path })
const [parseErrors, filePathToId] = await this.parse(path) const [parseErrors, filePathToId] = await this.parse(path)
@ -203,6 +211,7 @@ export class CircomPluginClient extends PluginClient {
} }
} else { } else {
this.internalEvents.emit('circuit_parsing_done', parseErrors, filePathToId) this.internalEvents.emit('circuit_parsing_done', parseErrors, filePathToId)
this.emit('statusChanged', { key: 'succeed', title: 'r1cs generated successfully', type: 'success' })
} }
if (compilationConfig) { if (compilationConfig) {
const { prime, version } = compilationConfig const { prime, version } = compilationConfig
@ -217,6 +226,7 @@ export class CircomPluginClient extends PluginClient {
const r1csErrors = r1csApi.report() const r1csErrors = r1csApi.report()
this.logCompilerReport(r1csErrors) this.logCompilerReport(r1csErrors)
_paq.push(['trackEvent', 'circuit-compiler', 'generateR1cs', 'R1CS Generation failed'])
throw new Error(r1csErrors) throw new Error(r1csErrors)
} else { } else {
this.internalEvents.emit('circuit_generating_r1cs_done') this.internalEvents.emit('circuit_generating_r1cs_done')
@ -225,6 +235,7 @@ export class CircomPluginClient extends PluginClient {
// @ts-ignore // @ts-ignore
await this.call('fileManager', 'writeFile', writePath, r1csProgram, true) await this.call('fileManager', 'writeFile', writePath, r1csProgram, true)
_paq.push(['trackEvent', 'circuit-compiler', 'generateR1cs', 'R1CS Generation successful'])
r1csApi.log().map(log => { r1csApi.log().map(log => {
log && this.call('terminal', 'log', { type: 'log', value: log }) log && this.call('terminal', 'log', { type: 'log', value: log })
}) })
@ -235,6 +246,7 @@ export class CircomPluginClient extends PluginClient {
async computeWitness (input: string): Promise<void> { async computeWitness (input: string): Promise<void> {
this.internalEvents.emit('circuit_computing_witness_start') this.internalEvents.emit('circuit_computing_witness_start')
this.emit('statusChanged', { key: 'loading', title: 'Computing...', type: 'info' })
const wasmPath = this.lastCompiledCircuitPath const wasmPath = this.lastCompiledCircuitPath
if (!wasmPath) throw new Error('No wasm file found') if (!wasmPath) throw new Error('No wasm file found')
@ -244,7 +256,9 @@ export class CircomPluginClient extends PluginClient {
const witness = this.compiler ? await this.compiler.generate_witness(dataRead, input) : await generate_witness(dataRead, input) const witness = this.compiler ? await this.compiler.generate_witness(dataRead, input) : await generate_witness(dataRead, input)
// @ts-ignore // @ts-ignore
await this.call('fileManager', 'writeFile', wasmPath.replace('.wasm', '.wtn'), witness, true) await this.call('fileManager', 'writeFile', wasmPath.replace('.wasm', '.wtn'), witness, true)
_paq.push(['trackEvent', 'circuit-compiler', 'computeWitness', 'Witness computing successful'])
this.internalEvents.emit('circuit_computing_witness_done') this.internalEvents.emit('circuit_computing_witness_done')
this.emit('statusChanged', { key: 'succeed', title: 'witness computed successfully', type: 'success' })
} }
async resolveDependencies(filePath: string, fileContent: string, output?: Record<string, string>, depPath: string = '', blackPath: string[] = []): Promise<Record<string, string>> { async resolveDependencies(filePath: string, fileContent: string, output?: Record<string, string>, depPath: string = '', blackPath: string[] = []): Promise<Record<string, string>> {
@ -403,7 +417,13 @@ export class CircomPluginClient extends PluginClient {
async logCompilerReport (report: CompilerReport[]): Promise<void> { async logCompilerReport (report: CompilerReport[]): Promise<void> {
this.call('terminal', 'log', { type: 'log', value: JSON.stringify(report, null, 2) }) this.call('terminal', 'log', { type: 'log', value: JSON.stringify(report, null, 2) })
if (report[0].type === 'Error') this.call('terminal', 'log', { type: 'error', value: 'previous errors were found' }) if (report[0].type === 'Error') {
if (report[0].type === 'Warning') this.call('terminal', 'log', { type: 'log', value: 'previous warnings were found' }) this.call('terminal', 'log', { type: 'error', value: 'previous errors were found' })
this.emit('statusChanged', { key: report.length, title: `You have ${report.length} problem${report.length === 1 ? '' : 's'}`, type: 'error' })
}
if (report[0].type === 'Warning') {
this.call('terminal', 'log', { type: 'log', value: 'previous warnings were found' })
this.emit('statusChanged', { key: report.length, title: `You have ${report.length} problem${report.length === 1 ? '' : 's'}`, type: 'warning' })
}
} }
} }

@ -21,7 +21,7 @@ module.exports = {
'default': { 'default': {
globals: { globals: {
waitForConditionTimeout: 10000, waitForConditionTimeout: 10000,
asyncHookTimeout: 100000 asyncHookTimeout: 10000000
}, },
screenshots: { screenshots: {
enabled: true, enabled: true,

@ -18,7 +18,7 @@ module.exports = {
'default': { 'default': {
globals: { globals: {
waitForConditionTimeout: 10000, waitForConditionTimeout: 10000,
asyncHookTimeout: 100000 asyncHookTimeout: 10000000
}, },
screenshots: { screenshots: {
enabled: true, enabled: true,

@ -60,7 +60,7 @@ function addFileSnekmate(browser: NightwatchBrowser, name: string, content: Nigh
}) })
.setEditorValue(content.content) .setEditorValue(content.content)
.getEditorValue((result) => { .getEditorValue((result) => {
if(result != content.content) { if (result != content.content) {
browser.setEditorValue(content.content) browser.setEditorValue(content.content)
} }
}) })

@ -181,5 +181,4 @@ module.exports = {
.end() .end()
} }
} }

@ -90,7 +90,7 @@ module.exports = {
.rightClick('li[data-id="treeViewLitreeViewItemREADME.txt"]') .rightClick('li[data-id="treeViewLitreeViewItemREADME.txt"]')
.waitForElementPresent('[data-id="contextMenuItemcopy') .waitForElementPresent('[data-id="contextMenuItemcopy')
.click('[data-id="contextMenuItemcopy"]') .click('[data-id="contextMenuItemcopy"]')
.rightClick('*[data-id="treeViewLiMenu"]') .rightClick('*[data-id="treeViewUltreeViewMenu"]')
.saveScreenshot('./reports/screenshot/file_explorer_context_menu.png') .saveScreenshot('./reports/screenshot/file_explorer_context_menu.png')
.click('*[data-id="contextMenuItempaste"]') .click('*[data-id="contextMenuItempaste"]')
.waitForElementVisible('*[data-id="treeViewLitreeViewItemCopy_README.txt"]', 7000) .waitForElementVisible('*[data-id="treeViewLitreeViewItemCopy_README.txt"]', 7000)
@ -111,7 +111,7 @@ module.exports = {
.rightClick('li[data-id="treeViewLitreeViewItemcontracts"]') .rightClick('li[data-id="treeViewLitreeViewItemcontracts"]')
.waitForElementPresent('[data-id="contextMenuItemcopy') .waitForElementPresent('[data-id="contextMenuItemcopy')
.click('[data-id="contextMenuItemcopy"]') .click('[data-id="contextMenuItemcopy"]')
.rightClick('*[data-id="treeViewLiMenu"]') .rightClick('*[data-id="treeViewUltreeViewMenu"]')
.click('*[data-id="contextMenuItempaste"]') .click('*[data-id="contextMenuItempaste"]')
.waitForElementVisible('*[data-id="treeViewLitreeViewItemCopy_contracts"]', 7000) .waitForElementVisible('*[data-id="treeViewLitreeViewItemCopy_contracts"]', 7000)
}, },

@ -6,7 +6,6 @@ const checkBrowserIsChrome = function (browser: NightwatchBrowser) {
return browser.browserName.indexOf('chrome') > -1 return browser.browserName.indexOf('chrome') > -1
} }
module.exports = { module.exports = {
'@disabled': true, '@disabled': true,
before: function (browser: NightwatchBrowser, done: VoidFunction) { before: function (browser: NightwatchBrowser, done: VoidFunction) {
@ -98,6 +97,4 @@ module.exports = {
} }
} }
} }

@ -3,15 +3,52 @@ import { NightwatchBrowser } from 'nightwatch'
import init from '../helpers/init' import init from '../helpers/init'
module.exports = { module.exports = {
'@disabled': true,
before: function (browser: NightwatchBrowser, done: VoidFunction) { before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done) init(browser, done)
}, },
'Should start coding': function (browser: NightwatchBrowser) { 'Should start coding #group1': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="homeTabGetStartedremixDefault"]')
.click('*[data-id="homeTabGetStartedremixDefault"]')
.waitForElementVisible('*[data-id="treeViewDivtreeViewItemcontracts/1_Storage.sol"]')
.waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts/2_Owner.sol"')
.waitForElementVisible('*[data-id="treeViewDivDraggableItemREADME.txt"')
.click('*[data-id="treeViewDivtreeViewItemcontracts/1_Storage.sol"]')
.waitForElementPresent({
selector: "//div[contains(@class, 'view-line') and contains(.//span, 'pragma')]",
locateStrategy: 'xpath'
})
.getEditorValue((editorContent) => {
browser.assert.ok(editorContent.indexOf(`pragma solidity`) !== -1, 'unexpected content encountered!')
})
},
'Should start with ERC20 workspace #group1': function (browser: NightwatchBrowser) {
browser
.click('*[data-path="home"')
.waitForElementVisible('*[data-id="homeTabGetStartedozerc20"]')
.click('*[data-id="homeTabGetStartedozerc20"')
.waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts/MyToken.sol"]')
.waitForElementVisible('*[data-id="treeViewDivtreeViewItemtests/MyToken_test.sol"]')
.click('*[data-id="treeViewDivtreeViewItemtests/MyToken_test.sol"]')
.waitForElementPresent({
selector: "//div[contains(@class, 'view-line') and contains(.//span, 'pragma')]",
locateStrategy: 'xpath'
})
.getEditorValue((editorContent) => {
browser.assert.ok(editorContent.indexOf(`import "../contracts/MyToken.sol";`) !== -1, 'content encountered!')
})
},
'Should create a new file in the current workspace': '' +function (browser: NightwatchBrowser) {
browser browser
.waitForElementVisible('*[data-id="homeTabStartCoding"]') .click('*[data-path="home"]')
.click('*[data-id="homeTabStartCoding"]') .waitForElementVisible('*[data-id="homeTabNewFile"]')
.waitForElementVisible('div[data-id="treeViewDivtreeViewItemcontracts/HelloWorld.sol"]') .click('*[data-id="homeTabNewFile"]')
.waitForElementVisible('*[data-id$="fileExplorerTreeItemInput"]')
.sendKeys('*[data-id$="fileExplorerTreeItemInput"]', 'HometabNewFile.txt')
.sendKeys('*[data-id$="fileExplorerTreeItemInput"]', browser.Keys.ENTER)
.waitForElementVisible('*[data-id="treeViewLitreeViewItemHometabNewFile.txt"]', 7000)
} }
} }

@ -3,9 +3,9 @@ import { NightwatchBrowser } from 'nightwatch'
import init from '../helpers/init' import init from '../helpers/init'
const testData = { const testData = {
validURL: 'https://github.com/OpenZeppelin/openzeppelin-solidity/blob/67bca857eedf99bf44a4b6a0fc5b5ed553135316/contracts/access/Roles.sol', validURL: 'https://github.com/remix-project-org/git-hometab-test.git',
invalidURL: 'https://github.com/Oppelin/Roles.sol', invalidURL: 'https://github.com/Oppelin/Roles.sol',
JSON: 'https://github.com/ethereum/remix-project/blob/master/package.json' JSON: 'https://github.com/remix-project-org/git-hometab-test.git'
} }
module.exports = { module.exports = {
@ -22,63 +22,53 @@ module.exports = {
.waitForElementVisible('button[data-id="landingPageImportFromGitHubButton"]') .waitForElementVisible('button[data-id="landingPageImportFromGitHubButton"]')
.pause(1000) .pause(1000)
.click('button[data-id="landingPageImportFromGitHubButton"]') .click('button[data-id="landingPageImportFromGitHubButton"]')
.waitForElementVisible('*[data-id="homeTabModalDialogModalTitle-react"]') .waitForElementVisible('*[data-id="fileSystemModalDialogModalTitle-react"]')
.assert.containsText('*[data-id="homeTabModalDialogModalTitle-react"]', 'Import from GitHub') .assert.containsText('*[data-id="fileSystemModalDialogModalTitle-react"]', 'Clone Git Repository')
.waitForElementVisible('*[data-id="homeTabModalDialogModalBody-react"]') .waitForElementVisible('*[data-id="fileSystemModalDialogModalBody-react"]')
.assert.containsText('*[data-id="homeTabModalDialogModalBody-react"]', 'Enter the github URL you would like to load.') .waitForElementVisible('input[data-id="modalDialogCustomPromptTextClone"]')
.waitForElementVisible('input[data-id="homeTabModalDialogCustomPromptText"]')
}, },
'Display Error Message For Invalid GitHub URL Modal #group1': function (browser: NightwatchBrowser) { 'Display Error Message For Invalid GitHub URL Modal #group1': function (browser: NightwatchBrowser) {
browser browser
.execute(() => { .execute(() => {
(document.querySelector('input[data-id="homeTabModalDialogCustomPromptText"]') as any).focus() (document.querySelector('input[data-id="modalDialogCustomPromptTextClone"]') as any).focus()
}, [], () => { }) }, [], () => { })
.setValue('input[data-id="homeTabModalDialogCustomPromptText"]', testData.invalidURL) .setValue('input[data-id="modalDialogCustomPromptTextClone"]', testData.invalidURL)
.waitForElementVisible('*[data-id="homeTab-modal-footer-ok-react"]') .waitForElementVisible('*[data-id="fileSystemModalDialogModalFooter-react"]')
.click('[data-id="homeTab-modal-footer-ok-react"]') // submitted .click('[data-id="fileSystem-modal-footer-ok-react"]') // submitted
//.waitForElementVisible('*[data-shared="tooltipPopup"]') //.waitForElementVisible('*[data-shared="tooltipPopup"]')
//.waitForElementContainsText('*[data-shared="tooltipPopup"] span', 'not found ' + testData.invalidURL) //.waitForElementContainsText('*[data-shared="tooltipPopup"] span', 'not found ' + testData.invalidURL)
}, },
'Import From GitHub For Valid URL #group2': function (browser: NightwatchBrowser) { 'Clone From GitHub with Valid URL #group2': function (browser: NightwatchBrowser) {
browser browser
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000) .waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000)
.clickLaunchIcon('filePanel') .clickLaunchIcon('filePanel')
.click('div[data-id="verticalIconsHomeIcon"]') .click('div[data-id="verticalIconsHomeIcon"]')
.waitForElementVisible('button[data-id="landingPageImportFromGitHubButton"]').pause(1000) .waitForElementVisible('button[data-id="landingPageImportFromGitHubButton"]').pause(1000)
.click('button[data-id="landingPageImportFromGitHubButton"]') .click('button[data-id="landingPageImportFromGitHubButton"]')
.waitForElementVisible('input[data-id="homeTabModalDialogCustomPromptText"]') .waitForElementVisible('input[data-id="modalDialogCustomPromptTextClone"]')
.execute(() => { .execute(() => {
(document.querySelector('input[data-id="homeTabModalDialogCustomPromptText"]') as any).focus() (document.querySelector('input[data-id="modalDialogCustomPromptTextClone"]') as any).focus()
}, [], () => { }) }, [], () => { })
.clearValue('input[data-id="homeTabModalDialogCustomPromptText"]').pause(1000) .clearValue('input[data-id="modalDialogCustomPromptTextClone"]').pause(1000)
.setValue('input[data-id="homeTabModalDialogCustomPromptText"]', testData.validURL) .setValue('input[data-id="modalDialogCustomPromptTextClone"]', testData.validURL)
.waitForElementVisible('*[data-id="homeTab-modal-footer-ok-react"]') .waitForElementVisible('*[data-id="fileSystem-modal-footer-ok-react"]')
.click('[data-id="homeTab-modal-footer-ok-react"]') .click('[data-id="fileSystem-modal-footer-ok-react"]')
.openFile('github/OpenZeppelin/openzeppelin-solidity/contracts/access/Roles.sol') .openFile('Roles.sol')
.waitForElementVisible({ .waitForElementVisible({
selector: `//*[@data-id='tab-active' and @data-path="default_workspace/github/OpenZeppelin/openzeppelin-solidity/contracts/access/Roles.sol"]`, selector: `//*[@data-id='tab-active' and @data-path="git-hometab-test.git/Roles.sol"]`,
locateStrategy: 'xpath' locateStrategy: 'xpath'
}) })
.getEditorValue((content) => { .getEditorValue((content) => {
browser.assert.ok(content.indexOf('library Roles {') !== -1, 'content does contain "library Roles {"') browser.assert.ok(content.indexOf('library Roles {') !== -1, 'content does contain "library Roles {"')
}) })
}, },
'Import JSON From GitHub For Valid URL #group2': function (browser: NightwatchBrowser) { 'Confirm JSON After Cloning From GitHub For Valid URL #group2': function (browser: NightwatchBrowser) {
browser browser
.click('div[data-id="verticalIconsHomeIcon"]') .click('div[data-id="verticalIconsHomeIcon"]')
.click('button[data-id="landingPageImportFromGitHubButton"]') .openFile('package.json')
.waitForElementVisible('input[data-id="homeTabModalDialogCustomPromptText"]').pause(1000) .waitForElementVisible("*[data-path='git-hometab-test.git/package.json'")
.execute(() => {
(document.querySelector('input[data-id="homeTabModalDialogCustomPromptText"]') as any).focus()
}, [], () => { })
.clearValue('input[data-id="homeTabModalDialogCustomPromptText"]').pause(1000)
.setValue('input[data-id="homeTabModalDialogCustomPromptText"]', testData.JSON)
.waitForElementVisible('*[data-id="homeTab-modal-footer-ok-react"]')
.click('[data-id="homeTab-modal-footer-ok-react"]')
.openFile('github/ethereum/remix-project/package.json')
.waitForElementVisible("div[data-path='default_workspace/github/ethereum/remix-project/package.json'")
.getEditorValue((content) => { .getEditorValue((content) => {
browser.assert.ok(content.indexOf('"name": "remix-project",') !== -1, 'content does contain "name": "remix-project"') browser.assert.ok(content.indexOf('"name": "remix-project",') !== -1, 'content does contain "name": "remix-project"')
}) })

@ -3,13 +3,8 @@ import { NightwatchBrowser } from 'nightwatch'
import init from '../helpers/init' import init from '../helpers/init'
import { join } from 'path' import { join } from 'path'
import { ChildProcess, spawn } from 'child_process' import { ChildProcess, spawn } from 'child_process'
import { writeFileSync } from 'fs' import { homedir } from 'os'
import * as hardhatCompilation from '../helpers/hardhat_compilation_7839ba878952cc00ff316061405f273a.json'
import * as hardhat_compilation_Lock_dbg from '../helpers/hardhat_compilation_Lock.dbg.json'
import * as hardhat_compilation_Lock from '../helpers/hardhat_compilation_Lock.json'
import * as foundryCompilation from '../helpers/foundry_compilation.json'
import * as truffle_compilation from '../helpers/truffle_compilation.json'
import kill from 'tree-kill' import kill from 'tree-kill'
let remixd: ChildProcess let remixd: ChildProcess
@ -71,9 +66,13 @@ module.exports = {
'@sources': function () { '@sources': function () {
return sources return sources
}, },
'run Remixd tests #group4': function (browser) { 'run Remixd tests #group1': function (browser) {
browser.perform(async (done) => { browser.perform(async (done) => {
try {
remixd = await spawnRemixd(join(process.cwd(), '/apps/remix-ide', '/contracts')) remixd = await spawnRemixd(join(process.cwd(), '/apps/remix-ide', '/contracts'))
} catch (err) {
console.error(err)
}
console.log('working directory', process.cwd()) console.log('working directory', process.cwd())
connectRemixd(browser, done) connectRemixd(browser, done)
}) })
@ -81,10 +80,10 @@ module.exports = {
runTests(browser, done) runTests(browser, done)
}) })
}, },
'Import from node_modules #group1': function (browser) { 'Import from node_modules #group2': function (browser) {
/* /*
when a relative import is used (i.e import "openzeppelin-solidity/contracts/math/SafeMath.sol") when a relative import is used (i.e import "openzeppelin-solidity/contracts/math/SafeMath.sol")
remix (as well as truffle) try to resolve it against the node_modules and installed_contracts folder. remix try to resolve it against the node_modules and installed_contracts folder.
*/ */
browser.perform(async (done) => { browser.perform(async (done) => {
remixd = await spawnRemixd(join(process.cwd(), '/apps/remix-ide', '/contracts')) remixd = await spawnRemixd(join(process.cwd(), '/apps/remix-ide', '/contracts'))
@ -96,7 +95,7 @@ module.exports = {
.setSolidityCompilerVersion('soljson-v0.5.0+commit.1d4f565a.js') .setSolidityCompilerVersion('soljson-v0.5.0+commit.1d4f565a.js')
.testContracts('test_import_node_modules.sol', sources[3]['test_import_node_modules.sol'], ['SafeMath']) .testContracts('test_import_node_modules.sol', sources[3]['test_import_node_modules.sol'], ['SafeMath'])
}, },
'Import from node_modules and reference a github import #group2': function (browser) { 'Import from node_modules and reference a github import #group3': function (browser) {
browser.perform(async (done) => { browser.perform(async (done) => {
remixd = await spawnRemixd(join(process.cwd(), '/apps/remix-ide', '/contracts')) remixd = await spawnRemixd(join(process.cwd(), '/apps/remix-ide', '/contracts'))
console.log('working directory', process.cwd()) console.log('working directory', process.cwd())
@ -107,77 +106,52 @@ module.exports = {
.setSolidityCompilerVersion('soljson-v0.8.20+commit.a1b79de6.js') // open-zeppelin moved to pragma ^0.8.20 .setSolidityCompilerVersion('soljson-v0.8.20+commit.a1b79de6.js') // open-zeppelin moved to pragma ^0.8.20
.testContracts('test_import_node_modules_with_github_import.sol', sources[4]['test_import_node_modules_with_github_import.sol'], ['ERC20', 'test11']) .testContracts('test_import_node_modules_with_github_import.sol', sources[4]['test_import_node_modules_with_github_import.sol'], ['ERC20', 'test11'])
}, },
'Static Analysis run with remixd #group3': '' + function (browser) {
browser.testContracts('test_static_analysis_with_remixd_and_hardhat.sol', sources[5]['test_static_analysis_with_remixd_and_hardhat.sol'], ['test5']).pause(2000)
.clickLaunchIcon('solidityStaticAnalysis')
/*
.click('#staticanalysisButton button').pause(4000)
.waitForElementPresent('#staticanalysisresult .warning', 2000, true, function () {
browser
.waitForElementVisible('[data-id="staticAnalysisModuleMiscellaneous1Button"]')
.click('[data-id="staticAnalysisModuleMiscellaneous1Button"]')
.waitForElementVisible('.highlightLine16', 60000)
.getEditorValue((content) => {
browser.assert.ok(content.indexOf(
'function _sendLogPayload(bytes memory payload) private view {') !== -1,
'code has not been loaded')
})
})
*/
},
'Run git status': '' + function (browser) { 'Should setup a hardhat project #group4': function (browser: NightwatchBrowser) {
browser browser.perform(async (done) => {
.executeScriptInTerminal('git status') await setupHardhatProject()
.pause(3000) done()
.journalLastChildIncludes('On branch ') })
},
'Close Remixd #group3': '' + function (browser) {
browser
.clickLaunchIcon('pluginManager')
.scrollAndClick('#pluginManager *[data-id="pluginManagerComponentDeactivateButtonremixd"]')
}, },
'Should listen on compilation result from hardhat #group5': function (browser: NightwatchBrowser) { 'Should listen on compilation result from hardhat #group4': function (browser: NightwatchBrowser) {
browser.perform(async (done) => { browser.perform(async (done) => {
remixd = await spawnRemixd(join(process.cwd(), '/apps/remix-ide', '/contracts/hardhat')) remixd = await spawnRemixd(join(process.cwd(), '/apps/remix-ide/hardhat-boilerplate'))
console.log('working directory', process.cwd()) console.log('working directory', process.cwd())
connectRemixd(browser, done) connectRemixd(browser, done)
}) })
.perform((done) => { .perform(async (done) => {
console.log('generating compilation result') console.log('generating compilation result')
writeFileSync('./apps/remix-ide/contracts/hardhat/artifacts/build-info/7839ba878952cc00ff316061405f273a.json', JSON.stringify(hardhatCompilation)) await compileHardhatProject()
writeFileSync('./apps/remix-ide/contracts/hardhat/artifacts/contracts/Lock.sol/Lock.json', JSON.stringify(hardhat_compilation_Lock))
writeFileSync('./apps/remix-ide/contracts/hardhat/artifacts/contracts/Lock.sol/Lock.dbg.json', JSON.stringify(hardhat_compilation_Lock_dbg))
done() done()
}) })
.expect.element('*[data-id="terminalJournal"]').text.to.contain('receiving compilation result from Hardhat').before(60000) .expect.element('*[data-id="terminalJournal"]').text.to.contain('receiving compilation result from Hardhat').before(60000)
let addressRef
browser.clickLaunchIcon('filePanel') browser.clickLaunchIcon('filePanel')
.openFile('contracts') .openFile('contracts')
.openFile('contracts/Lock.sol') .openFile('contracts/Token.sol')
.clickLaunchIcon('udapp') .clickLaunchIcon('udapp')
.selectContract('Lock') .selectAccount('0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c')
.createContract('1') .selectContract('Token')
.expect.element('*[data-id="terminalJournal"]').text.to.contain('Unlock time should be in the future').before(60000) .createContract('')
.clickInstance(0)
.clickFunction('balanceOf - call', { types: 'address account', values: '0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c' })
.getAddressAtPosition(0, (address) => {
addressRef = address
})
.perform((done) => {
browser.verifyCallReturnValue(addressRef, ['0:uint256: 1000000'])
.perform(() => done())
})
}, },
'Should load compilation result from hardhat when remixd connects #group6': function (browser: NightwatchBrowser) { 'Should load compilation result from hardhat when remixd connects #group4': function (browser: NightwatchBrowser) {
// artifacts/build-info/c7062fdd360381a85af23eeef31c98f8.json has already been created let addressRef
browser browser
.perform((done) => { .refresh()
writeFileSync('./apps/remix-ide/contracts/hardhat/artifacts/contracts/Lock.sol/Lock.dbg.json', JSON.stringify(hardhat_compilation_Lock_dbg))
writeFileSync('./apps/remix-ide/contracts/hardhat/artifacts/contracts/Lock.sol/Lock.json', JSON.stringify(hardhat_compilation_Lock))
writeFileSync('./apps/remix-ide/contracts/hardhat/artifacts/build-info/7839ba878952cc00ff316061405f273a.json', JSON.stringify(hardhatCompilation))
done()
})
.perform(async (done) => { .perform(async (done) => {
remixd = await spawnRemixd(join(process.cwd(), '/apps/remix-ide', '/contracts/hardhat'))
console.log('working directory', process.cwd()) console.log('working directory', process.cwd())
connectRemixd(browser, done) connectRemixd(browser, done)
}) })
@ -185,24 +159,40 @@ module.exports = {
browser.clickLaunchIcon('filePanel') browser.clickLaunchIcon('filePanel')
.openFile('contracts') .openFile('contracts')
.openFile('contracts/Lock.sol') .openFile('contracts/Token.sol')
.clickLaunchIcon('udapp') .clickLaunchIcon('udapp')
.selectContract('Lock') .selectAccount('0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c')
.createContract('1') .selectContract('Token')
.expect.element('*[data-id="terminalJournal"]').text.to.contain('Unlock time should be in the future').before(60000) .createContract('')
.clickInstance(0)
.clickFunction('balanceOf - call', { types: 'address account', values: '0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c' })
.getAddressAtPosition(0, (address) => {
addressRef = address
})
.perform((done) => {
browser.verifyCallReturnValue(addressRef, ['0:uint256: 1000000'])
.perform(() => done())
})
},
'Should install foundry #group5': function (browser: NightwatchBrowser) {
browser.perform(async (done) => {
await downloadFoundry()
await installFoundry()
await initFoundryProject()
done()
})
}, },
'Should listen on compilation result from foundry #group7': function (browser: NightwatchBrowser) { 'Should listen on compilation result from foundry #group5': function (browser: NightwatchBrowser) {
browser.perform(async (done) => { browser.perform(async (done) => {
remixd = await spawnRemixd(join(process.cwd(), '/apps/remix-ide', '/contracts/foundry')) console.log('working directory', homedir() + '/foundry_tmp/hello_foundry')
console.log('working directory', process.cwd()) remixd = await spawnRemixd(join(homedir(), '/foundry_tmp/hello_foundry'))
connectRemixd(browser, done) connectRemixd(browser, done)
}) })
.perform((done) => { .perform(async (done) => {
writeFileSync('./apps/remix-ide/contracts/foundry/out/Counter.sol/Counter.json', JSON.stringify(foundryCompilation)) await buildFoundryProject()
done() done()
}) })
.expect.element('*[data-id="terminalJournal"]').text.to.contain('receiving compilation result from Foundry').before(60000) .expect.element('*[data-id="terminalJournal"]').text.to.contain('receiving compilation result from Foundry').before(60000)
@ -225,35 +215,69 @@ module.exports = {
done() done()
}) })
}) })
}, },
'Should listen on compilation result from truffle #group8': function (browser: NightwatchBrowser) { 'Should load compilation result from hardhat when remixd connects #group5': function (browser: NightwatchBrowser) {
browser.perform(async (done) => { browser.refresh().perform(async (done) => {
remixd = await spawnRemixd(join(process.cwd(), '/apps/remix-ide', '/contracts/truffle')) console.log('working directory', homedir() + '/foundry_tmp/hello_foundry')
console.log('working directory', process.cwd())
connectRemixd(browser, done) connectRemixd(browser, done)
}) })
.perform((done) => { .expect.element('*[data-id="terminalJournal"]').text.to.contain('receiving compilation result from Foundry').before(60000)
writeFileSync('./apps/remix-ide/contracts/truffle/build/contracts/Migrations.json', JSON.stringify(truffle_compilation))
done()
})
.expect.element('*[data-id="terminalJournal"]').text.to.contain('receiving compilation result from Truffle').before(60000)
let contractAaddress
browser.clickLaunchIcon('filePanel') browser.clickLaunchIcon('filePanel')
.openFile('contracts') .openFile('src')
.openFile('contracts/Migrations.sol') .openFile('src/Counter.sol')
.clickLaunchIcon('udapp') .clickLaunchIcon('udapp')
.selectContract('Migrations') .selectContract('Counter')
.createContract('') .createContract('')
.testFunction('last', .getAddressAtPosition(0, (address) => {
{ console.log(contractAaddress)
status: '0x1 Transaction mined and execution succeed' contractAaddress = address
}) })
.clickInstance(0)
.clickFunction('increment - transact (not payable)')
.perform((done) => {
browser.testConstantFunction(contractAaddress, 'number - call', null, '0:\nuint256: 1').perform(() => {
done()
})
})
},
'Should install slither #group6': function (browser: NightwatchBrowser) {
browser.perform(async (done) => {
await installSlither()
done()
})
},
'Should perform slither analysis #group6': function (browser: NightwatchBrowser) {
browser.perform(async (done) => {
try {
remixd = await spawnRemixd(join(process.cwd(), '/apps/remix-ide', '/contracts'))
} catch (err) {
console.error(err)
}
console.log('working directory', process.cwd())
connectRemixd(browser, done)
})
.openFile('ballot.sol')
.pause(2000)
.clickLaunchIcon('solidityStaticAnalysis')
.useXpath()
.click('//*[@id="staticAnalysisRunBtn"]')
.waitForElementPresent('//*[@id="staticanalysisresult"]', 5000)
.waitForElementVisible({
selector: "//*[@data-id='nolibslitherwarnings'][contains(text(), '3')]",
locateStrategy: 'xpath',
timeout: 5000
})
.waitForElementVisible({
selector: "//div[@data-id='block']/span[contains(text(), '3 warnings found.')]",
locateStrategy: 'xpath',
timeout: 5000
})
} }
} }
@ -306,10 +330,34 @@ function testImportFromRemixd(browser: NightwatchBrowser, callback: VoidFunction
.perform(() => { callback() }) .perform(() => { callback() })
} }
async function installRemixd(): Promise<void> {
console.log('installRemixd')
const remixd = spawn('yarn install', [], { cwd: process.cwd() + '/dist/libs/remixd', shell: true, detached: true })
return new Promise((resolve, reject) => {
remixd.stdout.on('data', function (data) {
console.log(data.toString())
if(
data.toString().includes('success Saved lockfile')
|| data.toString().includes('success Already up-to-date')
) {
resolve()
}
})
remixd.stderr.on('err', function (data) {
console.log(data.toString())
reject(data.toString())
})
})
}
async function spawnRemixd(path: string): Promise<ChildProcess> { async function spawnRemixd(path: string): Promise<ChildProcess> {
console.log('spawnRemixd', path)
await installRemixd()
const remixd = spawn('chmod +x dist/libs/remixd/src/bin/remixd.js && dist/libs/remixd/src/bin/remixd.js --remix-ide http://127.0.0.1:8080', [`-s ${path}`], { cwd: process.cwd(), shell: true, detached: true }) const remixd = spawn('chmod +x dist/libs/remixd/src/bin/remixd.js && dist/libs/remixd/src/bin/remixd.js --remix-ide http://127.0.0.1:8080', [`-s ${path}`], { cwd: process.cwd(), shell: true, detached: true })
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
remixd.stdout.on('data', function (data) { remixd.stdout.on('data', function (data) {
console.log(data.toString())
if( if(
data.toString().includes('is listening') data.toString().includes('is listening')
|| data.toString().includes('There is already a client running') || data.toString().includes('There is already a client running')
@ -319,6 +367,7 @@ async function spawnRemixd(path: string): Promise<ChildProcess> {
} }
}) })
remixd.stderr.on('err', function (data) { remixd.stderr.on('err', function (data) {
console.log(data.toString())
reject(data.toString()) reject(data.toString())
}) })
}) })
@ -346,3 +395,144 @@ function connectRemixd(browser: NightwatchBrowser, done: any) {
.perform(() => done()) .perform(() => done())
} }
async function setupHardhatProject(): Promise<void> {
console.log(process.cwd())
try {
const server = spawn('git clone https://github.com/NomicFoundation/hardhat-boilerplate && cd hardhat-boilerplate && yarn install && yarn add "@typechain/ethers-v5@^10.1.0" && yarn add "@typechain/hardhat@^6.1.2" && yarn add "typechain@^8.1.0" && echo "END"', [], { cwd: process.cwd() + '/apps/remix-ide', shell: true, detached: true })
return new Promise((resolve, reject) => {
server.on('exit', function (exitCode) {
console.log("Child exited with code: " + exitCode);
console.log('end')
resolve()
})
})
} catch (e) {
console.log(e)
}
}
async function compileHardhatProject(): Promise<void> {
console.log(process.cwd())
try {
const server = spawn('npx hardhat compile', [], { cwd: process.cwd() + '/apps/remix-ide/hardhat-boilerplate', shell: true, detached: true })
return new Promise((resolve, reject) => {
server.on('exit', function (exitCode) {
console.log("Child exited with code: " + exitCode);
console.log('end')
resolve()
})
})
} catch (e) {
console.log(e)
}
}
async function downloadFoundry(): Promise<void> {
console.log('downloadFoundry', process.cwd())
try {
const server = spawn('curl -L https://foundry.paradigm.xyz | bash', [], { cwd: process.cwd(), shell: true, detached: true })
return new Promise((resolve, reject) => {
server.stdout.on('data', function (data) {
console.log(data.toString())
if (
data.toString().includes("simply run 'foundryup' to install Foundry")
|| data.toString().includes("foundryup: could not detect shell, manually add")
) {
console.log('resolving')
resolve()
}
})
server.stderr.on('err', function (data) {
console.log(data.toString())
reject(data.toString())
})
})
} catch (e) {
console.log(e)
}
}
async function installFoundry(): Promise<void> {
console.log('installFoundry', process.cwd())
try {
const server = spawn('export PATH="' + homedir() + '/.foundry/bin:$PATH" && foundryup', [], { cwd: process.cwd(), shell: true, detached: true })
return new Promise((resolve, reject) => {
server.stdout.on('data', function (data) {
console.log(data.toString())
if (
data.toString().includes("foundryup: done!")
) {
console.log('resolving')
resolve()
}
})
server.stderr.on('err', function (data) {
console.log(data.toString())
reject(data.toString())
})
})
} catch (e) {
console.log(e)
}
}
async function initFoundryProject(): Promise<void> {
console.log('initFoundryProject', homedir())
try {
spawn('git config --global user.email \"you@example.com\"', [], { cwd: homedir(), shell: true, detached: true })
spawn('git config --global user.name \"Your Name\"', [], { cwd: homedir(), shell: true, detached: true })
spawn('mkdir foundry_tmp', [], { cwd: homedir(), shell: true, detached: true })
const server = spawn('export PATH="' + homedir() + '/.foundry/bin:$PATH" && forge init hello_foundry', [], { cwd: homedir() + '/foundry_tmp', shell: true, detached: true })
server.stdout.pipe(process.stdout)
return new Promise((resolve, reject) => {
server.on('exit', function (exitCode) {
console.log("Child exited with code: " + exitCode);
console.log('end')
resolve()
})
})
} catch (e) {
console.log(e)
}
}
async function buildFoundryProject(): Promise<void> {
console.log('buildFoundryProject', homedir())
try {
const server = spawn('export PATH="' + homedir() + '/.foundry/bin:$PATH" && forge build', [], { cwd: homedir() + '/foundry_tmp/hello_foundry', shell: true, detached: true })
server.stdout.pipe(process.stdout)
return new Promise((resolve, reject) => {
server.on('exit', function (exitCode) {
console.log("Child exited with code: " + exitCode);
console.log('end')
resolve()
})
})
} catch (e) {
console.log(e)
}
}
async function installSlither(): Promise<void> {
console.log('installSlither', process.cwd())
try {
const server = spawn('node', ['./dist/libs/remixd/src/scripts/installSlither.js'], { cwd: process.cwd(), shell: true, detached: true })
return new Promise((resolve, reject) => {
server.stdout.on('data', function (data) {
console.log(data.toString())
if (
data.toString().includes("Slither is ready to use")
) {
console.log('resolving')
resolve()
}
})
server.stderr.on('err', function (data) {
console.log(data.toString())
reject(data.toString())
})
})
} catch (e) {
console.log(e)
}
}

@ -2,10 +2,6 @@
import { NightwatchBrowser } from 'nightwatch' import { NightwatchBrowser } from 'nightwatch'
import init from '../helpers/init' import init from '../helpers/init'
const branch = process.env.CIRCLE_BRANCH;
const isMasterBranch = branch === 'master';
const runMasterTests: boolean = (branch ? (isMasterBranch ? true : false) : true)
module.exports = { module.exports = {
'@disabled': true, '@disabled': true,
before: function (browser: NightwatchBrowser, done: VoidFunction) { before: function (browser: NightwatchBrowser, done: VoidFunction) {
@ -213,7 +209,6 @@ module.exports = {
}, },
'Should run a script which log transaction and block using web3.js and ethers #group7': function (browser: NightwatchBrowser) { 'Should run a script which log transaction and block using web3.js and ethers #group7': function (browser: NightwatchBrowser) {
if(runMasterTests){
browser browser
.clickLaunchIcon('udapp') .clickLaunchIcon('udapp')
.switchEnvironment('basic-http-provider') .switchEnvironment('basic-http-provider')
@ -234,7 +229,6 @@ module.exports = {
.waitForElementContainsText('*[data-id="terminalJournal"]', '0x0fbbd94c448fe6949f848380a1d145a974f386624b4b10aa40f9afb212b3ddeb', 120000) // hash of 4757766 .waitForElementContainsText('*[data-id="terminalJournal"]', '0x0fbbd94c448fe6949f848380a1d145a974f386624b4b10aa40f9afb212b3ddeb', 120000) // hash of 4757766
// check if the logsBloom is being logged (ethers.js call) // check if the logsBloom is being logged (ethers.js call)
.waitForElementContainsText('*[data-id="terminalJournal"]', '0x9db899cb75888a630ba50a1644c243b83d2eb38525eb828a06a5e8bb5663c0b0', 120000) // hash of 4757767 .waitForElementContainsText('*[data-id="terminalJournal"]', '0x9db899cb75888a630ba50a1644c243b83d2eb38525eb828a06a5e8bb5663c0b0', 120000) // hash of 4757767
}
}, },
'Should listen on all transactions #group8': function (browser: NightwatchBrowser) { 'Should listen on all transactions #group8': function (browser: NightwatchBrowser) {
@ -297,7 +291,6 @@ module.exports = {
}, },
'Should connect to mainnet fork and run web3.eth.getCode in the terminal #group9': function (browser: NightwatchBrowser) { 'Should connect to mainnet fork and run web3.eth.getCode in the terminal #group9': function (browser: NightwatchBrowser) {
if (runMasterTests) {
browser browser
.clickLaunchIcon('udapp') .clickLaunchIcon('udapp')
.switchEnvironment('vm-mainnet-fork') .switchEnvironment('vm-mainnet-fork')
@ -309,11 +302,9 @@ module.exports = {
.executeScriptInTerminal(`web3.eth.getCode('0x180587b00c8642e2c7ac3a758712d97e6f7bdcc7')`) // mainnet contract .executeScriptInTerminal(`web3.eth.getCode('0x180587b00c8642e2c7ac3a758712d97e6f7bdcc7')`) // mainnet contract
.waitForElementContainsText('*[data-id="terminalJournal"]', '0x608060405260043610601f5760003560e01c80635c60da1b14603157602b565b36602b576029605f565b005b6029605f565b348015603c57600080fd5b5060436097565b6040516001600160a01b03909116815260200160405180910390f35b609560917f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b60d1565b565b600060c97f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b905090565b90565b3660008037600080366000845af43d6000803e80801560ef573d6000f35b3d6000fdfea2646970667358221220969dbb4b1d8aec2bb348e26488dc1a33b6bcf0190f567d161312ab7ca9193d8d64736f6c63430008110033', 120000) .waitForElementContainsText('*[data-id="terminalJournal"]', '0x608060405260043610601f5760003560e01c80635c60da1b14603157602b565b36602b576029605f565b005b6029605f565b348015603c57600080fd5b5060436097565b6040516001600160a01b03909116815260200160405180910390f35b609560917f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b60d1565b565b600060c97f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b905090565b90565b3660008037600080366000845af43d6000803e80801560ef573d6000f35b3d6000fdfea2646970667358221220969dbb4b1d8aec2bb348e26488dc1a33b6bcf0190f567d161312ab7ca9193d8d64736f6c63430008110033', 120000)
.click('*[data-id="terminalClearConsole"]') .click('*[data-id="terminalClearConsole"]')
}
}, },
'Should connect to the sepolia fork and run web3.eth.getCode in the terminal #group9': function (browser: NightwatchBrowser) { 'Should connect to the sepolia fork and run web3.eth.getCode in the terminal #group9': function (browser: NightwatchBrowser) {
if (runMasterTests) {
browser browser
.switchEnvironment('vm-custom-fork') .switchEnvironment('vm-custom-fork')
.waitForElementVisible('[data-id="vm-custom-fork-modal-footer-ok-react"]') .waitForElementVisible('[data-id="vm-custom-fork-modal-footer-ok-react"]')
@ -340,7 +331,6 @@ module.exports = {
.executeScriptInTerminal(`web3.eth.getCode('0x75F509A4eDA030470272DfBAf99A47D587E76709')`) // sepolia contract .executeScriptInTerminal(`web3.eth.getCode('0x75F509A4eDA030470272DfBAf99A47D587E76709')`) // sepolia contract
.waitForElementContainsText('*[data-id="terminalJournal"]', byteCodeInSepolia, 120000) .waitForElementContainsText('*[data-id="terminalJournal"]', byteCodeInSepolia, 120000)
.click('*[data-id="terminalClearConsole"]') .click('*[data-id="terminalClearConsole"]')
}
}, },
'Should run a free function while being connected to mainnet #group9': function (browser: NightwatchBrowser) { 'Should run a free function while being connected to mainnet #group9': function (browser: NightwatchBrowser) {
@ -364,7 +354,6 @@ module.exports = {
console.log(resolver.addr(node)); console.log(resolver.addr(node));
} }
` `
if (runMasterTests) {
browser browser
// .clickLaunchIcon('udapp') // .clickLaunchIcon('udapp')
.switchEnvironment('vm-mainnet-fork') .switchEnvironment('vm-mainnet-fork')
@ -388,7 +377,6 @@ module.exports = {
}) })
.useCss() .useCss()
.waitForElementContainsText('*[data-id="terminalJournal"]', '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', 120000) .waitForElementContainsText('*[data-id="terminalJournal"]', '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', 120000)
}
}, },
'Should run free function which logs in the terminal #group10': function (browser: NightwatchBrowser) { 'Should run free function which logs in the terminal #group10': function (browser: NightwatchBrowser) {

@ -117,7 +117,7 @@ module.exports = {
}) })
}, },
'Should load Blockscout verified contracts from URL "address" and "blockscout" params (single source)': function (browser: NightwatchBrowser) { 'Should load Blockscout verified contracts from URL "address" and "blockscout" params (single source)': ''+function (browser: NightwatchBrowser) {
browser browser
.url('http://127.0.0.1:8080/#address=0xdAC17F958D2ee523a2206206994597C13D831ec7&blockscout=eth.blockscout.com') .url('http://127.0.0.1:8080/#address=0xdAC17F958D2ee523a2206206994597C13D831ec7&blockscout=eth.blockscout.com')
.refreshPage() .refreshPage()
@ -175,6 +175,7 @@ module.exports = {
'code has been loaded') 'code has been loaded')
}) })
.url('http://127.0.0.1:8080') // refresh without loading the code sample .url('http://127.0.0.1:8080') // refresh without loading the code sample
.pause(2000)
.currentWorkspaceIs('default_workspace') .currentWorkspaceIs('default_workspace')
.execute(() => { .execute(() => {
return document.querySelector('[data-id="dropdown-item-code-sample"]') === null return document.querySelector('[data-id="dropdown-item-code-sample"]') === null

@ -27,30 +27,30 @@ module.exports = {
.frameParent() .frameParent()
.clickLaunchIcon('filePanel') .clickLaunchIcon('filePanel')
.waitForElementVisible({ .waitForElementVisible({
selector: "//*[@data-id='workspacesSelect' and contains(.,'snekmate')]", selector: "//*[@data-id='workspacesSelect' and contains(.,'vyper-lang')]",
locateStrategy: 'xpath', locateStrategy: 'xpath',
timeout: 60000 timeout: 60000
}) })
.currentWorkspaceIs('snekmate') .currentWorkspaceIs('vyper-lang')
.waitForElementVisible({ // .waitForElementVisible({
selector: "//*[@data-id='treeViewLitreeViewItemsrc' and contains(.,'src')]", // selector: "//*[@data-id='treeViewLitreeViewItemsrc' and contains(.,'src')]",
locateStrategy: 'xpath', // locateStrategy: 'xpath',
timeout: 60000 // timeout: 60000
}) // })
.openFile('src') .openFile('examples')
.openFile('src/snekmate') .openFile('examples/auctions')
.openFile('src/snekmate/tokens') .openFile('examples/auctions/simple_open_auction.vy')
.openFile('src/snekmate/tokens/ERC721.vy')
}, },
// 'Add vyper file to run tests #group1': function (browser: NightwatchBrowser) {
// browser.addFile('TestBallot.sol', sources[0]['TestBallot.sol']) // '@sources': () => sources,
// }, // 'Context menu click to compile blind_auction should succeed #group1': function (browser: NightwatchBrowser) {
'@sources': () => sources, // browser
// .addFileSnekmate('blind_auction.vy', sources[0]['blindAuction'])
'Context menu click to compile blind_auction should succeed #group1': function (browser: NightwatchBrowser) { 'Context menu click to compile blind_auction should succeed #group1': function (browser: NightwatchBrowser) {
browser browser
.addFileSnekmate('blind_auction.vy', sources[0]['blindAuction']) .click('*[data-id="treeViewDivtreeViewItemexamples/auctions/blind_auction.vy"]')
.click('*[data-id="treeViewLitreeViewItemblind_auction.vy"]') .rightClick('*[data-id="treeViewDivtreeViewItemexamples/auctions/blind_auction.vy"]')
.rightClick('*[data-id="treeViewLitreeViewItemblind_auction.vy"]')
.waitForElementPresent('[data-id="contextMenuItemvyper"]') .waitForElementPresent('[data-id="contextMenuItemvyper"]')
.click('[data-id="contextMenuItemvyper"]') .click('[data-id="contextMenuItemvyper"]')
.clickLaunchIcon('vyper') .clickLaunchIcon('vyper')
@ -149,32 +149,32 @@ module.exports = {
}) })
}, },
'Compile Ownable contract from snekmate #group1': function (browser: NightwatchBrowser) { // 'Compile Ownable contract from snekmate #group1': function (browser: NightwatchBrowser) {
let contractAddress // let contractAddress
browser // browser
.frameParent() // .frameParent()
.clickLaunchIcon('filePanel') // .clickLaunchIcon('filePanel')
.switchWorkspace('snekmate') // .switchWorkspace('snekmate')
.openFile('src') // .openFile('src')
.openFile('src/snekmate') // .openFile('src/snekmate')
.openFile('src/snekmate/auth') // .openFile('src/snekmate/auth')
.openFile('src/snekmate/auth/Ownable.vy') // .openFile('src/snekmate/auth/Ownable.vy')
.rightClick('*[data-id="treeViewLitreeViewItemsrc/snekmate/auth/Ownable.vy"]') // .rightClick('*[data-id="treeViewLitreeViewItemsrc/snekmate/auth/Ownable.vy"]')
.waitForElementVisible('*[data-id="contextMenuItemvyper"]') // .waitForElementVisible('*[data-id="contextMenuItemvyper"]')
.click('*[data-id="contextMenuItemvyper"]') // .click('*[data-id="contextMenuItemvyper"]')
.clickLaunchIcon('vyper') // .clickLaunchIcon('vyper')
// @ts-ignore // // @ts-ignore
.frame(0) // .frame(0)
.click('[data-id="compile"]') // .click('[data-id="compile"]')
.waitForElementVisible({ // .waitForElementVisible({
selector:'[data-id="compilation-details"]', // selector:'[data-id="compilation-details"]',
timeout: 60000 // timeout: 60000
}) // })
.click('[data-id="compilation-details"]') // .click('[data-id="compilation-details"]')
.frameParent() // .frameParent()
.waitForElementVisible('[data-id="copy-abi"]') // .waitForElementVisible('[data-id="copy-abi"]')
.end() // .end()
} // }
} }
const testContract = ` const testContract = `
@ -388,6 +388,6 @@ def auctionEnd():
# Transfer funds to beneficiary # Transfer funds to beneficiary
send(self.beneficiary, self.highestBid) send(self.beneficiary, self.highestBid)
`} ` }
} }
] ]

@ -159,11 +159,13 @@ module.exports = {
.setValue('[data-id="modalDialogCustomPromptTextClone"]', 'https://github.com/ioedeveloper/test-branch-change') .setValue('[data-id="modalDialogCustomPromptTextClone"]', 'https://github.com/ioedeveloper/test-branch-change')
.click('[data-id="fileSystem-modal-footer-ok-react"]') .click('[data-id="fileSystem-modal-footer-ok-react"]')
.waitForElementPresent('.fa-spinner') .waitForElementPresent('.fa-spinner')
.pause(5000) .pause(7000)
.waitForElementNotPresent('.fa-spinner') .waitForElementNotPresent('.fa-spinner')
.waitForElementContainsText('[data-id="workspacesSelect"]', 'test-branch-change') .waitForElementContainsText('[data-id="workspacesSelect"]', 'test-branch-change')
.waitForElementVisible('[data-id="workspaceGitPanel"]') .waitForElementVisible('[data-id="workspaceGitPanel"]')
.waitForElementVisible('[data-id="workspaceGitBranchesDropdown"]')
.click('[data-id="workspaceGitBranchesDropdown"]') .click('[data-id="workspaceGitBranchesDropdown"]')
.pause()
.waitForElementVisible('[data-id="custom-dropdown-menu"]') .waitForElementVisible('[data-id="custom-dropdown-menu"]')
.waitForElementContainsText('[data-id="custom-dropdown-items"]', 'origin/dev') .waitForElementContainsText('[data-id="custom-dropdown-items"]', 'origin/dev')
.waitForElementContainsText('[data-id="custom-dropdown-items"]', 'origin/production') .waitForElementContainsText('[data-id="custom-dropdown-items"]', 'origin/production')
@ -312,6 +314,7 @@ module.exports = {
'When switching branches the submodules should disappear #group4': function (browser: NightwatchBrowser) { 'When switching branches the submodules should disappear #group4': function (browser: NightwatchBrowser) {
browser browser
.waitForElementVisible('[data-id="workspaceGitBranchesDropdown"]') .waitForElementVisible('[data-id="workspaceGitBranchesDropdown"]')
.pause()
.click('[data-id="workspaceGitBranchesDropdown"]') .click('[data-id="workspaceGitBranchesDropdown"]')
.waitForElementVisible('[data-id="custom-dropdown-menu"]') .waitForElementVisible('[data-id="custom-dropdown-menu"]')
.waitForElementContainsText('[data-id="custom-dropdown-items"]', 'origin/empty') .waitForElementContainsText('[data-id="custom-dropdown-items"]', 'origin/empty')
@ -393,11 +396,9 @@ module.exports = {
// GIT WORKSPACE E2E ENDS // GIT WORKSPACE E2E ENDS
tearDown: sauce, tearDown: sauce,
} }
const gitmodules = `[submodule "subdemo3"] const gitmodules = `[submodule "subdemo3"]
path = subdemo3 path = subdemo3
url = https://github.com/bunsenstraat/empty3 url = https://github.com/bunsenstraat/empty3

@ -60,7 +60,7 @@ contract Ballot {
!voters[voter].voted, !voters[voter].voted,
"The voter already voted." "The voter already voted."
); );
require(voters[voter].weight == 0); require(voters[voter].weight == 0, "Voter already has the right to vote.");
voters[voter].weight = 1; voters[voter].weight = 1;
} }
@ -101,6 +101,7 @@ contract Ballot {
Voter storage sender = voters[msg.sender]; Voter storage sender = voters[msg.sender];
require(sender.weight != 0, "Has no right to vote"); require(sender.weight != 0, "Has no right to vote");
require(!sender.voted, "Already voted."); require(!sender.voted, "Already voted.");
require(proposal < proposals.length, "Invalid proposal index.");
sender.voted = true; sender.voted = true;
sender.vote = proposal; sender.vote = proposal;

@ -1,398 +0,0 @@
{
"_format": "ethers-rs-sol-cache-3",
"paths": {
"artifacts": "out",
"build_infos": "out/build-info",
"sources": "src",
"tests": "test",
"scripts": "script",
"libraries": [
"lib"
]
},
"files": {
"lib/forge-std/lib/ds-test/src/test.sol": {
"lastModificationDate": 1661541843388,
"contentHash": "962996f0e05d5218857a538a62d7c47e",
"sourceName": "lib/forge-std/lib/ds-test/src/test.sol",
"solcConfig": {
"settings": {
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"bytecodeHash": "ipfs"
},
"outputSelection": {
"*": {
"": [
"ast"
],
"*": [
"abi",
"evm.bytecode",
"evm.deployedBytecode",
"evm.methodIdentifiers",
"metadata"
]
}
},
"evmVersion": "london",
"libraries": {}
}
},
"imports": [],
"versionRequirement": ">=0.5.0",
"artifacts": {
"DSTest": {
"0.8.16+commit.07a7930e.Linux.gcc": "test.sol/DSTest.json"
}
}
},
"lib/forge-std/src/Script.sol": {
"lastModificationDate": 1661541842048,
"contentHash": "b313d0193442f5a12848be9c422a0064",
"sourceName": "lib/forge-std/src/Script.sol",
"solcConfig": {
"settings": {
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"bytecodeHash": "ipfs"
},
"outputSelection": {
"*": {
"": [
"ast"
],
"*": [
"abi",
"evm.bytecode",
"evm.deployedBytecode",
"evm.methodIdentifiers",
"metadata"
]
}
},
"evmVersion": "london",
"libraries": {}
}
},
"imports": [
"lib/forge-std/src/Vm.sol",
"lib/forge-std/src/console.sol",
"lib/forge-std/src/console2.sol"
],
"versionRequirement": ">=0.6.0, <0.9.0",
"artifacts": {
"Script": {
"0.8.16+commit.07a7930e.Linux.gcc": "Script.sol/Script.json"
}
}
},
"lib/forge-std/src/Test.sol": {
"lastModificationDate": 1661541842048,
"contentHash": "8e1ae731c7bb8023f36077d86d18693f",
"sourceName": "lib/forge-std/src/Test.sol",
"solcConfig": {
"settings": {
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"bytecodeHash": "ipfs"
},
"outputSelection": {
"*": {
"": [
"ast"
],
"*": [
"abi",
"evm.bytecode",
"evm.deployedBytecode",
"evm.methodIdentifiers",
"metadata"
]
}
},
"evmVersion": "london",
"libraries": {}
}
},
"imports": [
"lib/forge-std/lib/ds-test/src/test.sol",
"lib/forge-std/src/Script.sol",
"lib/forge-std/src/Vm.sol",
"lib/forge-std/src/console.sol",
"lib/forge-std/src/console2.sol"
],
"versionRequirement": ">=0.6.0, <0.9.0",
"artifacts": {
"Test": {
"0.8.16+commit.07a7930e.Linux.gcc": "Test.sol/Test.json"
},
"stdError": {
"0.8.16+commit.07a7930e.Linux.gcc": "Test.sol/stdError.json"
},
"stdMath": {
"0.8.16+commit.07a7930e.Linux.gcc": "Test.sol/stdMath.json"
},
"stdStorage": {
"0.8.16+commit.07a7930e.Linux.gcc": "Test.sol/stdStorage.json"
}
}
},
"lib/forge-std/src/Vm.sol": {
"lastModificationDate": 1661541842048,
"contentHash": "225040109969e43ff90255e34aaecc99",
"sourceName": "lib/forge-std/src/Vm.sol",
"solcConfig": {
"settings": {
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"bytecodeHash": "ipfs"
},
"outputSelection": {
"*": {
"": [
"ast"
],
"*": [
"abi",
"evm.bytecode",
"evm.deployedBytecode",
"evm.methodIdentifiers",
"metadata"
]
}
},
"evmVersion": "london",
"libraries": {}
}
},
"imports": [],
"versionRequirement": ">=0.6.0, <0.9.0",
"artifacts": {
"Vm": {
"0.8.16+commit.07a7930e.Linux.gcc": "Vm.sol/Vm.json"
}
}
},
"lib/forge-std/src/console.sol": {
"lastModificationDate": 1663196945880,
"contentHash": "100b8a33b917da1147740d7ab8b0ded3",
"sourceName": "lib/forge-std/src/console.sol",
"solcConfig": {
"settings": {
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"bytecodeHash": "ipfs"
},
"outputSelection": {
"*": {
"": [
"ast"
],
"*": [
"abi",
"evm.bytecode",
"evm.deployedBytecode",
"evm.methodIdentifiers",
"metadata"
]
}
},
"evmVersion": "london",
"libraries": {}
}
},
"imports": [],
"versionRequirement": ">=0.4.22, <0.9.0",
"artifacts": {
"console": {
"0.8.16+commit.07a7930e.Linux.gcc": "console.sol/console.json"
}
}
},
"lib/forge-std/src/console2.sol": {
"lastModificationDate": 1661541842052,
"contentHash": "5df91f8e93efbfcccf68973dc1b74a70",
"sourceName": "lib/forge-std/src/console2.sol",
"solcConfig": {
"settings": {
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"bytecodeHash": "ipfs"
},
"outputSelection": {
"*": {
"": [
"ast"
],
"*": [
"abi",
"evm.bytecode",
"evm.deployedBytecode",
"evm.methodIdentifiers",
"metadata"
]
}
},
"evmVersion": "london",
"libraries": {}
}
},
"imports": [],
"versionRequirement": ">=0.4.22, <0.9.0",
"artifacts": {
"console2": {
"0.8.16+commit.07a7930e.Linux.gcc": "console2.sol/console2.json"
}
}
},
"script/Counter.s.sol": {
"lastModificationDate": 1661541840908,
"contentHash": "0705c52104730a78aef4aa6694175c81",
"sourceName": "script/Counter.s.sol",
"solcConfig": {
"settings": {
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"bytecodeHash": "ipfs"
},
"outputSelection": {
"*": {
"": [
"ast"
],
"*": [
"abi",
"evm.bytecode",
"evm.deployedBytecode",
"evm.methodIdentifiers",
"metadata"
]
}
},
"evmVersion": "london",
"libraries": {}
}
},
"imports": [
"lib/forge-std/src/Script.sol",
"lib/forge-std/src/Vm.sol",
"lib/forge-std/src/console.sol",
"lib/forge-std/src/console2.sol"
],
"versionRequirement": "^0.8.13",
"artifacts": {
"CounterScript": {
"0.8.16+commit.07a7930e.Linux.gcc": "Counter.s.sol/CounterScript.json"
}
}
},
"src/Counter.sol": {
"lastModificationDate": 1664875932853,
"contentHash": "ae6c800a2b4c57768024d6e9423d39e8",
"sourceName": "src/Counter.sol",
"solcConfig": {
"settings": {
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"bytecodeHash": "ipfs"
},
"outputSelection": {
"*": {
"": [
"ast"
],
"*": [
"abi",
"evm.bytecode",
"evm.deployedBytecode",
"evm.methodIdentifiers",
"metadata"
]
}
},
"evmVersion": "london",
"libraries": {}
}
},
"imports": [],
"versionRequirement": "^0.8.13",
"artifacts": {
"Counter": {
"0.8.16+commit.07a7930e.Linux.gcc": "Counter.sol/Counter.json"
}
}
},
"test/Counter.t.sol": {
"lastModificationDate": 1661541840908,
"contentHash": "5122f4f87ee8fbf9a2468a4c9c780b6a",
"sourceName": "test/Counter.t.sol",
"solcConfig": {
"settings": {
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"bytecodeHash": "ipfs"
},
"outputSelection": {
"*": {
"": [
"ast"
],
"*": [
"abi",
"evm.bytecode",
"evm.deployedBytecode",
"evm.methodIdentifiers",
"metadata"
]
}
},
"evmVersion": "london",
"libraries": {}
}
},
"imports": [
"lib/forge-std/lib/ds-test/src/test.sol",
"lib/forge-std/src/Script.sol",
"lib/forge-std/src/Test.sol",
"lib/forge-std/src/Vm.sol",
"lib/forge-std/src/console.sol",
"lib/forge-std/src/console2.sol",
"src/Counter.sol"
],
"versionRequirement": "^0.8.13",
"artifacts": {
"CounterTest": {
"0.8.16+commit.07a7930e.Linux.gcc": "Counter.t.sol/CounterTest.json"
}
}
}
}
}

@ -1,6 +0,0 @@
[profile.default]
src = 'src'
out = 'out'
libs = ['lib']
# See more config options https://github.com/foundry-rs/foundry/tree/master/config

@ -1,385 +0,0 @@
{
"abi": [
{
"inputs": [],
"name": "IS_SCRIPT",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "run",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "setUp",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "vm",
"outputs": [
{
"internalType": "contract Vm",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
}
],
"bytecode": {
"object": "0x60806040526000805460ff1916600117905534801561001d57600080fd5b5061014c8061002d6000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c80630a9254e4146100515780633a76846314610053578063c04062261461008b578063f8ccbf4714610093575b600080fd5b005b61006e737109709ecfa91a80626ff3989d68f67f5b1dd12d81565b6040516001600160a01b0390911681526020015b60405180910390f35b6100516100b0565b6000546100a09060ff1681565b6040519015158152602001610082565b604080516302bf260160e61b81529051737109709ecfa91a80626ff3989d68f67f5b1dd12d9163afc9804091600480830192600092919082900301818387803b1580156100fc57600080fd5b505af1158015610110573d6000803e3d6000fd5b5050505056fea26469706673582212203a39488c6d5e73072e1dd0c6593caff56e39d0849abc1557f1c6e25cf7dedc2e64736f6c63430008100033",
"sourceMap": "97:126:6:-:0;;;165:28:1;;;-1:-1:-1;;165:28:1;189:4;165:28;;;97:126:6;;;;;;;;;;;;;;;;",
"linkReferences": {}
},
"deployedBytecode": {
"object": "0x608060405234801561001057600080fd5b506004361061004c5760003560e01c80630a9254e4146100515780633a76846314610053578063c04062261461008b578063f8ccbf4714610093575b600080fd5b005b61006e737109709ecfa91a80626ff3989d68f67f5b1dd12d81565b6040516001600160a01b0390911681526020015b60405180910390f35b6100516100b0565b6000546100a09060ff1681565b6040519015158152602001610082565b604080516302bf260160e61b81529051737109709ecfa91a80626ff3989d68f67f5b1dd12d9163afc9804091600480830192600092919082900301818387803b1580156100fc57600080fd5b505af1158015610110573d6000803e3d6000fd5b5050505056fea26469706673582212203a39488c6d5e73072e1dd0c6593caff56e39d0849abc1557f1c6e25cf7dedc2e64736f6c63430008100033",
"sourceMap": "97:126:6:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;136:26;;316:38:1;;245:64;316:38;;;;;-1:-1:-1;;;;;189:32:9;;;171:51;;159:2;144:18;316:38:1;;;;;;;;168:53:6;;;:::i;165:28:1:-;;;;;;;;;;;;398:14:9;;391:22;373:41;;361:2;346:18;165:28:1;233:187:9;168:53:6;200:14;;;-1:-1:-1;;;200:14:6;;;;245:64:1;;200:12:6;;:14;;;;;269:37:1;;200:14:6;;;;;;;269:37:1;245:64;200:14:6;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;168:53::o",
"linkReferences": {}
},
"methodIdentifiers": {
"IS_SCRIPT()": "f8ccbf47",
"run()": "c0406226",
"setUp()": "0a9254e4",
"vm()": "3a768463"
},
"rawMetadata": "{\"compiler\":{\"version\":\"0.8.16+commit.07a7930e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"IS_SCRIPT\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"run\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"setUp\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"vm\",\"outputs\":[{\"internalType\":\"contract Vm\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"script/Counter.s.sol\":\"CounterScript\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[\":ds-test/=lib/forge-std/lib/ds-test/src/\",\":forge-std/=lib/forge-std/src/\"]},\"sources\":{\"lib/forge-std/src/Script.sol\":{\"keccak256\":\"0x4424dbcb8f5b741475445726f87408fcd89951fad973bec2ca442ee157f910e7\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://5b0b9f6dfb69245d8f888558ae82bf1d2cdeace46201444fe4b2e6a5283f944a\",\"dweb:/ipfs/QmWFSKeFEZngNcwNn7A84EF7pASo5qe6r5oK24r9Kwca7Z\"]},\"lib/forge-std/src/Vm.sol\":{\"keccak256\":\"0xa0ede8e0d3dc3246912530aed6cacbc4703e4430c4b4acd91963ccea709755ea\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://a28e7d00aab57ad5159247b0f0f268eda4c6980b29eee7f903578254a2be677f\",\"dweb:/ipfs/QmZrM8gY5BpW8o1QckmPNCYbBP5Q7k5DkcHdaVULKVntxp\"]},\"lib/forge-std/src/console.sol\":{\"keccak256\":\"0x91d5413c2434ca58fd278b6e1e79fd98d10c83931cc2596a6038eee4daeb34ba\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://91ccea707361e48b9b7a161fe81f496b9932bc471e9c4e4e1e9c283f2453cc70\",\"dweb:/ipfs/QmcB66sZhQ6Kz7MUHcLE78YXRUZxoZnnxZjN6yATsbB2ec\"]},\"lib/forge-std/src/console2.sol\":{\"keccak256\":\"0xbeb823fcdb356244a83aaccdf828ad019ecc1ffaa3dff18e624fc6d5714ea671\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://4cbe9400340e5f9ec55e2aff3bad1c15fa3afbbe37e80800e6f3fed2ad26854f\",\"dweb:/ipfs/QmdJBABsuXkvWxVzEyGXsTE3vyfBPXDdw5xvvtUz3JeoYW\"]},\"script/Counter.s.sol\":{\"keccak256\":\"0x01edaa1835b1a5bd3f4f66f73451488b8441d30642d3bf1f5fa2c5bf7c005bee\",\"license\":\"UNLICENSED\",\"urls\":[\"bzz-raw://3c6a0f19216ceeebf4ec16f8f2662a3bebbe18d4037d1399adf2e3e4ccbb57a2\",\"dweb:/ipfs/Qmc8NknjPkSgbXLg6zZQ8uKT6kAWBvBXz5JrDvZfa88UNT\"]}},\"version\":1}",
"metadata": {
"compiler": {
"version": "0.8.16+commit.07a7930e"
},
"language": "Solidity",
"output": {
"abi": [
{
"inputs": [],
"stateMutability": "view",
"type": "function",
"name": "IS_SCRIPT",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
]
},
{
"inputs": [],
"stateMutability": "nonpayable",
"type": "function",
"name": "run"
},
{
"inputs": [],
"stateMutability": "nonpayable",
"type": "function",
"name": "setUp"
},
{
"inputs": [],
"stateMutability": "view",
"type": "function",
"name": "vm",
"outputs": [
{
"internalType": "contract Vm",
"name": "",
"type": "address"
}
]
}
],
"devdoc": {
"kind": "dev",
"methods": {},
"version": 1
},
"userdoc": {
"kind": "user",
"methods": {},
"version": 1
}
},
"settings": {
"remappings": [
":ds-test/=lib/forge-std/lib/ds-test/src/",
":forge-std/=lib/forge-std/src/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"bytecodeHash": "ipfs"
},
"compilationTarget": {
"script/Counter.s.sol": "CounterScript"
},
"libraries": {}
},
"sources": {
"lib/forge-std/src/Script.sol": {
"keccak256": "0x4424dbcb8f5b741475445726f87408fcd89951fad973bec2ca442ee157f910e7",
"urls": [
"bzz-raw://5b0b9f6dfb69245d8f888558ae82bf1d2cdeace46201444fe4b2e6a5283f944a",
"dweb:/ipfs/QmWFSKeFEZngNcwNn7A84EF7pASo5qe6r5oK24r9Kwca7Z"
],
"license": "MIT"
},
"lib/forge-std/src/Vm.sol": {
"keccak256": "0xa0ede8e0d3dc3246912530aed6cacbc4703e4430c4b4acd91963ccea709755ea",
"urls": [
"bzz-raw://a28e7d00aab57ad5159247b0f0f268eda4c6980b29eee7f903578254a2be677f",
"dweb:/ipfs/QmZrM8gY5BpW8o1QckmPNCYbBP5Q7k5DkcHdaVULKVntxp"
],
"license": "MIT"
},
"lib/forge-std/src/console.sol": {
"keccak256": "0x91d5413c2434ca58fd278b6e1e79fd98d10c83931cc2596a6038eee4daeb34ba",
"urls": [
"bzz-raw://91ccea707361e48b9b7a161fe81f496b9932bc471e9c4e4e1e9c283f2453cc70",
"dweb:/ipfs/QmcB66sZhQ6Kz7MUHcLE78YXRUZxoZnnxZjN6yATsbB2ec"
],
"license": "MIT"
},
"lib/forge-std/src/console2.sol": {
"keccak256": "0xbeb823fcdb356244a83aaccdf828ad019ecc1ffaa3dff18e624fc6d5714ea671",
"urls": [
"bzz-raw://4cbe9400340e5f9ec55e2aff3bad1c15fa3afbbe37e80800e6f3fed2ad26854f",
"dweb:/ipfs/QmdJBABsuXkvWxVzEyGXsTE3vyfBPXDdw5xvvtUz3JeoYW"
],
"license": "MIT"
},
"script/Counter.s.sol": {
"keccak256": "0x01edaa1835b1a5bd3f4f66f73451488b8441d30642d3bf1f5fa2c5bf7c005bee",
"urls": [
"bzz-raw://3c6a0f19216ceeebf4ec16f8f2662a3bebbe18d4037d1399adf2e3e4ccbb57a2",
"dweb:/ipfs/Qmc8NknjPkSgbXLg6zZQ8uKT6kAWBvBXz5JrDvZfa88UNT"
],
"license": "UNLICENSED"
}
},
"version": 1
},
"ast": {
"absolutePath": "script/Counter.s.sol",
"id": 21582,
"exportedSymbols": {
"CounterScript": [
21581
],
"Script": [
2022
],
"Vm": [
5434
],
"console": [
13498
],
"console2": [
21562
]
},
"nodeType": "SourceUnit",
"src": "39:185:6",
"nodes": [
{
"id": 21564,
"nodeType": "PragmaDirective",
"src": "39:24:6",
"literals": [
"solidity",
"^",
"0.8",
".13"
]
},
{
"id": 21565,
"nodeType": "ImportDirective",
"src": "65:30:6",
"absolutePath": "lib/forge-std/src/Script.sol",
"file": "forge-std/Script.sol",
"nameLocation": "-1:-1:-1",
"scope": 21582,
"sourceUnit": 2023,
"symbolAliases": [],
"unitAlias": ""
},
{
"id": 21581,
"nodeType": "ContractDefinition",
"src": "97:126:6",
"nodes": [
{
"id": 21571,
"nodeType": "FunctionDefinition",
"src": "136:26:6",
"body": {
"id": 21570,
"nodeType": "Block",
"src": "160:2:6",
"statements": []
},
"functionSelector": "0a9254e4",
"implemented": true,
"kind": "function",
"modifiers": [],
"name": "setUp",
"nameLocation": "145:5:6",
"parameters": {
"id": 21568,
"nodeType": "ParameterList",
"parameters": [],
"src": "150:2:6"
},
"returnParameters": {
"id": 21569,
"nodeType": "ParameterList",
"parameters": [],
"src": "160:0:6"
},
"scope": 21581,
"stateMutability": "nonpayable",
"virtual": false,
"visibility": "public"
},
{
"id": 21580,
"nodeType": "FunctionDefinition",
"src": "168:53:6",
"body": {
"id": 21579,
"nodeType": "Block",
"src": "190:31:6",
"statements": [
{
"expression": {
"arguments": [],
"expression": {
"argumentTypes": [],
"expression": {
"id": 21574,
"name": "vm",
"nodeType": "Identifier",
"overloadedDeclarations": [],
"referencedDeclaration": 1817,
"src": "200:2:6",
"typeDescriptions": {
"typeIdentifier": "t_contract$_Vm_$5434",
"typeString": "contract Vm"
}
},
"id": 21576,
"isConstant": false,
"isLValue": false,
"isPure": false,
"lValueRequested": false,
"memberLocation": "203:9:6",
"memberName": "broadcast",
"nodeType": "MemberAccess",
"referencedDeclaration": 5173,
"src": "200:12:6",
"typeDescriptions": {
"typeIdentifier": "t_function_external_nonpayable$__$returns$__$",
"typeString": "function () external"
}
},
"id": 21577,
"isConstant": false,
"isLValue": false,
"isPure": false,
"kind": "functionCall",
"lValueRequested": false,
"nameLocations": [],
"names": [],
"nodeType": "FunctionCall",
"src": "200:14:6",
"tryCall": false,
"typeDescriptions": {
"typeIdentifier": "t_tuple$__$",
"typeString": "tuple()"
}
},
"id": 21578,
"nodeType": "ExpressionStatement",
"src": "200:14:6"
}
]
},
"functionSelector": "c0406226",
"implemented": true,
"kind": "function",
"modifiers": [],
"name": "run",
"nameLocation": "177:3:6",
"parameters": {
"id": 21572,
"nodeType": "ParameterList",
"parameters": [],
"src": "180:2:6"
},
"returnParameters": {
"id": 21573,
"nodeType": "ParameterList",
"parameters": [],
"src": "190:0:6"
},
"scope": 21581,
"stateMutability": "nonpayable",
"virtual": false,
"visibility": "public"
}
],
"abstract": false,
"baseContracts": [
{
"baseName": {
"id": 21566,
"name": "Script",
"nameLocations": [
"123:6:6"
],
"nodeType": "IdentifierPath",
"referencedDeclaration": 2022,
"src": "123:6:6"
},
"id": 21567,
"nodeType": "InheritanceSpecifier",
"src": "123:6:6"
}
],
"canonicalName": "CounterScript",
"contractDependencies": [],
"contractKind": "contract",
"fullyImplemented": true,
"linearizedBaseContracts": [
21581,
2022
],
"name": "CounterScript",
"nameLocation": "106:13:6",
"scope": 21582,
"usedErrors": []
}
],
"license": "UNLICENSED"
},
"id": 6
}

@ -1,377 +0,0 @@
{
"abi": [
{
"inputs": [],
"name": "increment",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "number",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "newNumber",
"type": "uint256"
}
],
"name": "setNumber",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
],
"bytecode": {
"object": "0x608060405234801561001057600080fd5b5060f78061001f6000396000f3fe6080604052348015600f57600080fd5b5060043610603c5760003560e01c80633fb5c1cb1460415780638381f58a146053578063d09de08a14606d575b600080fd5b6051604c3660046083565b600055565b005b605b60005481565b60405190815260200160405180910390f35b6051600080549080607c83609b565b9190505550565b600060208284031215609457600080fd5b5035919050565b60006001820160ba57634e487b7160e01b600052601160045260246000fd5b506001019056fea2646970667358221220f4a9b22e7a2d64c24355b4e7a6f8c62115ca728f26fc2a1e98e364ee91f794fa64736f6c63430008100033",
"sourceMap": "65:192:7:-:0;;;;;;;;;;;;;;;;;;;",
"linkReferences": {}
},
"deployedBytecode": {
"object": "0x6080604052348015600f57600080fd5b5060043610603c5760003560e01c80633fb5c1cb1460415780638381f58a146053578063d09de08a14606d575b600080fd5b6051604c3660046083565b600055565b005b605b60005481565b60405190815260200160405180910390f35b6051600080549080607c83609b565b9190505550565b600060208284031215609457600080fd5b5035919050565b60006001820160ba57634e487b7160e01b600052601160045260246000fd5b506001019056fea2646970667358221220f4a9b22e7a2d64c24355b4e7a6f8c62115ca728f26fc2a1e98e364ee91f794fa64736f6c63430008100033",
"sourceMap": "65:192:7:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;116:80;;;;;;:::i;:::-;171:6;:18;116:80;;;88:21;;;;;;;;;345:25:9;;;333:2;318:18;88:21:7;;;;;;;202:53;;240:6;:8;;;:6;:8;;;:::i;:::-;;;;;;202:53::o;14:180:9:-;73:6;126:2;114:9;105:7;101:23;97:32;94:52;;;142:1;139;132:12;94:52;-1:-1:-1;165:23:9;;14:180;-1:-1:-1;14:180:9:o;381:232::-;420:3;441:17;;;438:140;;500:10;495:3;491:20;488:1;481:31;535:4;532:1;525:15;563:4;560:1;553:15;438:140;-1:-1:-1;605:1:9;594:13;;381:232::o",
"linkReferences": {}
},
"methodIdentifiers": {
"increment()": "d09de08a",
"number()": "8381f58a",
"setNumber(uint256)": "3fb5c1cb"
},
"rawMetadata": "{\"compiler\":{\"version\":\"0.8.16+commit.07a7930e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"increment\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"number\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newNumber\",\"type\":\"uint256\"}],\"name\":\"setNumber\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/Counter.sol\":\"Counter\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[\":ds-test/=lib/forge-std/lib/ds-test/src/\",\":forge-std/=lib/forge-std/src/\"]},\"sources\":{\"src/Counter.sol\":{\"keccak256\":\"0x09277f949d59a9521708c870dc39c2c434ad8f86a5472efda6a732ef728c0053\",\"license\":\"UNLICENSED\",\"urls\":[\"bzz-raw://94cd5258357da018bf911aeda60ed9f5b130dce27445669ee200313cd3389200\",\"dweb:/ipfs/QmNbEfWAqXCtfQpk6u7TpGa8sTHXFLpUz7uebz2FVbchSC\"]}},\"version\":1}",
"metadata": {
"compiler": {
"version": "0.8.16+commit.07a7930e"
},
"language": "Solidity",
"output": {
"abi": [
{
"inputs": [],
"stateMutability": "nonpayable",
"type": "function",
"name": "increment"
},
{
"inputs": [],
"stateMutability": "view",
"type": "function",
"name": "number",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
]
},
{
"inputs": [
{
"internalType": "uint256",
"name": "newNumber",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function",
"name": "setNumber"
}
],
"devdoc": {
"kind": "dev",
"methods": {},
"version": 1
},
"userdoc": {
"kind": "user",
"methods": {},
"version": 1
}
},
"settings": {
"remappings": [
":ds-test/=lib/forge-std/lib/ds-test/src/",
":forge-std/=lib/forge-std/src/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"bytecodeHash": "ipfs"
},
"compilationTarget": {
"src/Counter.sol": "Counter"
},
"libraries": {}
},
"sources": {
"src/Counter.sol": {
"keccak256": "0x09277f949d59a9521708c870dc39c2c434ad8f86a5472efda6a732ef728c0053",
"urls": [
"bzz-raw://94cd5258357da018bf911aeda60ed9f5b130dce27445669ee200313cd3389200",
"dweb:/ipfs/QmNbEfWAqXCtfQpk6u7TpGa8sTHXFLpUz7uebz2FVbchSC"
],
"license": "UNLICENSED"
}
},
"version": 1
},
"ast": {
"absolutePath": "src/Counter.sol",
"id": 21604,
"exportedSymbols": {
"Counter": [
21603
]
},
"nodeType": "SourceUnit",
"src": "39:219:7",
"nodes": [
{
"id": 21583,
"nodeType": "PragmaDirective",
"src": "39:24:7",
"literals": [
"solidity",
"^",
"0.8",
".13"
]
},
{
"id": 21603,
"nodeType": "ContractDefinition",
"src": "65:192:7",
"nodes": [
{
"id": 21585,
"nodeType": "VariableDeclaration",
"src": "88:21:7",
"constant": false,
"functionSelector": "8381f58a",
"mutability": "mutable",
"name": "number",
"nameLocation": "103:6:7",
"scope": 21603,
"stateVariable": true,
"storageLocation": "default",
"typeDescriptions": {
"typeIdentifier": "t_uint256",
"typeString": "uint256"
},
"typeName": {
"id": 21584,
"name": "uint256",
"nodeType": "ElementaryTypeName",
"src": "88:7:7",
"typeDescriptions": {
"typeIdentifier": "t_uint256",
"typeString": "uint256"
}
},
"visibility": "public"
},
{
"id": 21595,
"nodeType": "FunctionDefinition",
"src": "116:80:7",
"body": {
"id": 21594,
"nodeType": "Block",
"src": "161:35:7",
"statements": [
{
"expression": {
"id": 21592,
"isConstant": false,
"isLValue": false,
"isPure": false,
"lValueRequested": false,
"leftHandSide": {
"id": 21590,
"name": "number",
"nodeType": "Identifier",
"overloadedDeclarations": [],
"referencedDeclaration": 21585,
"src": "171:6:7",
"typeDescriptions": {
"typeIdentifier": "t_uint256",
"typeString": "uint256"
}
},
"nodeType": "Assignment",
"operator": "=",
"rightHandSide": {
"id": 21591,
"name": "newNumber",
"nodeType": "Identifier",
"overloadedDeclarations": [],
"referencedDeclaration": 21587,
"src": "180:9:7",
"typeDescriptions": {
"typeIdentifier": "t_uint256",
"typeString": "uint256"
}
},
"src": "171:18:7",
"typeDescriptions": {
"typeIdentifier": "t_uint256",
"typeString": "uint256"
}
},
"id": 21593,
"nodeType": "ExpressionStatement",
"src": "171:18:7"
}
]
},
"functionSelector": "3fb5c1cb",
"implemented": true,
"kind": "function",
"modifiers": [],
"name": "setNumber",
"nameLocation": "125:9:7",
"parameters": {
"id": 21588,
"nodeType": "ParameterList",
"parameters": [
{
"constant": false,
"id": 21587,
"mutability": "mutable",
"name": "newNumber",
"nameLocation": "143:9:7",
"nodeType": "VariableDeclaration",
"scope": 21595,
"src": "135:17:7",
"stateVariable": false,
"storageLocation": "default",
"typeDescriptions": {
"typeIdentifier": "t_uint256",
"typeString": "uint256"
},
"typeName": {
"id": 21586,
"name": "uint256",
"nodeType": "ElementaryTypeName",
"src": "135:7:7",
"typeDescriptions": {
"typeIdentifier": "t_uint256",
"typeString": "uint256"
}
},
"visibility": "internal"
}
],
"src": "134:19:7"
},
"returnParameters": {
"id": 21589,
"nodeType": "ParameterList",
"parameters": [],
"src": "161:0:7"
},
"scope": 21603,
"stateMutability": "nonpayable",
"virtual": false,
"visibility": "public"
},
{
"id": 21602,
"nodeType": "FunctionDefinition",
"src": "202:53:7",
"body": {
"id": 21601,
"nodeType": "Block",
"src": "230:25:7",
"statements": [
{
"expression": {
"id": 21599,
"isConstant": false,
"isLValue": false,
"isPure": false,
"lValueRequested": false,
"nodeType": "UnaryOperation",
"operator": "++",
"prefix": false,
"src": "240:8:7",
"subExpression": {
"id": 21598,
"name": "number",
"nodeType": "Identifier",
"overloadedDeclarations": [],
"referencedDeclaration": 21585,
"src": "240:6:7",
"typeDescriptions": {
"typeIdentifier": "t_uint256",
"typeString": "uint256"
}
},
"typeDescriptions": {
"typeIdentifier": "t_uint256",
"typeString": "uint256"
}
},
"id": 21600,
"nodeType": "ExpressionStatement",
"src": "240:8:7"
}
]
},
"functionSelector": "d09de08a",
"implemented": true,
"kind": "function",
"modifiers": [],
"name": "increment",
"nameLocation": "211:9:7",
"parameters": {
"id": 21596,
"nodeType": "ParameterList",
"parameters": [],
"src": "220:2:7"
},
"returnParameters": {
"id": 21597,
"nodeType": "ParameterList",
"parameters": [],
"src": "230:0:7"
},
"scope": 21603,
"stateMutability": "nonpayable",
"virtual": false,
"visibility": "public"
}
],
"abstract": false,
"baseContracts": [],
"canonicalName": "Counter",
"contractDependencies": [],
"contractKind": "contract",
"fullyImplemented": true,
"linearizedBaseContracts": [
21603
],
"name": "Counter",
"nameLocation": "74:7:7",
"scope": 21604,
"usedErrors": []
}
],
"license": "UNLICENSED"
},
"id": 7
}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -1,14 +0,0 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
contract Counter {
uint256 public number;
function setNumber(uint256 newNumber) public {
number = newNumber;
}
function increment() public {
number++;
}
}

@ -9,6 +9,7 @@ import {Web3ProviderModule} from './app/tabs/web3-provider'
import {CompileAndRun} from './app/tabs/compile-and-run' import {CompileAndRun} from './app/tabs/compile-and-run'
import {PluginStateLogger} from './app/tabs/state-logger' import {PluginStateLogger} from './app/tabs/state-logger'
import {SidePanel} from './app/components/side-panel' import {SidePanel} from './app/components/side-panel'
import {StatusBar} from './app/components/status-bar'
import {HiddenPanel} from './app/components/hidden-panel' import {HiddenPanel} from './app/components/hidden-panel'
import {PinnedPanel} from './app/components/pinned-panel' import {PinnedPanel} from './app/components/pinned-panel'
import {VerticalIcons} from './app/components/vertical-icons' import {VerticalIcons} from './app/components/vertical-icons'
@ -345,7 +346,7 @@ class AppComponent {
solidityumlgen, solidityumlgen,
compilationDetails, compilationDetails,
vyperCompilationDetails, vyperCompilationDetails,
// remixGuide, remixGuide,
contractFlattener, contractFlattener,
solidityScript, solidityScript,
templates, templates,
@ -385,11 +386,12 @@ class AppComponent {
this.pinnedPanel = new PinnedPanel() this.pinnedPanel = new PinnedPanel()
const pluginManagerComponent = new PluginManagerComponent(appManager, this.engine) const pluginManagerComponent = new PluginManagerComponent(appManager, this.engine)
const filePanel = new FilePanel(appManager) const filePanel = new FilePanel(appManager, contentImport)
this.statusBar = new StatusBar(filePanel, this.menuicons)
const landingPage = new LandingPage(appManager, this.menuicons, fileManager, filePanel, contentImport) const landingPage = new LandingPage(appManager, this.menuicons, fileManager, filePanel, contentImport)
this.settings = new SettingsTab(Registry.getInstance().get('config').api, editor, appManager) this.settings = new SettingsTab(Registry.getInstance().get('config').api, editor, appManager)
this.engine.register([this.menuicons, landingPage, this.hiddenPanel, this.sidePanel, filePanel, pluginManagerComponent, this.settings, this.pinnedPanel]) this.engine.register([this.menuicons, landingPage, this.hiddenPanel, this.sidePanel, this.statusBar, filePanel, pluginManagerComponent, this.settings, this.pinnedPanel])
// CONTENT VIEWS & DEFAULT PLUGINS // CONTENT VIEWS & DEFAULT PLUGINS
const openZeppelinProxy = new OpenZeppelinProxy(blockchain) const openZeppelinProxy = new OpenZeppelinProxy(blockchain)
@ -471,6 +473,7 @@ class AppComponent {
'pluginStateLogger' 'pluginStateLogger'
]) ])
await this.appManager.activatePlugin(['mainPanel', 'menuicons', 'tabs']) await this.appManager.activatePlugin(['mainPanel', 'menuicons', 'tabs'])
await this.appManager.activatePlugin(['statusBar'])
await this.appManager.activatePlugin(['sidePanel']) // activating host plugin separately await this.appManager.activatePlugin(['sidePanel']) // activating host plugin separately
await this.appManager.activatePlugin(['pinnedPanel']) await this.appManager.activatePlugin(['pinnedPanel'])
await this.appManager.activatePlugin(['home']) await this.appManager.activatePlugin(['home'])
@ -510,9 +513,6 @@ class AppComponent {
) )
await this.appManager.activatePlugin(['solidity-script']) await this.appManager.activatePlugin(['solidity-script'])
await this.appManager.activatePlugin(['solcoder']) await this.appManager.activatePlugin(['solcoder'])
await this.appManager.activatePlugin(['filePanel']) await this.appManager.activatePlugin(['filePanel'])
// Set workspace after initial activation // Set workspace after initial activation

@ -1,7 +1,6 @@
import { RemixApp } from '@remix-ui/app' import { RemixApp } from '@remix-ui/app'
import axios from 'axios' import axios from 'axios'
import React, { useEffect, useRef, useState } from 'react' import React, { useEffect, useRef, useState } from 'react'
import { createRoot } from 'react-dom/client'
import * as packageJson from '../../../../../package.json' import * as packageJson from '../../../../../package.json'
import { fileSystem, fileSystems } from '../files/fileSystem' import { fileSystem, fileSystems } from '../files/fileSystem'
import { indexedDBFileSystem } from '../files/filesystems/indexedDB' import { indexedDBFileSystem } from '../files/filesystems/indexedDB'

@ -12,7 +12,7 @@ const sidePanel = {
displayName: 'Side Panel', displayName: 'Side Panel',
description: 'Remix IDE side panel', description: 'Remix IDE side panel',
version: packageJson.version, version: packageJson.version,
methods: ['addView', 'removeView', 'currentFocus', 'pinView', 'unPinView'] methods: ['addView', 'removeView', 'currentFocus', 'pinView', 'unPinView', 'focus']
} }
export class SidePanel extends AbstractPanel { export class SidePanel extends AbstractPanel {

@ -0,0 +1,105 @@
import React from 'react'
import { EventEmitter } from 'events'
import { Plugin } from '@remixproject/engine'
import packageJson from '../../../../../package.json'
import { PluginViewWrapper } from '@remix-ui/helper'
import { PluginProfile, StatusBarInterface } from '../../types'
import { RemixUIStatusBar } from '@remix-ui/statusbar'
import { FilePanelType } from '@remix-ui/workspace'
import { VerticalIcons } from './vertical-icons'
const statusBarProfile: PluginProfile = {
name: 'statusBar',
displayName: 'Status Bar',
description: 'Remix IDE status bar panel',
methods: ['isAIActive'],
version: packageJson.version,
}
export class StatusBar extends Plugin implements StatusBarInterface {
htmlElement: HTMLDivElement
events: EventEmitter
filePanelPlugin: FilePanelType
verticalIcons: VerticalIcons
dispatch: React.Dispatch<any> = () => {}
currentWorkspaceName: string = ''
isGitRepo: boolean = false
isAiActive: boolean = false
constructor(filePanel: FilePanelType, veritcalIcons: VerticalIcons) {
super(statusBarProfile)
this.filePanelPlugin = filePanel
this.verticalIcons = veritcalIcons
this.events = new EventEmitter()
this.htmlElement = document.createElement('div')
this.htmlElement.setAttribute('id', 'status-bar')
this.filePanelPlugin
}
async isWorkspaceAGitRepo() {
const isGit = await this.call('fileManager', 'isGitRepo')
if (!isGit) return
this.isGitRepo = true
this.renderComponent()
}
async setCurrentGitWorkspaceName() {
if (!this.isGitRepo) return
const workspaceName = localStorage.getItem('currentWorkspace')
workspaceName && workspaceName.length > 0 ? this.currentWorkspaceName = workspaceName : this.currentWorkspaceName = 'unknown'
this.renderComponent()
}
async isAIActive() {
let aiActive
this.on('settings', 'copilotChoiceUpdated', async (isChecked) => {
aiActive = isChecked
this.isAiActive = isChecked
})
this.renderComponent()
return aiActive
}
onActivation(): void {
this.on('filePanel', 'workspaceInitializationCompleted', async () => {
const isGit = await this.call('fileManager', 'isGitRepo')
if (!isGit) return
const workspaceName = localStorage.getItem('currentWorkspace')
workspaceName && workspaceName.length > 0 ? this.currentWorkspaceName = workspaceName : this.currentWorkspaceName = ''
})
this.on('filePanel', 'switchToWorkspace', async (workspace: string) => {
await this.isWorkspaceAGitRepo()
if (!this.isGitRepo) {
this.currentWorkspaceName = 'Not a git repo'
return
}
const workspaceName = localStorage.getItem('currentWorkspace')
workspaceName && workspaceName.length > 0 ? this.currentWorkspaceName = workspaceName : this.currentWorkspaceName = 'error'
})
this.on('settings', 'copilotChoiceChanged', (isAiActive) => {
this.isAiActive = isAiActive
})
this.renderComponent()
}
setDispatch(dispatch: React.Dispatch<any>) {
this.dispatch = dispatch
}
renderComponent() {
this.dispatch({
plugins: this,
})
}
updateComponent(state: any) {
return <RemixUIStatusBar statusBarPlugin={state.plugins} />
}
render() {
return (
<div data-id="status-bar-container">
<PluginViewWrapper plugin={this} />
</div>
)
}
}

@ -436,7 +436,7 @@ class DGitProvider extends Plugin {
const permission = await this.askUserPermission('clone', 'Import multiple files into your workspaces.') const permission = await this.askUserPermission('clone', 'Import multiple files into your workspaces.')
if (!permission) return false if (!permission) return false
if (parseFloat(this.calculateLocalStorage()) > 10000) throw new Error('The local storage of the browser is full.') if (parseFloat(this.calculateLocalStorage()) > 10000) throw new Error('The local storage of the browser is full.')
if (!workspaceExists) await this.call('filePanel', 'createWorkspace', workspaceName || `workspace_${Date.now()}`, true) if (!workspaceExists) await this.call('filePanel', 'createWorkspace', workspaceName || `workspace_${Date.now()}`, '', true)
const cmd = { const cmd = {
url: input.url, url: input.url,
singleBranch: input.singleBranch, singleBranch: input.singleBranch,
@ -445,7 +445,7 @@ class DGitProvider extends Plugin {
...await this.parseInput(input), ...await this.parseInput(input),
...await this.getGitConfig() ...await this.getGitConfig()
} }
this.call('terminal', 'logHtml', `Cloning ${input.url}...`) this.call('terminal', 'logHtml', `Cloning ${input.url}... please wait...`)
const result = await git.clone(cmd) const result = await git.clone(cmd)
if (!workspaceExists) { if (!workspaceExists) {
setTimeout(async () => { setTimeout(async () => {
@ -453,6 +453,12 @@ class DGitProvider extends Plugin {
}, 1000) }, 1000)
} }
this.emit('clone') this.emit('clone')
this.call('fileManager', 'hasGitSubmodules').then((submodules) => {
if (submodules) {
this.call('terminal', 'log', { type: 'warn', value: 'This repository has submodules. Please update submodules to pull all the dependencies.' })
this.emit('repositoryWithSubmodulesCloned')
}
})
return result return result
} }
} }

@ -24,7 +24,7 @@ const profile = {
methods: ['closeAllFiles', 'closeFile', 'file', 'exists', 'open', 'writeFile', 'writeMultipleFiles', 'writeFileNoRewrite', methods: ['closeAllFiles', 'closeFile', 'file', 'exists', 'open', 'writeFile', 'writeMultipleFiles', 'writeFileNoRewrite',
'readFile', 'copyFile', 'copyDir', 'rename', 'mkdir', 'readdir', 'dirList', 'fileList', 'remove', 'getCurrentFile', 'getFile', 'readFile', 'copyFile', 'copyDir', 'rename', 'mkdir', 'readdir', 'dirList', 'fileList', 'remove', 'getCurrentFile', 'getFile',
'getFolder', 'setFile', 'switchFile', 'refresh', 'getProviderOf', 'getProviderByName', 'getPathFromUrl', 'getUrlFromPath', 'getFolder', 'setFile', 'switchFile', 'refresh', 'getProviderOf', 'getProviderByName', 'getPathFromUrl', 'getUrlFromPath',
'saveCurrentFile', 'setBatchFiles', 'isGitRepo', 'isFile', 'isDirectory', 'hasGitSubmodule', 'copyFolderToJson' 'saveCurrentFile', 'setBatchFiles', 'isGitRepo', 'isFile', 'isDirectory', 'hasGitSubmodules', 'copyFolderToJson'
], ],
kind: 'file-system' kind: 'file-system'
} }

@ -34,6 +34,7 @@ const profile = {
methods: [ methods: [
'createNewFile', 'createNewFile',
'uploadFile', 'uploadFile',
'echoCall',
'getCurrentWorkspace', 'getCurrentWorkspace',
'getAvailableWorkspaceName', 'getAvailableWorkspaceName',
'getWorkspaces', 'getWorkspaces',
@ -58,7 +59,7 @@ const profile = {
maintainedBy: 'Remix' maintainedBy: 'Remix'
} }
module.exports = class Filepanel extends ViewPlugin { module.exports = class Filepanel extends ViewPlugin {
constructor(appManager) { constructor(appManager, contentImport) {
super(profile) super(profile)
this.registry = Registry.getInstance() this.registry = Registry.getInstance()
this.fileProviders = this.registry.get('fileproviders').api this.fileProviders = this.registry.get('fileproviders').api
@ -72,6 +73,7 @@ module.exports = class Filepanel extends ViewPlugin {
this.foundryHandle = new FoundryHandle() this.foundryHandle = new FoundryHandle()
this.truffleHandle = new TruffleHandle() this.truffleHandle = new TruffleHandle()
this.slitherHandle = new SlitherHandle() this.slitherHandle = new SlitherHandle()
this.contentImport = contentImport
this.workspaces = [] this.workspaces = []
this.appManager = appManager this.appManager = appManager
this.currentWorkspaceMetadata = null this.currentWorkspaceMetadata = null

@ -240,6 +240,11 @@ export class TabProxy extends Plugin {
if ((name.endsWith('.vy') && icon === undefined) || title.includes('Vyper')) { if ((name.endsWith('.vy') && icon === undefined) || title.includes('Vyper')) {
icon = 'assets/img/vyperLogo2.webp' icon = 'assets/img/vyperLogo2.webp'
} }
if (title === 'Solidity Compile Details') {
icon = 'assets/img/solidity.webp'
}
var slash = name.split('/') var slash = name.split('/')
const tabPath = slash.reverse() const tabPath = slash.reverse()
@ -357,7 +362,9 @@ export class TabProxy extends Plugin {
const onZoomIn = () => this.editor.editorFontSize(1) const onZoomIn = () => this.editor.editorFontSize(1)
const onZoomOut = () => this.editor.editorFontSize(-1) const onZoomOut = () => this.editor.editorFontSize(-1)
const onReady = (api) => { this.tabsApi = api } const onReady = (api) => {
this.tabsApi = api
}
this.dispatch({ this.dispatch({
plugin: this, plugin: this,

@ -45,7 +45,6 @@ export class CompilationDetailsPlugin extends ViewPlugin {
async showDetails(sentPayload: any) { async showDetails(sentPayload: any) {
await this.call('tabs', 'focus', 'compilationDetails') await this.call('tabs', 'focus', 'compilationDetails')
setTimeout(() => { setTimeout(() => {
// TODO: use the react API to render when the tab is focused and the plugin in the view.
this.payload = sentPayload this.payload = sentPayload
this.renderComponent() this.renderComponent()
}, 2000) }, 2000)

@ -1,12 +1,12 @@
import React from 'react' import React, {useState} from 'react' // eslint-disable-line
import { ViewPlugin } from '@remixproject/engine-web' import { ViewPlugin } from '@remixproject/engine-web'
import { PluginViewWrapper } from '@remix-ui/helper' import { PluginViewWrapper } from '@remix-ui/helper'
import { RemixAppManager } from '../../remixAppManager' import { RemixAppManager } from '../../remixAppManager'
import { RemixUIGridView } from '@remix-ui/remix-ui-grid-view' import { RemixUIGridView } from '@remix-ui/remix-ui-grid-view'
import { RemixUIGridSection } from '@remix-ui/remix-ui-grid-section' import { RemixUIGridSection } from '@remix-ui/remix-ui-grid-section'
import { RemixUIGridCell } from '@remix-ui/remix-ui-grid-cell' import { RemixUIGridCell } from '@remix-ui/remix-ui-grid-cell'
import { ThemeKeys, ThemeObject } from '@microlink/react-json-view' import * as Data from './remixGuideData.json'
//@ts-ignore //@ts-ignore
const _paq = (window._paq = window._paq || []) const _paq = (window._paq = window._paq || [])
@ -15,7 +15,6 @@ const profile = {
displayName: 'Remix Guide', displayName: 'Remix Guide',
description: 'Learn remix with videos', description: 'Learn remix with videos',
location: 'mainPanel', location: 'mainPanel',
methods: ['showDetails'],
events: [] events: []
} }
@ -24,13 +23,20 @@ export class RemixGuidePlugin extends ViewPlugin {
appManager: RemixAppManager appManager: RemixAppManager
element: HTMLDivElement element: HTMLDivElement
payload: any payload: any
themeStyle: any showVideo: boolean
theme: ThemeKeys | ThemeObject videoID: string
handleKeyDown: any
handleEscape: any
constructor(appManager: RemixAppManager) { constructor(appManager: RemixAppManager) {
super(profile) super(profile)
this.appManager = appManager this.appManager = appManager
this.element = document.createElement('div') this.element = document.createElement('div')
this.element.setAttribute('id', 'remixGuideEl') this.element.setAttribute('id', 'remixGuideEl')
this.payload = {
sectionToExpandedCell: [['', '']],
data: {}
}
} }
async onActivation() { async onActivation() {
@ -38,24 +44,23 @@ export class RemixGuidePlugin extends ViewPlugin {
await this.call('tabs', 'focus', 'remixGuide') await this.call('tabs', 'focus', 'remixGuide')
this.renderComponent() this.renderComponent()
_paq.push(['trackEvent', 'plugin', 'activated', 'remixGuide']) _paq.push(['trackEvent', 'plugin', 'activated', 'remixGuide'])
// Read the data
this.payload.data = Data
this.handleKeyDown = (event) => {
if (event.key === 'Escape') {
this.showVideo = false
this.renderComponent()
} }
}
onDeactivation(): void { document.addEventListener('keydown', this.handleKeyDown);
} }
async showDetails(sentPayload: any) { onDeactivation(): void {
const contractName = Object.entries(sentPayload).find(([key, value]) => key) document.removeEventListener('keydown', this.handleKeyDown);
await this.call('tabs', 'focus', 'remixGuide')
this.profile.displayName = `${contractName[0]}`
this.payload = sentPayload
const active = await this.call('theme', 'currentTheme')
this.renderComponent()
} }
private handleThemeChange() { private handleThemeChange() {
this.on('theme', 'themeChanged', (theme: any) => { this.on('theme', 'themeChanged', (theme: any) => {
this.renderComponent() this.renderComponent()
}) })
} }
@ -64,6 +69,7 @@ export class RemixGuidePlugin extends ViewPlugin {
this.dispatch = dispatch this.dispatch = dispatch
this.renderComponent() this.renderComponent()
} }
render() { render() {
return ( return (
<div className="bg-dark" id="remixGuide"> <div className="bg-dark" id="remixGuide">
@ -76,20 +82,21 @@ export class RemixGuidePlugin extends ViewPlugin {
this.dispatch({ this.dispatch({
...this, ...this,
...this.payload, ...this.payload,
themeStyle: this.themeStyle, showVideo: this.showVideo,
theme: this.theme videoID: this.videoID
}) })
} }
updateComponent(state: any) { updateComponent(state: any) {
return ( return (
<div className='d-flex'>
<RemixUIGridView <RemixUIGridView
plugin={this} plugin={this}
styleList={""} styleList={""}
logo='/assets/img/YouTubeLogo.webp' logo='/assets/img/YouTubeLogo.webp'
enableFilter={true} enableFilter={true}
showUntagged={true} showUntagged={true}
showPin={true} showPin={false}
tagList={[ tagList={[
['beginner', 'danger'], ['beginner', 'danger'],
['advanced', 'warning'], ['advanced', 'warning'],
@ -99,145 +106,64 @@ export class RemixGuidePlugin extends ViewPlugin {
['vyper', 'info'], ['vyper', 'info'],
['L2', 'danger'] ['L2', 'danger']
]} ]}
title='Remix Guide' title={Data.title}
description="Streamlined access to categorized video tutorials for mastering Remix IDE. From fundamentals to advanced techniques, level up your development skills with ease." description={Data.description}
//themeStyle={state.themeStyle}
> >
<RemixUIGridSection { Data.sections.map(section => {
return <RemixUIGridSection
plugin={this} plugin={this}
title='Basics' title={section.title}
hScrollable= {true} hScrollable= {true}
> >
<RemixUIGridCell { section.cells.map(cell => {
plugin={this} return <RemixUIGridCell
title="first item"
tagList={['L2', 'AI']}
logo='/assets/img/soliditySurvey2023.webp'
>
<img src={'/assets/img/soliditySurvey2023.webp'} style={{ height: '70px', width: '70px' }} alt=""></img>
</RemixUIGridCell>
<RemixUIGridCell
plugin={this}
title="next"
pinned={true}
tagList={['L2', 'plugins']}
>
<img src={'/assets/img/soliditySurvey2023.webp'} style={{ height: '70px', width: '70px' }} alt=""></img>
</RemixUIGridCell> <RemixUIGridCell
plugin={this}
title="something"
pinned={false}
tagList={['solidity', 'plugins']}
>
<img src={'/assets/img/soliditySurvey2023.webp'} style={{ height: '70px', width: '70px' }} alt=""></img>
</RemixUIGridCell>
<RemixUIGridCell
plugin={this}
title="1"
tagList={['solidity']}
>
<img src={'/assets/img/soliditySurvey2023.webp'} style={{ height: '70px', width: '70px' }} alt=""></img>
</RemixUIGridCell> <RemixUIGridCell
plugin={this}
title="1"
>
<img src={'/assets/img/soliditySurvey2023.webp'} style={{ height: '70px', width: '70px' }} alt=""></img>
</RemixUIGridCell>
<RemixUIGridCell
plugin={this}
title="Something very very long"
>
<img src={'/assets/img/soliditySurvey2023.webp'} style={{ height: '70px', width: '70px' }} alt=""></img>
</RemixUIGridCell> <RemixUIGridCell
plugin={this}
title="1"
>
<img src={'/assets/img/soliditySurvey2023.webp'} style={{ height: '70px', width: '70px' }} alt=""></img>
</RemixUIGridCell>
<RemixUIGridCell
plugin={this}
title="1"
>
<img src={'/assets/img/soliditySurvey2023.webp'} style={{ height: '70px', width: '70px' }} alt=""></img>
</RemixUIGridCell>
<RemixUIGridCell
plugin={this}
title="1"
>
<img src={'/assets/img/soliditySurvey2023.webp'} style={{ height: '70px', width: '70px' }} alt=""></img>
</RemixUIGridCell>
<RemixUIGridCell
plugin={this}
title="1"
>
<img src={'/assets/img/soliditySurvey2023.webp'} style={{ height: '70px', width: '70px' }} alt=""></img>
</RemixUIGridCell>
</RemixUIGridSection>
<RemixUIGridSection
plugin={this}
title='Basics not scrollable'
hScrollable= {false}
>
<RemixUIGridCell
plugin={this}
title="first item"
logo='/assets/img/soliditySurvey2023.webp'
>
<img src={'/assets/img/soliditySurvey2023.webp'} style={{ height: '70px', width: '70px' }} alt=""></img>
</RemixUIGridCell>
<RemixUIGridCell
plugin={this}
title="next"
>
<img src={'/assets/img/soliditySurvey2023.webp'} style={{ height: '70px', width: '70px' }} alt=""></img>
</RemixUIGridCell> <RemixUIGridCell
plugin={this} plugin={this}
title="something" title={cell.title}
> tagList={cell.tagList}
<img src={'/assets/img/soliditySurvey2023.webp'} style={{ height: '70px', width: '70px' }} alt=""></img> expandViewEl={
</RemixUIGridCell> cell.expandViewElement
<RemixUIGridCell }
plugin={this} handleExpand={() => {
title="1" this.showVideo = true
> this.videoID = cell.expandViewElement.videoID
<img src={'/assets/img/soliditySurvey2023.webp'} style={{ height: '70px', width: '70px' }} alt=""></img> this.renderComponent()
</RemixUIGridCell> <RemixUIGridCell }}
plugin={this} logo={cell.expandViewElement.logo}
title="1"
>
<img src={'/assets/img/soliditySurvey2023.webp'} style={{ height: '70px', width: '70px' }} alt=""></img>
</RemixUIGridCell>
<RemixUIGridCell
plugin={this}
title="1"
>
<img src={'/assets/img/soliditySurvey2023.webp'} style={{ height: '70px', width: '70px' }} alt=""></img>
</RemixUIGridCell> <RemixUIGridCell
plugin={this}
title="1"
>
<img src={'/assets/img/soliditySurvey2023.webp'} style={{ height: '70px', width: '70px' }} alt=""></img>
</RemixUIGridCell>
<RemixUIGridCell
plugin={this}
title="1"
>
<img src={'/assets/img/soliditySurvey2023.webp'} style={{ height: '70px', width: '70px' }} alt=""></img>
</RemixUIGridCell>
<RemixUIGridCell
plugin={this}
title="1"
>
<img src={'/assets/img/soliditySurvey2023.webp'} style={{ height: '70px', width: '70px' }} alt=""></img>
</RemixUIGridCell>
<RemixUIGridCell
plugin={this}
title="1"
> >
<img src={'/assets/img/soliditySurvey2023.webp'} style={{ height: '70px', width: '70px' }} alt=""></img> <a href={"https://www.youtube.com/@" + cell.authorURL} target="__blank">
<img src={"//img.youtube.com/vi/" + this.videoID + "/0.jpg"} style={{ height: '70px', width: '70px' }}></img>
</a>
</RemixUIGridCell> </RemixUIGridCell>
})}
</RemixUIGridSection> </RemixUIGridSection>
})}
</RemixUIGridView> </RemixUIGridView>
{ state.showVideo && <div
data-id={`EnterModalDialogContainer-react`}
data-backdrop="static"
data-keyboard="false"
className={"modal d-flex"}
role="dialog"
style={{ justifyContent: "center" }}
>
<div className="align-self-center pb-4" role="document">
<div
tabIndex={-1}
className={'modal-content remixModalContent mb-4'}
>
<div className="text-break remixModalBody d-flex flex-column p-3 justify-content-between" data-id={`EnterModalDialogModalBody-react`}>
<iframe style={{ minHeight: "500px", minWidth: "1000px" }} width="1000" height="500" src={"https://www.youtube.com/embed/" + this.videoID + "?si=ZdckOaSPR7VsLj_2"} allowFullScreen></iframe>
</div>
<div className="modal-footer d-flex flex-column">
<button onClick={() => {
this.showVideo = false
this.renderComponent()
}}>Close</button>
</div>
</div>
</div>
</div>}
</div>
) )
} }

@ -0,0 +1,47 @@
{
"logo": "/assets/img/YouTubeLogo.webp",
"title": "Remix Guide",
"description": "Streamlined access to categorized video tutorials for mastering Remix IDE. From fundamentals to advanced techniques, level up your development skills with ease.",
"sections": [
{
"title": "Basics",
"hScrollable": "true",
"cells": [
{
"title": "first item",
"tagList": [
"L2",
"AI"
],
"authorURL": "EatTheBlocks",
"expandViewElement": {
"videoID": "vH8T3In6ZkE",
"logo": "https://yt3.ggpht.com/9NFZbC9mkA152sSWJJgNBls6GlBdknsF-9gi6ZVk_xsHjmc82j3q1Pd5a--GCnOKUrP-YtNbHls=s48-c-k-c0x00ffffff-no-rj"
}
},
{
"title": "second item",
"tagList": [
"solidity",
"AI"
],
"expandViewElement": {
"videoID": "vH8T3In6ZkE",
"logo": "https://yt3.ggpht.com/9NFZbC9mkA152sSWJJgNBls6GlBdknsF-9gi6ZVk_xsHjmc82j3q1Pd5a--GCnOKUrP-YtNbHls=s48-c-k-c0x00ffffff-no-rj"
}
},
{
"title": "third item",
"tagList": [
"vyper",
"AI"
],
"expandViewElement": {
"videoID": "vH8T3In6ZkE",
"logo": "https://yt3.ggpht.com/9NFZbC9mkA152sSWJJgNBls6GlBdknsF-9gi6ZVk_xsHjmc82j3q1Pd5a--GCnOKUrP-YtNbHls=s48-c-k-c0x00ffffff-no-rj"
}
}
]
}
]
}

@ -47,6 +47,12 @@ export class SolCoder extends Plugin {
this.solgpt_chat_history = [] this.solgpt_chat_history = []
} }
pushChatHistory(prompt, result){
const chat:ChatEntry = [prompt, result.data[0]]
this.solgpt_chat_history.push(chat)
if (this.solgpt_chat_history.length > this.max_history){this.solgpt_chat_history.shift()}
}
async code_generation(prompt): Promise<any> { async code_generation(prompt): Promise<any> {
this.emit("aiInfering") this.emit("aiInfering")
this.call('layout', 'maximizeTerminal') this.call('layout', 'maximizeTerminal')
@ -105,9 +111,7 @@ export class SolCoder extends Plugin {
} }
if (result) { if (result) {
this.call('terminal', 'log', { type: 'aitypewriterwarning', value: result.data[0] }) this.call('terminal', 'log', { type: 'aitypewriterwarning', value: result.data[0] })
const chat:ChatEntry = [prompt, result.data[0]] this.pushChatHistory(prompt, result)
this.solgpt_chat_history.push(chat)
if (this.solgpt_chat_history.length >this.max_history){this.solgpt_chat_history.shift()}
} else if (result.error) { } else if (result.error) {
this.call('terminal', 'log', { type: 'aitypewriterwarning', value: "Error on request" }) this.call('terminal', 'log', { type: 'aitypewriterwarning', value: "Error on request" })
} }
@ -134,6 +138,7 @@ export class SolCoder extends Plugin {
).json() ).json()
if (result) { if (result) {
this.call('terminal', 'log', { type: 'aitypewriterwarning', value: result.data[0] }) this.call('terminal', 'log', { type: 'aitypewriterwarning', value: result.data[0] })
this.pushChatHistory(prompt, result)
} }
return result.data[0] return result.data[0]
} catch (e) { } catch (e) {
@ -250,6 +255,7 @@ export class SolCoder extends Plugin {
).json() ).json()
if (result) { if (result) {
this.call('terminal', 'log', { type: 'aitypewriterwarning', value: result.data[0] }) this.call('terminal', 'log', { type: 'aitypewriterwarning', value: result.data[0] })
this.pushChatHistory(prompt, result)
} }
return result.data[0] return result.data[0]
} catch (e) { } catch (e) {

@ -21,7 +21,7 @@
"editor.formatCode": "Format Code", "editor.formatCode": "Format Code",
"editor.generateDocumentation": "Generate documentation for this function", "editor.generateDocumentation": "Generate documentation for this function",
"editor.generateDocumentation2": "Generate documentation for the function \"{name}\"", "editor.generateDocumentation2": "Generate documentation for the function \"{name}\"",
"editor.generateDocumentationByAI": "solidity code: {content}\n Generate the documentation for the function {currentFunction} using the Doxygen style syntax", "editor.generateDocumentationByAI": "solidity code: {content}\n Generate the natspec documentation for the function {currentFunction} using the docstring style syntax. Only use docstring supported tags",
"editor.explainFunction": "Explain this function", "editor.explainFunction": "Explain this function",
"editor.explainFunctionSol": "Explain this code", "editor.explainFunctionSol": "Explain this code",
"editor.explainFunction2": "Explain the function \"{name}\"", "editor.explainFunction2": "Explain the function \"{name}\"",

@ -68,7 +68,7 @@
"filePanel.createNewFolder": "Create new folder", "filePanel.createNewFolder": "Create new folder",
"filePanel.publishToGist": "Publish to Gist", "filePanel.publishToGist": "Publish to Gist",
"filePanel.workspace.publishToGist": "Publish workspace to GitHub gist", "filePanel.workspace.publishToGist": "Publish workspace to GitHub gist",
"filePanel.uploadFile": "Upload files", "filePanel.uploadFile": "Open a File from your File System",
"filePanel.uploadFolder": "Upload folder", "filePanel.uploadFolder": "Upload folder",
"filePanel.updateGist": "Update Gist", "filePanel.updateGist": "Update Gist",
"filePanel.workspace.updateGist": "Publish Gist update", "filePanel.workspace.updateGist": "Publish Gist update",

@ -29,7 +29,7 @@
"home.dgitPluginDesc": "Add source control to your projects.", "home.dgitPluginDesc": "Add source control to your projects.",
"home.oneClickDappDesc": "Quickly generate smart contract interfaces", "home.oneClickDappDesc": "Quickly generate smart contract interfaces",
"home.getStarted": "Get Started", "home.getStarted": "Get Started",
"home.projectTemplates": "Project Templates", "home.projectTemplates": "Explore. Prototype. Create.",
"home.blankTemplateDesc": "Create an empty workspace.", "home.blankTemplateDesc": "Create an empty workspace.",
"home.remixDefaultTemplateDesc": "Create a workspace with sample files.", "home.remixDefaultTemplateDesc": "Create a workspace with sample files.",
"home.ozerc20TemplateDesc": "Create an ERC20 token by importing OpenZeppelin library.", "home.ozerc20TemplateDesc": "Create an ERC20 token by importing OpenZeppelin library.",
@ -39,7 +39,7 @@
"home.zeroxErc20TemplateDesc": "Create an ERC20 token by importing 0xProject contract.", "home.zeroxErc20TemplateDesc": "Create an ERC20 token by importing 0xProject contract.",
"home.learn": "Learn", "home.learn": "Learn",
"home.learnEth1": "Remix Basics", "home.learnEth1": "Remix Basics",
"home.learnEth1Desc":"An introduction to Remix's interface and basic operations.", "home.learnEth1Desc": "An introduction to Remix's interface and basic operations.",
"home.learnEth2": "Intro to Solidity", "home.learnEth2": "Intro to Solidity",
"home.learnEth2Desc": "Interactively learn Solidity beginner concepts.", "home.learnEth2Desc": "Interactively learn Solidity beginner concepts.",
"home.remixAdvanced": "Deploying with Libraries", "home.remixAdvanced": "Deploying with Libraries",
@ -56,15 +56,19 @@
"home.remixDesktop": "Remix Desktop", "home.remixDesktop": "Remix Desktop",
"home.searchDocumentation": "Search Documentation", "home.searchDocumentation": "Search Documentation",
"home.files": "Files", "home.files": "Files",
"home.newFile": "New File", "home.newFile": "New",
"home.startCoding": "Start Coding", "home.startCoding": "Start Coding",
"home.startCodingPlayground": "Open a playground for prototyping and simple learning", "home.startCodingPlayground": "Open a playground for prototyping and simple learning",
"home.openFile": "Open File", "home.openFile": "Open",
"home.openFileTooltip": "Open a File from your File System", "home.openFileTooltip": "Open a File from your File System",
"home.accessFileSystem": "Access File System", "home.accessFileSystem": "Connect to Local Filesystem",
"home.loadFrom": "Load from", "home.loadFrom": "Load from",
"home.resources": "Resources", "home.resources": "Resources",
"home.connectToLocalhost": "Connect to Localhost", "home.connectToLocalhost": "Connect to Localhost",
"home.seeAllTutorials": "See all tutorials", "home.seeAllTutorials": "See all tutorials",
"home.maintainedByRemix": "Maintained by Remix" "home.maintainedByRemix": "Maintained by Remix",
"home.gitCloneTooltip": "Clone a Github repo to a new workspace",
"home.gistTooltip": "Open Gist repo",
"home.newFileTooltip": "Add a new file to a workspace"
} }

@ -52,6 +52,19 @@
"solidity._comment_contract-selection.tsx": "libs/remix-ui/solidity-compiler/src/lib/contract-selection.tsx", "solidity._comment_contract-selection.tsx": "libs/remix-ui/solidity-compiler/src/lib/contract-selection.tsx",
"solidity.publishOn": "Publish on", "solidity.publishOn": "Publish on",
"solidity.runStaticAnalysis": "Run Remix Analysis",
"solidity.runStaticAnalysis.iconTooltip": "Click to analyze this contract for vulnerabilities using Remix, Solhint and Slither analyzers",
"solidity.runSolidityScan": "Run SolidityScan",
"solidity.solScan.iconTooltip": "Click to scan this contract for vulnerabilities using SolidityScan, a third-party provider [BETA]",
"solidity.solScan.modalTitle": "Permission to share code",
"solidity.solScan.modalMessage": "To scan and analyze the contract for risks and vulnerabilities, its code will be shared with SolidityScan, a third-party provider. ",
"solidity.solScan.likeToContinue": "Would you like to continue?",
"solidity.solScan.modalOkLabel": "Continue",
"solidity.solScan.modalCancelLabel": "Cancel",
"solidity.solScan.errModalTitle": "Scan error",
"solidity.solScan.successModalTitle": "Scan result",
"solidity.flatten": "Flatten contracts before UML generation.", "solidity.flatten": "Flatten contracts before UML generation.",
"solidity.generateUML": "Generate a UML diagram of your contract.", "solidity.generateUML": "Generate a UML diagram of your contract.",
"solidity.flattenLabel": "Flatten", "solidity.flattenLabel": "Flatten",

@ -80,15 +80,6 @@
"udapp.pinnedAt": "Pinned at", "udapp.pinnedAt": "Pinned at",
"udapp.filePath": "File path", "udapp.filePath": "File path",
"udapp.solScan.iconTooltip": "Click to scan this contract for vulnerabilities using third-party SolidityScan [BETA]",
"udapp.solScan.modalTitle": "Permission to share code",
"udapp.solScan.modalMessage": "To scan the contract for vulnerabilities & possible risks, smart contract code will be shared to third-party SolidityScan (https://solidityscan.com/).\n\n Would you like to continue?",
"udapp.solScan.modalOkLabel": "Continue",
"udapp.solScan.modalCancelLabel": "Cancel",
"udapp.solScan.errModalTitle": "Scan error",
"udapp.solScan.successModalTitle": "Scan result",
"udapp._comment_recorderCardUI.tsx": "libs/remix-ui/run-tab/src/lib/components/recorderCardUI.tsx", "udapp._comment_recorderCardUI.tsx": "libs/remix-ui/run-tab/src/lib/components/recorderCardUI.tsx",
"udapp.transactionsRecorded": "Transactions recorded", "udapp.transactionsRecorded": "Transactions recorded",
"udapp.transactionsCountTooltip": "The number of recorded transactions", "udapp.transactionsCountTooltip": "The number of recorded transactions",

@ -97,6 +97,7 @@ module.exports = class SettingsTab extends ViewPlugin {
updateCopilotChoice(isChecked) { updateCopilotChoice(isChecked) {
this.config.set('settings/copilot/suggest/activate', isChecked) this.config.set('settings/copilot/suggest/activate', isChecked)
this.useCopilot = isChecked this.useCopilot = isChecked
this.emit('copilotChoiceUpdated', isChecked)
this.dispatch({ this.dispatch({
...this ...this
}) })

@ -161,7 +161,6 @@ export class RunTab extends ViewPlugin {
await addProvider(position, name, displayName, true, false, false) await addProvider(position, name, displayName, true, false, false)
} }
const registerInjectedProvider = async (event) => { const registerInjectedProvider = async (event) => {
console.log('registerInjectedProvider', event)
const name = 'injected-' + event.detail.info.name const name = 'injected-' + event.detail.info.name
const displayName = 'Injected Provider - ' + event.detail.info.name const displayName = 'Injected Provider - ' + event.detail.info.name
await this.engine.register([new InjectedProviderDefault(event.detail.provider, name)]) await this.engine.register([new InjectedProviderDefault(event.detail.provider, name)])

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

@ -31,6 +31,7 @@ let requiredModules = [ // services + layout views + system views
'menuicons', 'menuicons',
'filePanel', 'filePanel',
'terminal', 'terminal',
'statusBar',
'settings', 'settings',
'pluginManager', 'pluginManager',
'tabs', 'tabs',
@ -78,7 +79,8 @@ let requiredModules = [ // services + layout views + system views
'remix-templates', 'remix-templates',
'solhint', 'solhint',
'pinnedPanel', 'pinnedPanel',
'pluginStateLogger' 'pluginStateLogger',
'remixGuide'
] ]
@ -117,6 +119,7 @@ export function isNative(name) {
'solhint', 'solhint',
'solidityUnitTesting', 'solidityUnitTesting',
'layout', 'layout',
'statusBar',
'notification', 'notification',
'hardhat-provider', 'hardhat-provider',
'ganache-provider', 'ganache-provider',
@ -157,7 +160,6 @@ export class RemixAppManager extends PluginManager {
if (Registry.getInstance().get('platform').api.isDesktop()) { if (Registry.getInstance().get('platform').api.isDesktop()) {
requiredModules = [...requiredModules, 'fs', 'electronTemplates', 'isogit', 'remix-templates', 'electronconfig', 'xterm', 'compilerloader', 'ripgrep'] requiredModules = [...requiredModules, 'fs', 'electronTemplates', 'isogit', 'remix-templates', 'electronconfig', 'xterm', 'compilerloader', 'ripgrep']
} }
} }
async canActivatePlugin(from, to) { async canActivatePlugin(from, to) {

@ -10,7 +10,7 @@ export class RemixEngine extends Engine {
setPluginOption ({ name, kind }) { setPluginOption ({ name, kind }) {
if (kind === 'provider') return { queueTimeout: 60000 * 2 } if (kind === 'provider') return { queueTimeout: 60000 * 2 }
if (name === 'LearnEth') return { queueTimeout: 60000 } if (name === 'LearnEth') return { queueTimeout: 60000 }
if (name === 'dGitProvider') return { queueTimeout: 60000 * 4 } if (name === 'dGitProvider') return { queueTimeout: 60000 * 30 }
if (name === 'slither') return { queueTimeout: 60000 * 4 } // Requires when a solc version is installed if (name === 'slither') return { queueTimeout: 60000 * 4 } // Requires when a solc version is installed
if (name === 'hardhat') return { queueTimeout: 60000 * 4 } if (name === 'hardhat') return { queueTimeout: 60000 * 4 }
if (name === 'truffle') return { queueTimeout: 60000 * 4 } if (name === 'truffle') return { queueTimeout: 60000 * 4 }

@ -0,0 +1,19 @@
export interface PluginProfile {
name: string
displayName: string
description: string
keywords?: string[]
icon?: string
url?: string
methods?: string[]
events?: string[]
version?: string
}
export interface StatusBarInterface {
htmlElement: HTMLDivElement
events: EventEmitter
filePanelPlugin: FilePanelType
dispatch: React.Dispatch<any>
setDispatch(dispatch: React.Dispatch<any>): void
}

@ -170,7 +170,7 @@ class CopyFileAfterBuild {
apply(compiler) { apply(compiler) {
const onEnd = async () => { const onEnd = async () => {
try { try {
console.log('runnning CopyFileAfterBuild') console.log('running CopyFileAfterBuild')
// This copy the raw-loader files used by the etherscan plugin to the remix-ide root folder. // This copy the raw-loader files used by the etherscan plugin to the remix-ide root folder.
// This is needed because by default the etherscan resources are served from the /plugins/etherscan/ folder, // This is needed because by default the etherscan resources are served from the /plugins/etherscan/ folder,
// but the raw-loader try to access the resources from the root folder. // but the raw-loader try to access the resources from the root folder.

@ -1,23 +1,19 @@
import React, {useState, useEffect, useRef} from 'react' import { useState, useEffect, useRef } from 'react'
import {remixClient} from './utils' import { remixClient } from './utils'
import {CompilationResult} from '@remixproject/plugin-api' import { CompilationResult } from '@remixproject/plugin-api'
// Components // Components
import CompilerButton from './components/CompilerButton' import CompilerButton from './components/CompilerButton'
import WarnRemote from './components/WarnRemote'
import VyperResult from './components/VyperResult' import VyperResult from './components/VyperResult'
import LocalUrlInput from './components/LocalUrl' import LocalUrlInput from './components/LocalUrl'
import ToggleButtonGroup from 'react-bootstrap/ToggleButtonGroup'
import ToggleButton from 'react-bootstrap/ToggleButton'
import Button from 'react-bootstrap/Button' import Button from 'react-bootstrap/Button'
import Accordion from 'react-bootstrap/Accordion' import Accordion from 'react-bootstrap/Accordion'
import Card from 'react-bootstrap/Card'
import './app.css' import './app.css'
import {CustomTooltip} from '@remix-ui/helper' import { CustomTooltip } from '@remix-ui/helper'
import {Form} from 'react-bootstrap' import { Form } from 'react-bootstrap'
import {CompileErrorCard} from './components/CompileErrorCard' import { CompileErrorCard } from './components/CompileErrorCard'
import CustomAccordionToggle from './components/CustomAccordionToggle' import CustomAccordionToggle from './components/CustomAccordionToggle'
interface AppState { interface AppState {
@ -89,11 +85,11 @@ const App = () => {
/** Update the environment state value */ /** Update the environment state value */
function setEnvironment(environment: 'local' | 'remote') { function setEnvironment(environment: 'local' | 'remote') {
setState({...state, environment}) setState({ ...state, environment })
} }
function setLocalUrl(url: string) { function setLocalUrl(url: string) {
setState({...state, localUrl: url}) setState({ ...state, localUrl: url })
} }
function compilerUrl() { function compilerUrl() {
@ -111,14 +107,19 @@ const App = () => {
spinnerIcon.current.classList.add('remixui_spinningIcon') spinnerIcon.current.classList.add('remixui_spinningIcon')
} }
const [toggleAccordion, setToggleAccordion] = useState(false) const [cloneCount, setCloneCount] = useState(0)
return ( return (
<main id="vyper-plugin"> <main id="vyper-plugin">
<section> <section>
<div className="px-3 pt-3 mb-3 w-100"> <div className="px-3 pt-3 mb-3 w-100">
<CustomTooltip placement="bottom" tooltipText="Clone a repo of Vyper examples. Switch to the File Explorer to see the examples."> <CustomTooltip placement="bottom" tooltipText="Clone a repo of Vyper examples. Switch to the File Explorer to see the examples.">
<Button data-id="add-repository" className="w-100 btn btn-secondary" onClick={() => remixClient.cloneVyperRepo()}> <Button data-id="add-repository" className="w-100 btn btn-secondary" onClick={() => {
{cloneCount === 0 ? remixClient.cloneVyperRepo() : remixClient.cloneVyperRepo(cloneCount)}
setCloneCount((prev) => {
return ++prev
})
}}>
Clone a repo of Vyper examples Clone a repo of Vyper examples
</Button> </Button>
</CustomTooltip> </CustomTooltip>
@ -162,7 +163,7 @@ const App = () => {
in the .vy file. in the .vy file.
</span> </span>
<div className="px-3 w-100 mb-3 mt-1" id="compile-btn"> <div className="px-3 w-100 mb-3 mt-1" id="compile-btn">
<CompilerButton compilerUrl={compilerUrl()} contract={contract} setOutput={(name, update) => setOutput({...output, [name]: update})} resetCompilerState={resetCompilerResultState} output={output} remixClient={remixClient}/> <CompilerButton compilerUrl={compilerUrl()} contract={contract} setOutput={(name, update) => setOutput({ ...output, [name]: update })} resetCompilerState={resetCompilerResultState} output={output} remixClient={remixClient}/>
</div> </div>
<article id="result" className="p-2 mx-3 border-top mt-2"> <article id="result" className="p-2 mx-3 border-top mt-2">

@ -1,9 +1,8 @@
import { ABIDescription} from '@remixproject/plugin-api' import { ABIDescription } from '@remixproject/plugin-api'
import axios from 'axios' import axios from 'axios'
import { remixClient } from './remix-client' import { remixClient } from './remix-client'
import _ from 'lodash' import _ from 'lodash'
export interface Contract { export interface Contract {
name: string name: string
content: string content: string
@ -72,8 +71,8 @@ const buildError = (output) => {
const line = output.line const line = output.line
if (line) { if (line) {
const lineColumnPos = { const lineColumnPos = {
start: {line: line - 1, column: 10}, start: { line: line - 1, column: 10 },
end: {line: line - 1, column: 10} end: { line: line - 1, column: 10 }
} }
// remixClient.highlight(lineColumnPos as any, _contract.name, '#e0b4b4') // remixClient.highlight(lineColumnPos as any, _contract.name, '#e0b4b4')
} else { } else {
@ -92,8 +91,8 @@ const buildError = (output) => {
} }
if (location?.length > 0) { if (location?.length > 0) {
const lineColumnPos = { const lineColumnPos = {
start: {line: parseInt(location[0]) - 1, column: 10}, start: { line: parseInt(location[0]) - 1, column: 10 },
end: {line: parseInt(location[0]) - 1, column: 10} end: { line: parseInt(location[0]) - 1, column: 10 }
} }
// remixClient.highlight(lineColumnPos as any, _contract.name, message) // remixClient.highlight(lineColumnPos as any, _contract.name, message)
} }
@ -175,13 +174,11 @@ export async function compile(url: string, contract: Contract): Promise<any> {
throw new Error('Use extension .vy for Vyper.') throw new Error('Use extension .vy for Vyper.')
} }
let contractName = contract['name'] let contractName = contract['name']
const compilePackage = { const compilePackage = {
manifest: 'ethpm/3', manifest: 'ethpm/3',
sources: { sources: {
[contractName] : {content : fixContractContent(contract.content)} [contractName] : { content : fixContractContent(contract.content) }
} }
} }
let response = await axios.post(`${url}compile`, compilePackage ) let response = await axios.post(`${url}compile`, compilePackage )
@ -266,7 +263,6 @@ export function toStandardOutput(fileName: string, compilationResult: any): any
} }
} }
export async function compileContract(contract: string, compilerUrl: string, setOutput?: any, setLoadingSpinnerState?: React.Dispatch<React.SetStateAction<boolean>>, spinner?: boolean) { export async function compileContract(contract: string, compilerUrl: string, setOutput?: any, setLoadingSpinnerState?: React.Dispatch<React.SetStateAction<boolean>>, spinner?: boolean) {
remixClient.eventEmitter.emit('resetCompilerState', {}) remixClient.eventEmitter.emit('resetCompilerState', {})
spinner && spinner === true ? setLoadingSpinnerState && setLoadingSpinnerState(true) : null spinner && spinner === true ? setLoadingSpinnerState && setLoadingSpinnerState(true) : null
@ -301,7 +297,7 @@ export async function compileContract(contract: string, compilerUrl: string, set
}) })
setLoadingSpinnerState && setLoadingSpinnerState(false) setLoadingSpinnerState && setLoadingSpinnerState(false)
remixClient.eventEmitter.emit('setOutput', {status: 'failed', message: output.message, title: 'Error compiling...', line: output.line, column: output.column, key: 1 }) remixClient.eventEmitter.emit('setOutput', { status: 'failed', message: output.message, title: 'Error compiling...', line: output.line, column: output.column, key: 1 })
output = null output = null
return return
} }
@ -332,13 +328,10 @@ export async function compileContract(contract: string, compilerUrl: string, set
}) })
setLoadingSpinnerState && setLoadingSpinnerState(false) setLoadingSpinnerState && setLoadingSpinnerState(false)
remixClient.eventEmitter.emit('setOutput', {status: 'failed', message: err.message}) remixClient.eventEmitter.emit('setOutput', { status: 'failed', message: err.message })
} }
} }
export type StandardOutput = { export type StandardOutput = {
sources: { sources: {
[fileName: string]: { [fileName: string]: {

@ -1,9 +1,9 @@
import {HighlightPosition, CompilationResult, RemixApi, customAction} from '@remixproject/plugin-api' import { HighlightPosition, CompilationResult, RemixApi, customAction } from '@remixproject/plugin-api'
import {Api, Status} from '@remixproject/plugin-utils' import { Api, Status } from '@remixproject/plugin-utils'
import {createClient} from '@remixproject/plugin-webview' import { createClient } from '@remixproject/plugin-webview'
import {PluginClient} from '@remixproject/plugin' import { PluginClient } from '@remixproject/plugin'
import {Contract, compileContract} from './compiler' import { Contract, compileContract } from './compiler'
import {ExampleContract} from '../components/VyperResult' import { ExampleContract } from '../components/VyperResult'
import EventEmitter from 'events' import EventEmitter from 'events'
export type VyperComplierAddress = 'https://vyper2.remixproject.org/' | 'http://localhost:8000/' export type VyperComplierAddress = 'https://vyper2.remixproject.org/' | 'http://localhost:8000/'
@ -49,7 +49,7 @@ export class RemixClient extends PluginClient {
} }
/** Load Ballot contract example into the file manager */ /** Load Ballot contract example into the file manager */
async loadContract({name, address}: ExampleContract) { async loadContract({ name, address }: ExampleContract) {
try { try {
const content = await this.client.call('contentImport', 'resolve', address) const content = await this.client.call('contentImport', 'resolve', address)
await this.client.call('fileManager', 'setFile', content.cleanUrl, content.content) await this.client.call('fileManager', 'setFile', content.cleanUrl, content.content)
@ -77,23 +77,24 @@ export class RemixClient extends PluginClient {
} }
} }
async cloneVyperRepo() { async cloneVyperRepo(count?: number) {
try { try {
// @ts-ignore // @ts-ignore
this.call('notification', 'toast', 'cloning Snekmate Vyper repository...') this.call('notification', 'toast', 'cloning Vyper-lang repository...')
await this.call('manager', 'activatePlugin', 'dGitProvider') await this.call('manager', 'activatePlugin', 'dGitProvider')
await this.call( await this.call(
'dGitProvider', 'dGitProvider',
'clone', 'clone',
{url: 'https://github.com/pcaversaccio/snekmate', token: null, branch: 'v0.0.5'}, { url: 'https://github.com/vyperlang/vyper', token: null, branch: 'v0.3.10' },
// @ts-ignore // @ts-ignore
'snekmate' (count === undefined || count === 0) ? 'vyper-lang' : `vyper-lang_${count}`
) )
this.call( this.call(
// @ts-ignore // @ts-ignore
'notification', 'notification',
'toast', 'toast',
'Snekmate Vyper repository cloned, the workspace snekmate has been created.' 'Vyper repository cloned, the workspace Vyper has been created.'
) )
} catch (e) { } catch (e) {
// @ts-ignore // @ts-ignore

@ -2,12 +2,12 @@ const fs = require('fs')
let value = fs.readFileSync('./done.json') let value = fs.readFileSync('./done.json')
value = JSON.parse(value) value = JSON.parse(value)
const inDone = value.data.search.edges[0].node.project.columns.edges[0].node.cards.edges const inDone = value.data.repository.projectV2.items.edges
let data = '' let data = ''
console.log(inDone.length, 'issues/Prs\n') console.log(inDone.length, 'PRs\n')
data = inDone.length + ' issues/Prs\n' data = inDone.length + ' PRs\n'
for (let card of inDone) { for (let card of inDone) {
if (card.node.content.url && card.node.content.merged !== false) { if (card.node.content.url && card.node.content.merged && card.node.content.merged !== false) {
data += `${card.node.content.title} - ${card.node.content.url}\n` data += `${card.node.content.title} - ${card.node.content.url}\n`
} }
} }
@ -24,37 +24,17 @@ console.log('done.txt updated')
- get the result in the file done.txt - get the result in the file done.txt
/* /*
{ {
search(type: REPOSITORY, query: "remix-project", first: 1) { repository(owner: "ethereum", name: "remix-project") {
edges {
node {
__typename
... on Repository {
owner {
id
}
name
project(number: 31) {
number
name
columns(last: 1) {
edges {
node {
name name
cards(first: 100) { projectV2(number: 52) {
url
items(first: 100) {
totalCount
edges { edges {
cursor
node { node {
id
note
state
content { content {
... on Issue {
url
id
number
title
}
... on PullRequest { ... on PullRequest {
url url
id id
@ -68,11 +48,5 @@ console.log('done.txt updated')
} }
} }
} }
}
}
}
}
}
}
} }
*/ */

@ -1,11 +1,17 @@
import { signTypedData, SignTypedDataVersion, TypedMessage, MessageTypes } from '@metamask/eth-sig-util'
import { privateToAddress, toChecksumAddress, isValidPrivate, Address, toBytes, bytesToHex, Account } from '@ethereumjs/util' import { privateToAddress, toChecksumAddress, isValidPrivate, Address, toBytes, bytesToHex, Account } from '@ethereumjs/util'
import { privateKeyToAccount } from 'web3-eth-accounts' import { privateKeyToAccount } from 'web3-eth-accounts'
import { toBigInt } from 'web3-utils' import { toBigInt } from 'web3-utils'
import * as crypto from 'crypto' import * as crypto from 'crypto'
type AccountType = {
nonce: number,
privateKey: Uint8Array
}
export class Web3Accounts { export class Web3Accounts {
accounts: Record<string, unknown> accounts: Record<string, AccountType>
accountsKeys: Record<string, unknown> accountsKeys: Record<string, string>
vmContext vmContext
constructor (vmContext) { constructor (vmContext) {
@ -73,7 +79,9 @@ export class Web3Accounts {
eth_accounts: this.eth_accounts.bind(this), eth_accounts: this.eth_accounts.bind(this),
eth_getBalance: this.eth_getBalance.bind(this), eth_getBalance: this.eth_getBalance.bind(this),
eth_sign: this.eth_sign.bind(this), eth_sign: this.eth_sign.bind(this),
eth_chainId: this.eth_chainId.bind(this) eth_chainId: this.eth_chainId.bind(this),
eth_signTypedData: this.eth_signTypedData_v4.bind(this), // default call is using V4
eth_signTypedData_v4: this.eth_signTypedData_v4.bind(this)
} }
} }
@ -108,4 +116,49 @@ export class Web3Accounts {
eth_chainId (_payload, cb) { eth_chainId (_payload, cb) {
return cb(null, '0x539') // 0x539 is hex of 1337 return cb(null, '0x539') // 0x539 is hex of 1337
} }
eth_signTypedData_v4 (payload, cb) {
const address: string = payload.params[0]
const typedData: TypedMessage<MessageTypes> = payload.params[1]
try {
if (this.accounts[toChecksumAddress(address)] == null) {
throw new Error("cannot sign data; no private key");
}
if (typeof typedData === "string") {
throw new Error("cannot sign data; string sent, expected object");
}
if (!typedData.types) {
throw new Error("cannot sign data; types missing");
}
if (!typedData.types.EIP712Domain) {
throw new Error("cannot sign data; EIP712Domain definition missing");
}
if (!typedData.domain) {
throw new Error("cannot sign data; domain missing");
}
if (!typedData.primaryType) {
throw new Error("cannot sign data; primaryType missing");
}
if (!typedData.message) {
throw new Error("cannot sign data; message missing");
}
const ret = signTypedData({
privateKey: Buffer.from(this.accounts[toChecksumAddress(address)].privateKey),
data: typedData,
version: SignTypedDataVersion.V4
})
cb(null, ret)
} catch (e) {
cb(e.message)
}
}
} }

@ -11,7 +11,6 @@ import { Transactions } from './methods/transactions'
import { Debug } from './methods/debug' import { Debug } from './methods/debug'
import { VMContext } from './vm-context' import { VMContext } from './vm-context'
import { Web3PluginBase } from 'web3' import { Web3PluginBase } from 'web3'
import { Block } from '@ethereumjs/block'
export interface JSONRPCRequestPayload { export interface JSONRPCRequestPayload {
params: any[]; params: any[];
@ -109,11 +108,11 @@ export class Provider {
callback(new Error('unknown method ' + payload.method)) callback(new Error('unknown method ' + payload.method))
} }
sendAsync (payload: JSONRPCRequestPayload, callback: (err: Error, result?: JSONRPCResponsePayload) => void) { async sendAsync (payload: JSONRPCRequestPayload, callback?: (err: Error, result?: JSONRPCResponsePayload) => void) : Promise<JSONRPCResponsePayload> {
return new Promise((resolve,reject)=>{ return new Promise((resolve,reject)=>{
const cb = (err, result) => { const cb = (err, result) => {
if (typeof callback==='function'){ if (typeof callback==='function'){
callback(err,result) return callback(err, result)
} }
if (err){ if (err){
return reject(err) return reject(err)
@ -125,7 +124,12 @@ export class Provider {
} }
send (payload, callback) { send (payload, callback) {
return this.sendAsync(payload,callback) this.sendAsync(payload, callback)
}
async request (payload: JSONRPCRequestPayload) : Promise<any> {
const ret = await this.sendAsync(payload)
return ret.result
} }
isConnected () { isConnected () {

@ -39,4 +39,40 @@ describe('Accounts', () => {
assert.deepEqual(typeof signature === 'string' ? signature.length : signature.signature.length, 132) assert.deepEqual(typeof signature === 'string' ? signature.length : signature.signature.length, 132)
}) })
}) })
describe('eth_signTypedData', () => {
it('should sign typed data', async () => {
const accounts: string[] = await web3.eth.getAccounts()
const typedData = {
domain: {
chainId: 1,
name: "Example App",
verifyingContract: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC",
version: "1",
},
message: {
prompt: "Welcome! In order to authenticate to this website, sign this request and your public address will be sent to the server in a verifiable way.",
createdAt: 1718570375196,
},
primaryType: 'AuthRequest',
types: {
EIP712Domain: [
{ name: 'name', type: 'string' },
{ name: 'version', type: 'string' },
{ name: 'chainId', type: 'uint256' },
{ name: 'verifyingContract', type: 'address' },
],
AuthRequest: [
{ name: 'prompt', type: 'string' },
{ name: 'createdAt', type: 'uint256' },
],
},
};
const result = await web3.currentProvider.request({
method: 'eth_signTypedData',
params: [accounts[0], typedData]
})
assert.equal(result, '0x248d23de0e23231370db8aa21ad5908ca90c33ae2b8c611b906674bda6b1a8b85813f945c2ea896316e240089029619ab3d801a1b098c199bd462dd8026349da1c')
})
})
}) })

@ -23,7 +23,18 @@ const DragBar = (props: IRemixDragBarUi) => {
if (props.hidden) { if (props.hidden) {
setDragBarPosX(offset) setDragBarPosX(offset)
} else if (props.layoutPosition === 'left') { } else if (props.layoutPosition === 'left') {
setDragBarPosX(offset + props.refObject.current.offsetWidth) const checkResolution = () => {
const width = window.innerWidth
const height = window.innerHeight
if (height <= 781 && width <= 1150) {
setDragBarPosX(props.minWidth - 50)
} else {
setDragBarPosX(props.minWidth + 50)
}
}
checkResolution()
props.refObject.current.style.width = props.minWidth + 'px'
} else if (props.layoutPosition === 'right') { } else if (props.layoutPosition === 'right') {
setDragBarPosX(offset) setDragBarPosX(offset)
} }

@ -217,8 +217,11 @@ const RemixApp = (props: IRemixAppUi) => {
layoutPosition='right' layoutPosition='right'
></DragBar> ></DragBar>
} }
</div>
<div>{props.app.hiddenPanel.render()}</div> <div>{props.app.hiddenPanel.render()}</div>
<div className="statusBar fixed-bottom">
{props.app.statusBar.render()}
</div>
</div>
<AppDialogs></AppDialogs> <AppDialogs></AppDialogs>
<DialogViewPlugin></DialogViewPlugin> <DialogViewPlugin></DialogViewPlugin>
</AppProvider> </AppProvider>

@ -20,6 +20,7 @@ pre {
overflow : hidden; overflow : hidden;
flex : 1; flex : 1;
min-width : 320px; min-width : 320px;
padding-bottom : 1.4rem;
} }
.iconpanel { .iconpanel {
display : flex; display : flex;
@ -27,12 +28,17 @@ pre {
overflow : hidden; overflow : hidden;
width : 50px; width : 50px;
user-select : none; user-select : none;
padding-bottom : 1.4rem;
} }
.sidepanel { .sidepanel {
display : flex; display : flex;
flex-direction : row-reverse; flex-direction : row-reverse;
width : 320px; width : 320px;
transition : width 0.25s; transition : width 0.25s;
padding-bottom : 1.4rem;
}
.statusBar {
} }
.pinnedpanel { .pinnedpanel {
width : 320px; width : 320px;

@ -0,0 +1,37 @@
/* eslint-disable no-control-regex */
import { EditorUIProps, monacoTypes } from '@remix-ui/editor';
export class RemixSolidityDocumentationProvider implements monacoTypes.languages.InlineCompletionsProvider{
props:EditorUIProps
monaco:any
completion:string
constructor(completion: any){
this.completion = completion
}
async provideInlineCompletions(model: monacoTypes.editor.ITextModel, position: monacoTypes.Position, context: monacoTypes.languages.InlineCompletionContext, token: monacoTypes.CancellationToken): Promise<monacoTypes.languages.InlineCompletions<monacoTypes.languages.InlineCompletion>> {
const item: monacoTypes.languages.InlineCompletion = {
insertText: this.completion
};
return {
items: [item],
enableForwardStability: true
}
}
handleItemDidShow?(completions: monacoTypes.languages.InlineCompletions<monacoTypes.languages.InlineCompletion>, item: monacoTypes.languages.InlineCompletion, updatedInsertText: string): void {
}
handlePartialAccept?(completions: monacoTypes.languages.InlineCompletions<monacoTypes.languages.InlineCompletion>, item: monacoTypes.languages.InlineCompletion, acceptedCharacters: number): void {
}
freeInlineCompletions(completions: monacoTypes.languages.InlineCompletions<monacoTypes.languages.InlineCompletion>): void {
}
groupId?: string;
yieldsToGroupIds?: string[];
toString?(): string {
throw new Error('Method not implemented.');
}
}

@ -16,6 +16,7 @@ import { retrieveNodesAtPosition } from './helpers/retrieveNodesAtPosition'
import { RemixHoverProvider } from './providers/hoverProvider' import { RemixHoverProvider } from './providers/hoverProvider'
import { RemixReferenceProvider } from './providers/referenceProvider' import { RemixReferenceProvider } from './providers/referenceProvider'
import { RemixCompletionProvider } from './providers/completionProvider' import { RemixCompletionProvider } from './providers/completionProvider'
import { RemixSolidityDocumentationProvider } from './providers/documentationProvider'
import { RemixHighLightProvider } from './providers/highlightProvider' import { RemixHighLightProvider } from './providers/highlightProvider'
import { RemixDefinitionProvider } from './providers/definitionProvider' import { RemixDefinitionProvider } from './providers/definitionProvider'
import { RemixCodeActionProvider } from './providers/codeActionProvider' import { RemixCodeActionProvider } from './providers/codeActionProvider'
@ -23,6 +24,7 @@ import './remix-ui-editor.css'
import { circomLanguageConfig, circomTokensProvider } from './syntaxes/circom' import { circomLanguageConfig, circomTokensProvider } from './syntaxes/circom'
import { IPosition } from 'monaco-editor' import { IPosition } from 'monaco-editor'
import { RemixInLineCompletionProvider } from './providers/inlineCompletionProvider' import { RemixInLineCompletionProvider } from './providers/inlineCompletionProvider'
import { providers } from 'ethers'
const _paq = (window._paq = window._paq || []) const _paq = (window._paq = window._paq || [])
enum MarkerSeverity { enum MarkerSeverity {
@ -174,6 +176,8 @@ export const EditorUI = (props: EditorUIProps) => {
const currentFunction = useRef('') const currentFunction = useRef('')
const currentFileRef = useRef('') const currentFileRef = useRef('')
const currentUrlRef = useRef('') const currentUrlRef = useRef('')
let currenFunctionNode = useRef('')
// const currentDecorations = useRef({ sourceAnnotationsPerFile: {}, markerPerFile: {} }) // decorations that are currently in use by the editor // const currentDecorations = useRef({ sourceAnnotationsPerFile: {}, markerPerFile: {} }) // decorations that are currently in use by the editor
// const registeredDecorations = useRef({}) // registered decorations // const registeredDecorations = useRef({}) // registered decorations
@ -713,17 +717,62 @@ export const EditorUI = (props: EditorUIProps) => {
} }
let gptGenerateDocumentationAction let gptGenerateDocumentationAction
const extractNatspecComments = (codeString: string): string => {
const natspecCommentRegex = /\/\*\*[\s\S]*?\*\//g;
const comments = codeString.match(natspecCommentRegex);
return comments ? comments[0] : "";
}
const executeGptGenerateDocumentationAction = { const executeGptGenerateDocumentationAction = {
id: 'generateDocumentation', id: 'generateDocumentation',
label: intl.formatMessage({ id: 'editor.generateDocumentation' }), label: intl.formatMessage({ id: 'editor.generateDocumentation' }),
contextMenuOrder: 0, // choose the order contextMenuOrder: 0, // choose the order
contextMenuGroupId: 'gtp', // create a new grouping contextMenuGroupId: 'gtp', // create a new grouping
keybindings: [], keybindings: [
// Keybinding for Ctrl + D
monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyCode.KeyD
],
run: async () => { run: async () => {
const unsupportedDocTags = ['@title'] // these tags are not supported by the current docstring parser
const file = await props.plugin.call('fileManager', 'getCurrentFile') const file = await props.plugin.call('fileManager', 'getCurrentFile')
const content = await props.plugin.call('fileManager', 'readFile', file) const content = await props.plugin.call('fileManager', 'readFile', file)
const message = intl.formatMessage({ id: 'editor.generateDocumentationByAI' }, { content, currentFunction: currentFunction.current }) const message = intl.formatMessage({ id: 'editor.generateDocumentationByAI' }, { content, currentFunction: currentFunction.current })
await props.plugin.call('solcoder', 'code_explaining', message) const cm = await props.plugin.call('solcoder', 'code_explaining', message)
const natSpecCom = "\n" + extractNatspecComments(cm)
const cln = await props.plugin.call('codeParser', "getLineColumnOfNode", currenFunctionNode)
const range = new monacoRef.current.Range(cln.start.line, cln.start.column, cln.start.line, cln.start.column)
const lines = natSpecCom.split('\n')
const newNatSpecCom = []
for (let i = 0; i < lines.length; i++) {
let cont = false
for (let j = 0; j < unsupportedDocTags.length; j++) {
if (lines[i].includes(unsupportedDocTags[j])) {
cont = true
break
}
}
if (cont) {continue}
if (i <= 1) { newNatSpecCom.push(' '.repeat(cln.start.column) + lines[i].trimStart()) }
else { newNatSpecCom.push(' '.repeat(cln.start.column + 1) + lines[i].trimStart()) }
}
// TODO: activate the provider to let the user accept the documentation suggestion
// const provider = new RemixSolidityDocumentationProvider(natspecCom)
// monacoRef.current.languages.registerInlineCompletionsProvider('solidity', provider)
editor.executeEdits('clipboard', [
{
range: range,
text: newNatSpecCom.join('\n'),
forceMoveMarkers: true,
},
]);
_paq.push(['trackEvent', 'ai', 'solcoder', 'generateDocumentation']) _paq.push(['trackEvent', 'ai', 'solcoder', 'generateDocumentation'])
}, },
} }
@ -734,7 +783,10 @@ export const EditorUI = (props: EditorUIProps) => {
label: intl.formatMessage({ id: 'editor.explainFunction' }), label: intl.formatMessage({ id: 'editor.explainFunction' }),
contextMenuOrder: 1, // choose the order contextMenuOrder: 1, // choose the order
contextMenuGroupId: 'gtp', // create a new grouping contextMenuGroupId: 'gtp', // create a new grouping
keybindings: [], keybindings: [
// Keybinding for Ctrl + Shift + E
monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyMod.Shift | monacoRef.current.KeyCode.KeyE
],
run: async () => { run: async () => {
const file = await props.plugin.call('fileManager', 'getCurrentFile') const file = await props.plugin.call('fileManager', 'getCurrentFile')
const content = await props.plugin.call('fileManager', 'readFile', file) const content = await props.plugin.call('fileManager', 'readFile', file)
@ -750,7 +802,10 @@ export const EditorUI = (props: EditorUIProps) => {
label: intl.formatMessage({ id: 'editor.explainFunctionSol' }), label: intl.formatMessage({ id: 'editor.explainFunctionSol' }),
contextMenuOrder: 1, // choose the order contextMenuOrder: 1, // choose the order
contextMenuGroupId: 'sol-gtp', // create a new grouping contextMenuGroupId: 'sol-gtp', // create a new grouping
keybindings: [], keybindings: [
// Keybinding for Ctrl + E
monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyCode.KeyE
],
run: async () => { run: async () => {
const file = await props.plugin.call('fileManager', 'getCurrentFile') const file = await props.plugin.call('fileManager', 'getCurrentFile')
const content = await props.plugin.call('fileManager', 'readFile', file) const content = await props.plugin.call('fileManager', 'readFile', file)
@ -836,6 +891,8 @@ export const EditorUI = (props: EditorUIProps) => {
const functionImpl = nodesAtPosition.find((node) => node.kind === 'function') const functionImpl = nodesAtPosition.find((node) => node.kind === 'function')
if (functionImpl) { if (functionImpl) {
currentFunction.current = functionImpl.name currentFunction.current = functionImpl.name
currenFunctionNode = functionImpl
executeGptGenerateDocumentationAction.label = intl.formatMessage({ id: 'editor.generateDocumentation2' }, { name: functionImpl.name }) executeGptGenerateDocumentationAction.label = intl.formatMessage({ id: 'editor.generateDocumentation2' }, { name: functionImpl.name })
gptGenerateDocumentationAction = editor.addAction(executeGptGenerateDocumentationAction) gptGenerateDocumentationAction = editor.addAction(executeGptGenerateDocumentationAction)
executegptExplainFunctionAction.label = intl.formatMessage({ id: 'editor.explainFunction2' }, { name: functionImpl.name }) executegptExplainFunctionAction.label = intl.formatMessage({ id: 'editor.explainFunction2' }, { name: functionImpl.name })

@ -16,15 +16,16 @@ export const CustomCheckbox = (props: CustomCheckboxProps) => {
if (!textColor || textColor == '') textColor = filterCon.keyValueMap[props.label].color if (!textColor || textColor == '') textColor = filterCon.keyValueMap[props.label].color
return ( return (
<div id={textColor + props.label} className="h-80 mx-1 align-items-center custom-control custom-checkbox" style={{ minWidth: '4rem' }}> <div id={textColor + props.label}
className="h-80 mx-1 align-items-center custom-control custom-checkbox"
style={{ minWidth: '4rem' }}
>
<input <input
className="custom-control-input" className="custom-control-input"
id={"GVCheckbox" + props.label} id={"GVCheckbox" + props.label}
defaultChecked={defChecked} defaultChecked={defChecked}
onChange={e => { onChange={e => {
if (props.label == 'no tag') filterCon.updateValue(props.label, e.target.checked, textColor)}}
filterCon.showUntagged = ! filterCon.showUntagged
else filterCon.updateValue(props.label, e.target.checked, textColor)}}
type="checkbox" type="checkbox"
/> />
<label <label

@ -3,9 +3,10 @@ import React, { createContext, useState, useContext } from 'react';
interface FilterContextType { interface FilterContextType {
showUntagged: boolean showUntagged: boolean
showPin: boolean showPin: boolean
keyValueMap: Record<string, { enabled: boolean; color: string; }>; keyValueMap: Record<string, { enabled: boolean; color: string; }>
updateValue: (key: string, enabled: boolean, color: string) => void updateValue: (key: string, enabled: boolean, color: string) => void
addValue: (key: string, enabled: boolean, color: string) => void addValue: (key: string, enabled: boolean, color: string) => void
filter: string
} }
const FiltersContext = createContext<FilterContextType>({ const FiltersContext = createContext<FilterContextType>({
showUntagged: false, showUntagged: false,
@ -13,6 +14,7 @@ const FiltersContext = createContext<FilterContextType>({
keyValueMap: {}, keyValueMap: {},
updateValue: () => {}, updateValue: () => {},
addValue: () => {}, addValue: () => {},
filter: ""
}); });
export default FiltersContext export default FiltersContext

@ -38,6 +38,12 @@
top: 0.1rem; top: 0.1rem;
} }
.remixui_grid_cell_tags_no_pin {
position: relative;
right: 0rem;
top: 0.1rem;
}
.remixui_grid_cell_tag { .remixui_grid_cell_tag {
font-size: x-small; font-size: x-small;
font-weight: bolder; font-weight: bolder;

@ -1,4 +1,4 @@
import React, {useState, useEffect, useContext, useRef, ReactNode} from 'react' // eslint-disable-line import React, {useState, useEffect, useContext, useRef, ReactNode, ReactHTMLElement} from 'react' // eslint-disable-line
import './remix-ui-grid-cell.css' import './remix-ui-grid-cell.css'
import FiltersContext from "./filtersContext" import FiltersContext from "./filtersContext"
@ -16,26 +16,51 @@ interface RemixUIGridCellProps {
pinned?: boolean pinned?: boolean
pinStateCallback?: any pinStateCallback?: any
logo?: string logo?: string
title?: string title: string
tagList?: string[] // max 8, others will be ignored tagList?: string[] // max 8, others will be ignored
classList?: string classList?: string
styleList?: any styleList?: any
children?: ReactNode children?: ReactNode
expandViewEl?: any
handleExpand?: any
} }
export const RemixUIGridCell = (props: RemixUIGridCellProps) => { export const RemixUIGridCell = (props: RemixUIGridCellProps) => {
const filterCon = useContext(FiltersContext) const filterCon = useContext(FiltersContext)
const [anyEnabled, setAnyEnabled] = useState(false) const [anyEnabled, setAnyEnabled] = useState(false)
const [expand, setExpand] = useState(false)
const [pinned, setPinned] = useState<boolean>(props.pinned) const [pinned, setPinned] = useState<boolean>(props.pinned)
useEffect(() => { useEffect(() => {
if (props.tagList) setAnyEnabled(props.tagList.some((key) => filterCon.keyValueMap[key]?.enabled)) if (props.tagList) setAnyEnabled(props.tagList.some((key) => filterCon.keyValueMap[key]?.enabled))
else setAnyEnabled(filterCon.showUntagged) else setAnyEnabled(filterCon?.keyValueMap['no tag']?.enabled)
if (filterCon.filter != '') setAnyEnabled(anyEnabled && props.title.toLowerCase().includes(filterCon.filter))
console.log("pin ", pinned)
}, [filterCon, props.tagList]) }, [filterCon, props.tagList])
/*const listenOnExpand = (key) => {
if (key === props.key) setExpand(props.toggleExpandView)
console.log('expand ----> ', key)
}
// The expanded widged should go to the grid-segment and be updated based on the expandedItem state variable of the plugin.
// The state var will work like theme dispattching is working.
useEffect(() => {
// TODO should be refactored to update based on state of plugin.
props.plugin.on(props.plugin.name, 'expandGridCell', listenOnExpand)
}, [])
*/
return ( return (
<div className='mr-2 mt-3'> <div className='mr-2 mt-3' onClick={() => {
{ anyEnabled && <div className='d-flex flex-grid'> if (props.expandViewEl)
props.handleExpand(!expand)
else return
}}>
{ anyEnabled && <div className='d-flex flex-column'>
<div className='d-flex flex-grid'>
<div className={"d-flex mx-0 p-2 bg-light border border-secondary remixui_grid_cell_container " + props.classList || ''} data-id={"remixUIGS" + props.title}> <div className={"d-flex mx-0 p-2 bg-light border border-secondary remixui_grid_cell_container " + props.classList || ''} data-id={"remixUIGS" + props.title}>
<div className="d-flex remixui_grid_cell flex-column"> <div className="d-flex remixui_grid_cell flex-column">
<div className='d-flex flex-row pb-1 align-items-end' style={{ width: '8rem', height: '1rem' }}> <div className='d-flex flex-row pb-1 align-items-end' style={{ width: '8rem', height: '1rem' }}>
@ -57,7 +82,7 @@ export const RemixUIGridCell = (props: RemixUIGridCellProps) => {
props.pinStateCallback() props.pinStateCallback()
}} }}
></button>} ></button>}
{ props.tagList && <div className='d-flex flex-column align-items-begin remixui_grid_cell_tags'> { props.tagList && <div className={`d-flex flex-column align-items-begin ` +`${filterCon.showPin ? 'remixui_grid_cell_tags' : 'remixui_grid_cell_tags_no_pin'}`}>
{ Object.keys(props.tagList).map((key) => ( { Object.keys(props.tagList).map((key) => (
filterCon.keyValueMap[props.tagList[key]].enabled && ( filterCon.keyValueMap[props.tagList[key]].enabled && (
<CustomTooltip <CustomTooltip
@ -77,6 +102,11 @@ export const RemixUIGridCell = (props: RemixUIGridCellProps) => {
{ !props.tagList && <span { !props.tagList && <span
className={'remixui_grid_cell_tags'}> className={'remixui_grid_cell_tags'}>
</span> } </span> }
</div>
{ expand && <div>
{ props.expandViewEl }
</div>
}
</div> } </div> }
</div> </div>
) )

@ -16,6 +16,7 @@ interface RemixUIGridSectionProps {
classList?: string classList?: string
styleList?: any styleList?: any
children?: ReactNode children?: ReactNode
expandedCell?: any
} }
export const RemixUIGridSection = (props: RemixUIGridSectionProps) => { export const RemixUIGridSection = (props: RemixUIGridSectionProps) => {
@ -30,6 +31,10 @@ export const RemixUIGridSection = (props: RemixUIGridSectionProps) => {
<div className={(props.hScrollable) ? `d-flex flex-row pb-2 overflow-auto` : `d-flex flex-wrap`}> <div className={(props.hScrollable) ? `d-flex flex-row pb-2 overflow-auto` : `d-flex flex-wrap`}>
{ props.children } { props.children }
</div> </div>
{ props.expandedCell && <div>
{ props.expandedCell }
</div>
}
</div> </div>
</div> </div>
) )

@ -1,7 +1,6 @@
import React, {useState, useEffect, useContext, useRef, ReactNode} from 'react' // eslint-disable-line import React, {useState, useEffect, useContext, useRef, ReactNode} from 'react' // eslint-disable-line
import './remix-ui-grid-view.css' import './remix-ui-grid-view.css'
import { ThemeContext, themes } from './themeContext'
import CustomCheckbox from './components/customCheckbox' import CustomCheckbox from './components/customCheckbox'
import FiltersContext from "./filtersContext" import FiltersContext from "./filtersContext"
@ -28,7 +27,7 @@ interface RemixUIGridViewProps {
export const RemixUIGridView = (props: RemixUIGridViewProps) => { export const RemixUIGridView = (props: RemixUIGridViewProps) => {
const [keyValueMap, setKeyValueMap] = useState<Record<string, { enabled: boolean; color: string; }>>({}); const [keyValueMap, setKeyValueMap] = useState<Record<string, { enabled: boolean; color: string; }>>({});
const [filter, setFilter] = useState("")
const showUntagged = props.showUntagged || false const showUntagged = props.showUntagged || false
const showPin = props.showPin || false const showPin = props.showPin || false
const updateValue = (key: string, enabled: boolean, color?: string) => { const updateValue = (key: string, enabled: boolean, color?: string) => {
@ -39,6 +38,30 @@ export const RemixUIGridView = (props: RemixUIGridViewProps) => {
})) }))
} }
const [state, setState] = useState<{
searchDisable: boolean
}>({
searchDisable: true
})
const searchInputRef = useRef(null)
const handleSearchKeyDown = (e: KeyboardEvent) => {
if (e.target !== searchInputRef.current) return
if (e.key === 'Enter') {
searchInputRef.current.value = ''
} else {
setState((prevState) => {
console.log("update filter", searchInputRef.current.value)
return {
...prevState,
searchDisable: searchInputRef.current.value === '',
filter: searchInputRef.current.value
}
})
setFilter(searchInputRef.current.value)
}
}
const addValue = (key: string, enabled: boolean, color: string) => { const addValue = (key: string, enabled: boolean, color: string) => {
// Check if the key already exists, if so, do not add // Check if the key already exists, if so, do not add
if (key in keyValueMap) { if (key in keyValueMap) {
@ -52,17 +75,10 @@ export const RemixUIGridView = (props: RemixUIGridViewProps) => {
})) }))
} }
const { plugin } = props.plugin
const searchInputRef = useRef(null)
const [state, setState] = useState<{
themeQuality: {filter: string; name: string}
}>({
themeQuality: themes.light
})
// Initialize filters context with data from props // Initialize filters context with data from props
useEffect(() => { useEffect(() => {
document.addEventListener('keyup', (e) => handleSearchKeyDown(e))
if (props.tagList && Array.isArray(props.tagList)) { if (props.tagList && Array.isArray(props.tagList)) {
const initialKeyValueMap: Record<string, { enabled: boolean; color: string; }> = {}; const initialKeyValueMap: Record<string, { enabled: boolean; color: string; }> = {};
@ -74,33 +90,14 @@ export const RemixUIGridView = (props: RemixUIGridViewProps) => {
if (showUntagged) initialKeyValueMap['no tag'] = { enabled: true, color: 'primary' } if (showUntagged) initialKeyValueMap['no tag'] = { enabled: true, color: 'primary' }
setKeyValueMap(initialKeyValueMap) setKeyValueMap(initialKeyValueMap)
} }
}, []) return () => {
document.removeEventListener('keyup', handleSearchKeyDown)
useEffect(() => {
plugin?.call('theme', 'currentTheme').then((theme) => {
// update theme quality. To be used for for images
setState((prevState) => {
return {
...prevState,
themeQuality: theme.quality === 'dark' ? themes.dark : themes.light
}
})
})
plugin?.on('theme', 'themeChanged', (theme) => {
// update theme quality. To be used for for images
setState((prevState) => {
return {
...prevState,
themeQuality: theme.quality === 'dark' ? themes.dark : themes.light
} }
}) }, [])
})
}, [plugin])
return ( return (
<FiltersContext.Provider value={{ showUntagged, showPin, keyValueMap, updateValue, addValue }}> <FiltersContext.Provider value={{ showUntagged, showPin, keyValueMap, updateValue, addValue, filter }}>
<div className={"d-flex flex-column bg-dark w-100 h-100 remixui_grid_view_container " + props.classList || ''} data-id="remixUIGV"> <div className={"d-flex flex-column bg-dark w-100 h-100 remixui_grid_view_container " + props.classList || ''} data-id="remixUIGV">
<ThemeContext.Provider value={state.themeQuality}>
<div className="d-flex flex-column w-100 remixui_grid_view"> <div className="d-flex flex-column w-100 remixui_grid_view">
<div className='d-flex p-4 bg-light flex-column remixui_grid_view_titlebar'> <div className='d-flex p-4 bg-light flex-column remixui_grid_view_titlebar'>
<div className='d-flex flex-row align-items-center mb-2'> <div className='d-flex flex-row align-items-center mb-2'>
@ -112,10 +109,11 @@ export const RemixUIGridView = (props: RemixUIGridViewProps) => {
<div className="d-flex flex-row pr-2 pb-1 align-items-center justify-content-between"> <div className="d-flex flex-row pr-2 pb-1 align-items-center justify-content-between">
<div className='d-flex' id="GVFilter"> <div className='d-flex' id="GVFilter">
<button <button
disabled={state.searchDisable}
className="remixui_grid_view_btn text-secondary form-control bg-light border d-flex align-items-center p-2 justify-content-center fas fa-filter bg-light" className="remixui_grid_view_btn text-secondary form-control bg-light border d-flex align-items-center p-2 justify-content-center fas fa-filter bg-light"
onClick={(e) => { onClick={(e) => {
setFilter(searchInputRef.current.value)
_paq.push(['trackEvent', 'GridView' + props.title ? props.title : '', 'filter', searchInputRef.current.value]) _paq.push(['trackEvent', 'GridView' + props.title ? props.title : '', 'filter', searchInputRef.current.value])
//setstate
}} }}
></button> ></button>
<input <input
@ -138,7 +136,6 @@ export const RemixUIGridView = (props: RemixUIGridViewProps) => {
</div> </div>
{ props.children } { props.children }
</div> </div>
</ThemeContext.Provider>
</div> </div>
</FiltersContext.Provider> </FiltersContext.Provider>
) )

@ -12,12 +12,9 @@ function HomeTabFeatured() {
const themeFilter = useContext(ThemeContext) const themeFilter = useContext(ThemeContext)
return ( return (
<div className="pt-3 pl-2" id="hTFeaturedeSection"> <div className="pt-1 pl-2 h-100" id="hTFeaturedeSection">
<label style={{ fontSize: '1.2rem' }}> <div className="mb-2 remix_ui-carousel-container">
<FormattedMessage id="home.featured" /> <div className="w-100 d-flex flex-column rounded-3 remix_ui-carouselbox">
</label>
<div className="mb-2">
<div className="w-100 d-flex flex-column" style={{ height: '200px' }}>
<ThemeContext.Provider value={themeFilter}> <ThemeContext.Provider value={themeFilter}>
<Carousel <Carousel
arrows={false} arrows={false}
@ -34,16 +31,16 @@ function HomeTabFeatured() {
centerMode={false} centerMode={false}
autoPlay={true} autoPlay={true}
keyBoardControl={true} keyBoardControl={true}
containerClass="border w-full carousel-container" containerClass="border w-full carousel-container d-flex align-items-center"
sliderClass="h-100 justify-content-between" sliderClass="h-100 justify-content-between"
deviceType={'desktop'} deviceType={'desktop'}
itemClass="" itemClass=""
autoPlaySpeed={10000} autoPlaySpeed={10000}
dotListClass="position-relative mt-2" dotListClass="position-relative mt-2"
> >
<div className="mr-1 pr-1 d-flex"> <div className="mr-1 pr-1 d-flex align-items-center justify-content-center h-100">
<a href={releaseDetails.moreLink} target="__blank"> <a href={releaseDetails.moreLink} target="__blank">
<img src={'assets/img/remi_drums_whatsnew.webp'} style={{ flex: '1', height: '170px', maxWidth: '170px' }} alt=""></img> <img src={'assets/img/remi_drums_whatsnew.webp'} className="remixui_carouselImage" alt=""></img>
</a> </a>
<div className="h6 w-50 p-2 pl-4 align-self-center" style={{ flex: '1' }}> <div className="h6 w-50 p-2 pl-4 align-self-center" style={{ flex: '1' }}>
<h5>{releaseDetails.version} {releaseDetails.title}</h5> <h5>{releaseDetails.version} {releaseDetails.title}</h5>
@ -65,9 +62,9 @@ function HomeTabFeatured() {
</a> </a>
</div> </div>
</div> </div>
<div className="mr-1 pr-1 d-flex"> <div className="mr-1 pr-1 d-flex align-items-center justify-content-center h-100">
<a href="https://remix-project.org" target="__blank"> <a href="https://remix-project.org" target="__blank">
<img src={'assets/img/bgRemi_small.webp'} style={{ flex: '1', height: '170px', maxWidth: '170px' }} alt=""></img> <img src={'assets/img/bgRemi_small.webp'} className="remixui_carouselImage" alt=""></img>
</a> </a>
<div className="h6 w-50 p-2 pl-4 align-self-center" style={{ flex: '1' }}> <div className="h6 w-50 p-2 pl-4 align-self-center" style={{ flex: '1' }}>
<h5> <h5>
@ -86,9 +83,9 @@ function HomeTabFeatured() {
</a> </a>
</div> </div>
</div> </div>
<div className="mr-1 pr-1 d-flex"> <div className="mr-1 pr-1 d-flex align-items-center justify-content-center h-100">
<a href="https://www.youtube.com/@EthereumRemix/videos" target="__blank"> <a href="https://www.youtube.com/@EthereumRemix/videos" target="__blank">
<img src={'/assets/img/YouTubeLogo.webp'} style={{ flex: '1', height: '170px', maxWidth: '170px' }} alt=""></img> <img src={'/assets/img/YouTubeLogo.webp'} className="remixui_carouselImage" alt=""></img>
</a> </a>
<div className="h6 w-50 p-2 pl-4 align-self-center" style={{ flex: '1' }}> <div className="h6 w-50 p-2 pl-4 align-self-center" style={{ flex: '1' }}>
<h5> <h5>
@ -110,9 +107,9 @@ function HomeTabFeatured() {
</a> </a>
</div> </div>
</div> </div>
<div className="mr-1 pr-1 d-flex"> <div className="mr-1 pr-1 d-flex align-items-center justify-content-center h-100">
<a href="https://docs.google.com/forms/d/e/1FAIpQLSd0WsJnKbeJo-BGrnf7WijxAdmE4PnC_Z4M0IApbBfHLHZdsQ/viewform" target="__blank"> <a href="https://docs.google.com/forms/d/e/1FAIpQLSd0WsJnKbeJo-BGrnf7WijxAdmE4PnC_Z4M0IApbBfHLHZdsQ/viewform" target="__blank">
<img src={'/assets/img/remixRewardBetaTester_small.webp'} style={{ flex: '1', height: '170px', maxWidth: '170px' }} alt=""></img> <img src={'/assets/img/remixRewardBetaTester_small.webp'} className="remixui_carouselImage_remixbeta" alt=""></img>
</a> </a>
<div className="h6 w-50 p-2 pl-4 align-self-center" style={{ flex: '1' }}> <div className="h6 w-50 p-2 pl-4 align-self-center" style={{ flex: '1' }}>
<h5> <h5>

@ -84,7 +84,7 @@ function HomeTabFeaturedPlugins({ plugin }: HomeTabFeaturedPluginsProps) {
} }
return ( return (
<div className="pl-2 w-100" id="hTFeaturedPlugins"> <div className="pl-2 w-100 align-items-end remixui_featuredplugins_container" id="hTFeaturedPlugins">
<label className="" style={{ fontSize: '1.2rem' }}> <label className="" style={{ fontSize: '1.2rem' }}>
<FormattedMessage id="home.featuredPlugins" /> <FormattedMessage id="home.featuredPlugins" />
</label> </label>

@ -1,31 +1,14 @@
/* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/no-unused-vars */
import React, { useState, useRef, useReducer, useEffect } from 'react' import React, { useState, useRef, useReducer, useEffect } from 'react'
import { FormattedMessage } from 'react-intl' import { FormattedMessage } from 'react-intl'
import { ModalDialog } from '@remix-ui/modal-dialog' // eslint-disable-line import {Toaster} from '@remix-ui/toaster' // eslint-disable-line
import { Toaster } from '@remix-ui/toaster' // eslint-disable-line
const _paq = (window._paq = window._paq || []) // eslint-disable-line const _paq = (window._paq = window._paq || []) // eslint-disable-line
import { CustomTooltip } from '@remix-ui/helper' import { CustomTooltip } from '@remix-ui/helper'
import { TEMPLATE_NAMES } from '@remix-ui/workspace'
interface HomeTabFileProps { interface HomeTabFileProps {
plugin: any plugin: any
} }
const loadingInitialState = {
tooltip: '',
showModalDialog: false,
importSource: ''
}
const loadingReducer = (state = loadingInitialState, action) => {
return {
...state,
tooltip: action.tooltip,
showModalDialog: false,
importSource: ''
}
}
function HomeTabFile({ plugin }: HomeTabFileProps) { function HomeTabFile({ plugin }: HomeTabFileProps) {
const [state, setState] = useState<{ const [state, setState] = useState<{
searchInput: string searchInput: string
@ -45,13 +28,9 @@ function HomeTabFile({ plugin }: HomeTabFileProps) {
modalInfo: { title: '', loadItem: '', examples: [], prefix: '' }, modalInfo: { title: '', loadItem: '', examples: [], prefix: '' },
importSource: '', importSource: '',
toasterMsg: '', toasterMsg: '',
recentWorkspaces: [] recentWorkspaces: [],
}) })
const [, dispatch] = useReducer(loadingReducer, loadingInitialState)
const inputValue = useRef(null)
useEffect(() => { useEffect(() => {
plugin.on('filePanel', 'setWorkspace', async () => { plugin.on('filePanel', 'setWorkspace', async () => {
let recents = JSON.parse(localStorage.getItem('recentWorkspaces')) let recents = JSON.parse(localStorage.getItem('recentWorkspaces'))
@ -59,8 +38,11 @@ function HomeTabFile({ plugin }: HomeTabFileProps) {
if (!recents) { if (!recents) {
recents = [] recents = []
} else { } else {
const filtered = recents.filter((workspace) => {
return workspace !== null
})
setState((prevState) => { setState((prevState) => {
return { ...prevState, recentWorkspaces: recents.slice(0, recents.length <= 3 ? recents.length : 3) } return { ...prevState, recentWorkspaces: filtered.slice(0, filtered.length <= 3 ? filtered.length : 3) }
}) })
} }
}) })
@ -71,7 +53,9 @@ function HomeTabFile({ plugin }: HomeTabFileProps) {
if (!recents) { if (!recents) {
newRecents = [] newRecents = []
} else { } else {
newRecents = recents.filter((el) => { return el !== name}) newRecents = recents.filter((el) => {
return el !== name
})
localStorage.setItem('recentWorkspaces', JSON.stringify(newRecents)) localStorage.setItem('recentWorkspaces', JSON.stringify(newRecents))
} }
setState((prevState) => { setState((prevState) => {
@ -85,46 +69,10 @@ function HomeTabFile({ plugin }: HomeTabFileProps) {
try { try {
plugin.off('filePanel', 'setWorkspace') plugin.off('filePanel', 'setWorkspace')
plugin.off('filePanel', 'workspaceDeleted') plugin.off('filePanel', 'workspaceDeleted')
} catch (e) { } catch (e) {}
}
} }
}, [plugin]) }, [plugin])
const processLoading = (type: string) => {
_paq.push(['trackEvent', 'hometab', 'filesSection', 'importFrom' + type])
const contentImport = plugin.contentImport
const workspace = plugin.fileManager.getProvider('workspace')
const startsWith = state.importSource.substring(0, 4)
if ((type === 'ipfs' || type === 'IPFS') && startsWith !== 'ipfs' && startsWith !== 'IPFS') {
setState((prevState) => {
return { ...prevState, importSource: startsWith + state.importSource }
})
}
contentImport.import(
state.modalInfo.prefix + state.importSource,
(loadingMsg) => dispatch({ tooltip: loadingMsg }),
async (error, content, cleanUrl, type, url) => {
if (error) {
toast(error.message || error)
} else {
try {
if (await workspace.exists(type + '/' + cleanUrl)) toast('File already exists in workspace')
else {
workspace.addExternal(type + '/' + cleanUrl, content, url)
plugin.call('menuicons', 'select', 'filePanel')
}
} catch (e) {
toast(e.message)
}
}
}
)
setState((prevState) => {
return { ...prevState, showModalDialog: false, importSource: '' }
})
}
const toast = (message: string) => { const toast = (message: string) => {
setState((prevState) => { setState((prevState) => {
return { ...prevState, toasterMsg: message } return { ...prevState, toasterMsg: message }
@ -145,16 +93,16 @@ function HomeTabFile({ plugin }: HomeTabFileProps) {
await plugin.call('filePanel', 'switchToWorkspace', { name: wName, isLocalHost: false }) await plugin.call('filePanel', 'switchToWorkspace', { name: wName, isLocalHost: false })
await plugin.call('filePanel', 'switchToWorkspace', { name: wName, isLocalHost: false }) // calling once is not working. await plugin.call('filePanel', 'switchToWorkspace', { name: wName, isLocalHost: false }) // calling once is not working.
const content = `// SPDX-License-Identifier: MIT const content = `// SPDX-License-Identifier: MIT
pragma solidity >=0.6.12 <0.9.0; pragma solidity >=0.6.12 <0.9.0;
contract HelloWorld { contract HelloWorld {
/** /**
* @dev Prints Hello World string * @dev Prints Hello World string
*/ */
function print() public pure returns (string memory) { function print() public pure returns (string memory) {
return "Hello World!"; return "Hello World!";
} }
} }
` `
if (createFile) { if (createFile) {
const { newPath } = await plugin.call('fileManager', 'writeFileNoRewrite', '/contracts/HelloWorld.sol', content) const { newPath } = await plugin.call('fileManager', 'writeFileNoRewrite', '/contracts/HelloWorld.sol', content)
@ -162,7 +110,6 @@ contract HelloWorld {
} else { } else {
await plugin.call('fileManager', 'open', '/contracts/HelloWorld.sol') await plugin.call('fileManager', 'open', '/contracts/HelloWorld.sol')
} }
} }
const uploadFile = async (target) => { const uploadFile = async (target) => {
@ -180,184 +127,98 @@ contract HelloWorld {
plugin.verticalIcons.select('filePanel') plugin.verticalIcons.select('filePanel')
} }
const showFullMessage = (title: string, loadItem: string, examples: Array<string>, prefix = '') => {
setState((prevState) => {
return {
...prevState,
showModalDialog: true,
modalInfo: {
title: title,
loadItem: loadItem,
examples: examples,
prefix,
},
}
})
}
const hideFullMessage = () => {
//eslint-disable-line
setState((prevState) => {
return { ...prevState, showModalDialog: false, importSource: '' }
})
}
const handleSwichToRecentWorkspace = async (e, workspaceName) => { const handleSwichToRecentWorkspace = async (e, workspaceName) => {
e.preventDefault() e.preventDefault()
_paq.push(['trackEvent', 'hometab', 'filesSection', 'loadRecentWorkspace']) _paq.push(['trackEvent', 'hometab', 'filesSection', 'loadRecentWorkspace'])
await plugin.call('filePanel', 'switchToWorkspace', { name: workspaceName, isLocalhost: false }) await plugin.call('filePanel', 'switchToWorkspace', { name: workspaceName, isLocalhost: false })
} }
const examples = state.modalInfo.examples.map((urlEl, key) => (
<div key={key} className="p-1 user-select-auto">
<a>{urlEl}</a>
</div>
))
return ( return (
<> <>
<ModalDialog <Toaster message={state.toasterMsg} />
id="homeTab" <div className="justify-content-start p-2 d-flex flex-column" id="hTFileSection">
title={'Import from ' + state.modalInfo.title} <div className="mb-1">
okLabel="Import" {(state.recentWorkspaces[0] || state.recentWorkspaces[1] || state.recentWorkspaces[2]) && (
hide={!state.showModalDialog} <div className="d-flex flex-column mb-5 remixui_recentworkspace">
handleHide={() => hideFullMessage()} <label style={{ fontSize: '0.8rem' }} className="mt-1">
okFn={() => processLoading(state.modalInfo.title)} Recent Workspaces
> </label>
<div className="p-2 user-select-auto"> {state.recentWorkspaces[0] && state.recentWorkspaces[0] !== '' && (
{state.modalInfo.loadItem !== '' && <span>Enter the {state.modalInfo.loadItem} you would like to load.</span>} <a className="cursor-pointer mb-1 ml-2" href="#" onClick={(e) => handleSwichToRecentWorkspace(e, state.recentWorkspaces[0])}>
{state.modalInfo.examples.length !== 0 && ( {state.recentWorkspaces[0]}
<> </a>
<div>e.g</div> )}
<div>{examples}</div> {state.recentWorkspaces[1] && state.recentWorkspaces[1] !== '' && (
</> <a className="cursor-pointer mb-1 ml-2" href="#" onClick={(e) => handleSwichToRecentWorkspace(e, state.recentWorkspaces[1])}>
{state.recentWorkspaces[1]}
</a>
)}
{state.recentWorkspaces[2] && state.recentWorkspaces[2] !== '' && (
<a className="cursor-pointer ml-2" href="#" onClick={(e) => handleSwichToRecentWorkspace(e, state.recentWorkspaces[2])}>
{state.recentWorkspaces[2]}
</a>
)} )}
<div className="d-flex flex-row">
{state.modalInfo.prefix && <span className="text-nowrap align-self-center mr-2">ipfs://</span>}
<input
ref={inputValue}
type='text'
name='prompt_text'
id='inputPrompt_text'
className="w-100 mt-1 form-control"
data-id="homeTabModalDialogCustomPromptText"
value={state.importSource}
onInput={(e) => {
setState((prevState) => {
return { ...prevState, importSource: inputValue.current.value }
})
}}
/>
</div> </div>
)}
</div> </div>
</ModalDialog> <div className="d-flex flex-column flex-nowrap mt-4">
<Toaster message={state.toasterMsg} />
<div className="justify-content-start mt-1 p-2 d-flex flex-column" id="hTFileSection">
<label style={{ fontSize: '1.2rem' }}> <label style={{ fontSize: '1.2rem' }}>
<FormattedMessage id="home.files" /> <FormattedMessage id="home.files" />
</label> </label>
<div className="d-flex flex-column"> <div className="d-flex flex-row flex-wrap">
<div className="d-flex flex-row"> <CustomTooltip placement={'top'} tooltipId="overlay-tooltip" tooltipClasses="text-nowrap" tooltipText={<FormattedMessage id="home.newFileTooltip" />} tooltipTextClasses="border bg-light text-dark p-1 pr-3">
<CustomTooltip <button className="btn btn-primary text-nowrap p-2 mr-2 border my-1 mb-2" data-id="homeTabNewFile" style={{ width: 'fit-content' }} onClick={async () => {
placement={'top'} _paq.push(['trackEvent', 'hometab', 'filesSection', 'newFile'])
tooltipId="overlay-tooltip" await plugin.call('menuicons', 'select', 'filePanel')
tooltipClasses="text-nowrap" await plugin.call('filePanel', 'createNewFile')
tooltipText={<FormattedMessage id='home.startCodingPlayground' />} }}>
tooltipTextClasses="border bg-light text-dark p-1 pr-3" <i className="far fa-file pl-1 pr-2"></i>
> <FormattedMessage id="home.newFile" />
<button className="btn btn-primary text-nowrap p-2 mr-2 border my-1" data-id="homeTabStartCoding" style={{ width: 'fit-content' }} onClick={() => startCoding()}>
<FormattedMessage id="home.startCoding" />
</button> </button>
</CustomTooltip> </CustomTooltip>
<CustomTooltip <CustomTooltip placement={'top'} tooltipId="overlay-tooltip" tooltipClasses="text-nowrap" tooltipText={<FormattedMessage id="home.openFileTooltip" />} tooltipTextClasses="border bg-light text-dark p-1 pr-3">
placement={'top'}
tooltipId="overlay-tooltip"
tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id='home.openFileTooltip' />}
tooltipTextClasses="border bg-light text-dark p-1 pr-3"
>
<span> <span>
<label className="btn text-nowrap p-2 mr-2 border my-1" style={{ width: 'fit-content', cursor: 'pointer' }} htmlFor="openFileInput"> <label className="btn text-nowrap p-2 mr-2 border my-1 mb-2" style={{ width: 'fit-content', cursor: 'pointer' }} htmlFor="openFileInput">
<i className="far fa-upload pl-1 pr-2"></i>
<FormattedMessage id="home.openFile" /> <FormattedMessage id="home.openFile" />
</label> </label>
<input <input
title="open file" title="open file"
type="file" type="file"
id="openFileInput" id="openFileInput"
onChange={(event) => { onChange={async (event) => {
event.stopPropagation() event.stopPropagation()
plugin.verticalIcons.select('filePanel') await plugin.call('menuicons', 'select', 'filePanel')
uploadFile(event.target) uploadFile(event.target)
}} }}
multiple multiple
/> />
</span> </span>
</CustomTooltip> </CustomTooltip>
<CustomTooltip <CustomTooltip placement={'top'} tooltipId="overlay-tooltip" tooltipClasses="text-nowrap" tooltipText={<FormattedMessage id="home.gistTooltip" />} tooltipTextClasses="border bg-light text-dark p-1 pr-3"
placement={'top'}
tooltipId="overlay-tooltip"
tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id="home.connectToLocalhost" />}
tooltipTextClasses="border bg-light text-dark p-1 pr-3"
> >
<button className="btn text-nowrap p-2 border my-1" style={{ width: 'fit-content' }} onClick={() => connectToLocalhost()}> <button className="btn text-nowrap p-2 mr-2 border my-1 mb-2" data-id="landingPageImportFromGistButton" onClick={() => importFromGist()}>
<FormattedMessage id="home.accessFileSystem" /> <i className="fab fa-github pl-1 pr-2"></i>
Gist
</button> </button>
</CustomTooltip> </CustomTooltip>
</div> <CustomTooltip placement={'top'} tooltipId="overlay-tooltip" tooltipClasses="text-nowrap" tooltipText={<FormattedMessage id="home.gitCloneTooltip" />} tooltipTextClasses="border bg-light text-dark p-1 pr-3"
{(state.recentWorkspaces[0] || state.recentWorkspaces[1] || state.recentWorkspaces[2]) && (
<div className="d-flex flex-column">
<label style={{ fontSize: '0.8rem' }} className="mt-3">
Recent workspaces
</label>
{state.recentWorkspaces[0] && state.recentWorkspaces[0] !== '' && (
<a className="cursor-pointer mb-1 ml-2" href="#" onClick={(e) => handleSwichToRecentWorkspace(e, state.recentWorkspaces[0])}>
{state.recentWorkspaces[0]}
</a>
)}
{state.recentWorkspaces[1] && state.recentWorkspaces[1] !== '' && (
<a className="cursor-pointer mb-1 ml-2" href="#" onClick={(e) => handleSwichToRecentWorkspace(e, state.recentWorkspaces[1])}>
{state.recentWorkspaces[1]}
</a>
)}
{state.recentWorkspaces[2] && state.recentWorkspaces[2] !== '' && (
<a className="cursor-pointer ml-2" href="#" onClick={(e) => handleSwichToRecentWorkspace(e, state.recentWorkspaces[2])}>
{state.recentWorkspaces[2]}
</a>
)}
</div>
)}
</div>
<label style={{ fontSize: '0.8rem' }} className="pt-3">
<FormattedMessage id="home.loadFrom" />
</label>
<div className="d-flex">
<button
className="btn p-2 border mr-2"
data-id="landingPageImportFromGitHubButton"
onClick={() =>
showFullMessage('GitHub', 'github URL', [
'https://github.com/0xcert/ethereum-erc721/src/contracts/tokens/nf-token-metadata.sol',
'https://github.com/OpenZeppelin/openzeppelin-solidity/blob/67bca857eedf99bf44a4b6a0fc5b5ed553135316/contracts/access/Roles.sol',
])
}
> >
GitHub <button className="btn text-nowrap p-2 mr-2 border my-1 mb-2" data-id="landingPageImportFromGitHubButton" onClick={async () => {
</button> _paq.push(['trackEvent', 'hometab', 'filesSection', 'Git Clone'])
<button className="btn p-2 border mr-2" data-id="landingPageImportFromGistButton" onClick={() => importFromGist()}> await plugin.call('filePanel', 'clone')
Gist }}>
</button> <i className="fa-brands fa-github-alt pl-1 pr-2"></i>
<button className="btn p-2 border mr-2" onClick={() => showFullMessage('Ipfs', 'ipfs hash', ['ipfs://QmQQfBMkpDgmxKzYaoAtqfaybzfgGm9b2LWYyT56Chv6xH'], 'ipfs://')}> Git Clone
IPFS
</button> </button>
<button </CustomTooltip>
className="btn p-2 border" <CustomTooltip placement={'top'} tooltipId="overlay-tooltip" tooltipClasses="text-nowrap" tooltipText={<FormattedMessage id="home.connectToLocalhost" />} tooltipTextClasses="border bg-light text-dark p-1 pr-3">
onClick={() => <button className="btn text-nowrap p-2 border my-1 mb-2" onClick={() => connectToLocalhost()}>
showFullMessage('Https', 'http/https raw content', ['https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/master/contracts/token/ERC20/ERC20.sol']) <i className="fa-regular fa-desktop pr-2"></i>
} <FormattedMessage id="home.accessFileSystem" />
>
HTTPS
</button> </button>
</CustomTooltip>
</div>
</div> </div>
</div> </div>
</> </>

@ -1,13 +1,12 @@
/* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/no-unused-vars */
import React, { useEffect, useRef, useContext } from 'react' import React, { useEffect, useRef, useContext, SyntheticEvent, useState } from 'react'
import { useIntl, FormattedMessage } from 'react-intl' import { useIntl, FormattedMessage } from 'react-intl'
import { TEMPLATE_NAMES,TEMPLATE_METADATA } from '@remix-ui/workspace' import { TEMPLATE_NAMES, TEMPLATE_METADATA } from '@remix-ui/workspace'
import { ThemeContext } from '../themeContext' import { ThemeContext } from '../themeContext'
import Carousel from 'react-multi-carousel'
import WorkspaceTemplate from './workspaceTemplate' import WorkspaceTemplate from './workspaceTemplate'
import 'react-multi-carousel/lib/styles.css' import 'react-multi-carousel/lib/styles.css'
import CustomNavButtons from './customNavButtons'
import { appPlatformTypes, platformContext } from '@remix-ui/app' import { appPlatformTypes, platformContext } from '@remix-ui/app'
import { CustomTooltip } from '@remix-ui/helper'
declare global { declare global {
interface Window { interface Window {
@ -19,12 +18,65 @@ interface HomeTabGetStartedProps {
plugin: any plugin: any
} }
type WorkspaceTemplate = {
gsID: string
workspaceTitle: string
description: string
projectLogo: string
templateName: string
}
const workspaceTemplates: WorkspaceTemplate[] = [
{
gsID: 'sUTLogo',
workspaceTitle: 'Start Coding',
description: 'Start coding using the default template.',
projectLogo: 'assets/img/remixverticaltextLogo.png',
templateName: 'remixDefault',
},
{
gsID: 'sUTLogo',
workspaceTitle: 'ZK Semaphore',
description: 'Create a new ZK Project with Circom using this template.',
projectLogo: 'assets/img/circom.webp',
templateName: 'semaphore',
},
{
gsID: 'sUTLogo',
workspaceTitle: 'ERC20',
description: 'Create a new ERC20 token using this template.',
projectLogo: 'assets/img/oxprojectLogo.png',
templateName: 'ozerc20',
},
{
gsID: 'sUTLogo',
workspaceTitle: 'Uniswap V4 Hooks',
description: 'Create a new workspace based on this template.',
projectLogo: 'assets/img/gnosissafeLogo.png',
templateName: 'uniswapV4Template',
},
{
gsID: 'sUTLogo',
workspaceTitle: 'NFT / ERC721',
description: 'Create a new ERC721 token using this template.',
projectLogo: 'assets/img/openzeppelinLogo.png',
templateName: 'ozerc721',
},
{
gsID: 'sUTLogo',
workspaceTitle: 'MultiSig',
description: 'Create a new MultiSig wallet using this template.',
projectLogo: 'assets/img/gnosissafeLogo.png',
templateName: 'gnosisSafeMultisig',
},
]
function HomeTabGetStarted({ plugin }: HomeTabGetStartedProps) { function HomeTabGetStarted({ plugin }: HomeTabGetStartedProps) {
const platform = useContext(platformContext) const platform = useContext(platformContext)
const themeFilter = useContext(ThemeContext) const themeFilter = useContext(ThemeContext)
const intl = useIntl()
const carouselRef = useRef<any>({}) const carouselRef = useRef<any>({})
const carouselRefDiv = useRef(null) const carouselRefDiv = useRef(null)
const intl = useIntl()
useEffect(() => { useEffect(() => {
document.addEventListener('wheel', handleScroll) document.addEventListener('wheel', handleScroll)
@ -61,8 +113,7 @@ function HomeTabGetStarted({ plugin }: HomeTabGetStartedProps) {
} }
const createWorkspace = async (templateName) => { const createWorkspace = async (templateName) => {
if (platform === appPlatformTypes.desktop) {
if (platform === appPlatformTypes.desktop){
await plugin.call('remix-templates', 'loadTemplateInNewWindow', templateName) await plugin.call('remix-templates', 'loadTemplateInNewWindow', templateName)
return return
} }
@ -92,90 +143,29 @@ function HomeTabGetStarted({ plugin }: HomeTabGetStartedProps) {
return ( return (
<div className="pl-2" id="hTGetStartedSection"> <div className="pl-2" id="hTGetStartedSection">
<label style={{ fontSize: '1.2rem' }}> <label className="pt-3" style={{ fontSize: '1.2rem' }}>
<FormattedMessage id="home.projectTemplates" /> <FormattedMessage id="home.projectTemplates" />
</label> </label>
<div ref={carouselRefDiv} className="w-100 d-flex flex-column"> <div ref={carouselRefDiv} className="w-100 d-flex flex-column pt-1">
<ThemeContext.Provider value={themeFilter}> <ThemeContext.Provider value={themeFilter}>
<Carousel <div className="pt-3">
ref={carouselRef} <div className="d-flex flex-row align-items-center mb-3 flex-wrap">
focusOnSelect={true} {workspaceTemplates.map((template, index) => (
customButtonGroup={<CustomNavButtons next={undefined} previous={undefined} goToSlide={undefined} parent={carouselRef} />} <CustomTooltip tooltipText={template.description} tooltipId={template.gsID} tooltipClasses="text-nowrap" tooltipTextClasses="border bg-light text-dark p-1 pr-3" placement="top-start" key={`${template.gsID}-${template.workspaceTitle}-${index}`}>
arrows={false} <button
swipeable={false} key={index}
draggable={true} className={index === 0 ? 'btn btn-primary border p-2 text-nowrap mr-3 mb-3' : index === workspaceTemplates.length - 1 ? 'btn border p-2 text-nowrap mr-2 mb-3' : 'btn border p-2 text-nowrap mr-3 mb-3'}
showDots={false} onClick={(e) => {
responsive={{ createWorkspace(template.templateName)
superLargeDesktop: {
breakpoint: { max: 4000, min: 3000 },
items: 5
},
desktop: {
breakpoint: { max: 3000, min: 1024 },
items: 5,
partialVisibilityGutter: 0
}
}} }}
renderButtonGroupOutside={true} data-id={`homeTabGetStarted${template.templateName}`}
ssr={true} // means to render carousel on server-side.
keyBoardControl={true}
containerClass="carousel-container"
deviceType={'desktop'}
itemClass="w-100"
> >
<WorkspaceTemplate {template.workspaceTitle}
gsID="sUTLogo" </button>
workspaceTitle="MultiSig" </CustomTooltip>
description={ ))}
intl.formatMessage({ id: 'home.gnosisSafeMultisigTemplateDesc' }) </div>
} </div>
projectLogo="assets/img/gnosissafeLogo.png"
callback={() => createWorkspace("gnosisSafeMultisig")}
/>
<WorkspaceTemplate
gsID="sUTLogo"
workspaceTitle="ERC20"
description={
intl.formatMessage({ id: 'home.zeroxErc20TemplateDesc' })
}
projectLogo="assets/img/oxprojectLogo.png"
callback={() => createWorkspace("zeroxErc20")}
/>
<WorkspaceTemplate
gsID="sourcifyLogo"
workspaceTitle="ERC20"
description={intl.formatMessage({ id: 'home.ozerc20TemplateDesc' })}
projectLogo="assets/img/openzeppelinLogo.png"
callback={() => createWorkspace('ozerc20')}
/>
<WorkspaceTemplate
gsID="sUTLogo"
workspaceTitle="ERC721"
description={intl.formatMessage({
id: 'home.ozerc721TemplateDesc'
})}
projectLogo="assets/img/openzeppelinLogo.png"
callback={() => createWorkspace("ozerc721")}
/>
<WorkspaceTemplate
gsID="sUTLogo"
workspaceTitle="ERC1155"
description={intl.formatMessage({
id: 'home.ozerc1155TemplateDesc'
})}
projectLogo="assets/img/openzeppelinLogo.png"
callback={() => createWorkspace("ozerc1155")}
/>
<WorkspaceTemplate
gsID="solhintLogo"
workspaceTitle="Basic"
description={intl.formatMessage({
id: 'home.remixDefaultTemplateDesc'
})}
projectLogo="assets/img/remixverticaltextLogo.png"
callback={() => createWorkspace("remixDefault")}
/>
</Carousel>
</ThemeContext.Provider> </ThemeContext.Provider>
</div> </div>
</div> </div>

@ -3,8 +3,55 @@
import React, { useEffect, useState, useRef, useContext } from 'react' import React, { useEffect, useState, useRef, useContext } from 'react'
import { FormattedMessage, useIntl } from 'react-intl' import { FormattedMessage, useIntl } from 'react-intl'
import { CustomTooltip } from '@remix-ui/helper' import { CustomTooltip } from '@remix-ui/helper'
import { Placement } from 'react-bootstrap/esm/Overlay'
const _paq = (window._paq = window._paq || []) // eslint-disable-line const _paq = (window._paq = window._paq || []) // eslint-disable-line
type HometabIconSection = {
textToolip: JSX.Element
urlLink: string
iconClass: 'fa-youtube'|'fa-x-twitter'|'fa-linkedin'|'fa-medium'|'fa-discord'
placement: Placement
matomoTrackingEntry: string[]
}
const iconButtons: HometabIconSection[] = [
{
textToolip: <FormattedMessage id="home.remixYoutubePlaylist" />,
matomoTrackingEntry: ['trackEvent', 'hometab', 'socialMedia', 'youtube'],
urlLink: 'https://www.youtube.com/channel/UCjTUPyFEr2xDGN6Cg8nKDaA',
iconClass: 'fa-youtube',
placement: 'top'
},
{
textToolip: <FormattedMessage id="home.remixTwitterProfile" />,
matomoTrackingEntry: ['trackEvent', 'hometab', 'socialMedia', 'twitter'],
urlLink: 'https://twitter.com/EthereumRemix',
iconClass: 'fa-x-twitter',
placement: 'top'
},
{
textToolip: <FormattedMessage id="home.remixLinkedinProfile" />,
matomoTrackingEntry: ['trackEvent', 'hometab', 'socialmedia', 'linkedin'],
urlLink: 'https://www.linkedin.com/company/ethereum-remix/',
iconClass: 'fa-linkedin',
placement: 'top'
},
{
textToolip: <FormattedMessage id="home.remixMediumPosts" />,
matomoTrackingEntry: ['trackEvent', 'hometab', 'socialmedia', 'medium'],
urlLink: 'https://medium.com/remix-ide',
iconClass: 'fa-medium',
placement: 'top'
},
{
textToolip: <FormattedMessage id="home.joinUsOnDiscord" />,
matomoTrackingEntry: ['trackEvent', 'hometab', 'socialmedia', 'discord'],
urlLink: 'https://discord.gg/mh9hFCKkEq',
iconClass: 'fa-discord',
placement: 'top'
}
]
function HomeTabTitle() { function HomeTabTitle() {
useEffect(() => { useEffect(() => {
document.addEventListener('keyup', (e) => handleSearchKeyDown(e)) document.addEventListener('keyup', (e) => handleSearchKeyDown(e))
@ -64,82 +111,25 @@ function HomeTabTitle() {
</div> </div>
</div> </div>
<span className="d-flex flex-nowrap align-self-end"> <span className="d-flex flex-nowrap align-self-end">
{iconButtons.map((button, index) => (
<CustomTooltip <CustomTooltip
placement={'top'} key={index}
tooltipId="overlay-tooltip" placement={button.placement}
tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id="home.remixYoutubePlaylist" />}
tooltipTextClasses="border bg-light text-dark p-1 pr-3"
>
<button
onClick={() => {
openLink('https://www.youtube.com/channel/UCjTUPyFEr2xDGN6Cg8nKDaA')
_paq.push(['trackEvent', 'hometab', 'socialMedia', 'youtube'])
}}
className="border-0 px-1 h-100 btn fab fa-youtube"
></button>
</CustomTooltip>
<CustomTooltip
placement={'top'}
tooltipId="overlay-tooltip"
tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id="home.remixTwitterProfile" />}
tooltipTextClasses="border bg-light text-dark p-1 pr-3"
>
<button
onClick={() => {
openLink('https://twitter.com/EthereumRemix')
_paq.push(['trackEvent', 'hometab', 'socialMedia', 'twitter'])
}}
className="border-0 px-1 h-100 btn fab fa-x-twitter"
></button>
</CustomTooltip>
<CustomTooltip
placement={'top'}
tooltipId="overlay-tooltip"
tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id="home.remixLinkedinProfile" />}
tooltipTextClasses="border bg-light text-dark p-1 pr-3"
>
<button
onClick={() => {
openLink('https://www.linkedin.com/company/ethereum-remix/')
_paq.push(['trackEvent', 'hometab', 'socialmedia', 'linkedin'])
}}
className="border-0 px-1 h-100 btn fab fa-linkedin"
></button>
</CustomTooltip>
<CustomTooltip
placement={'top'}
tooltipId="overlay-tooltip"
tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id="home.remixMediumPosts" />}
tooltipTextClasses="border bg-light text-dark p-1 pr-3"
>
<button
onClick={() => {
openLink('https://medium.com/remix-ide')
_paq.push(['trackEvent', 'hometab', 'socialmedia', 'medium'])
}}
className="border-0 h-100 px-1 btn fab fa-medium"
></button>
</CustomTooltip>
<CustomTooltip
placement={'top'}
tooltipId="overlay-tooltip" tooltipId="overlay-tooltip"
tooltipClasses="text-nowrap" tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id="home.joinUsOnDiscord" />} tooltipText={button.textToolip}
tooltipTextClasses="border bg-light text-dark p-1 pr-3" tooltipTextClasses="border bg-light text-dark p-1 pr-3"
> >
<button <button
key={index}
onClick={() => { onClick={() => {
openLink('https://discord.gg/mh9hFCKkEq') openLink(button.urlLink)
_paq.push(['trackEvent', 'hometab', 'socialmedia', 'discord']) _paq.push(button.matomoTrackingEntry)
}} }}
className="border-0 h-100 pl-1 pr-0 btn fab fa-discord" className={`border-0 h-100 px-1 btn fab ${button.iconClass}`}
></button> ></button>
</CustomTooltip> </CustomTooltip>
))}
</span> </span>
</div> </div>
<b className="py-1 text-dark" style={{ fontStyle: 'italic' }}> <b className="py-1 text-dark" style={{ fontStyle: 'italic' }}>
@ -149,27 +139,19 @@ function HomeTabTitle() {
<a className="remixui_home_text" onClick={() => _paq.push(['trackEvent', 'hometab', 'header', 'webSite'])} target="__blank" href="https://remix-project.org"> <a className="remixui_home_text" onClick={() => _paq.push(['trackEvent', 'hometab', 'header', 'webSite'])} target="__blank" href="https://remix-project.org">
<FormattedMessage id="home.website" /> <FormattedMessage id="home.website" />
</a> </a>
<a {/* <a
className="pl-2 remixui_home_text" className="pl-2 remixui_home_text"
onClick={() => _paq.push(['trackEvent', 'hometab', 'header', 'documentation'])} onClick={() => _paq.push(['trackEvent', 'hometab', 'header', 'documentation'])}
target="__blank" target="__blank"
href="https://remix-ide.readthedocs.io/en/latest" href="https://remix-ide.readthedocs.io/en/latest"
> >
<FormattedMessage id="home.documentation" /> <FormattedMessage id="home.documentation" />
</a> </a> */}
<a
className="pl-2 remixui_home_text"
onClick={() => _paq.push(['trackEvent', 'hometab', 'header', 'remixPlugin'])}
target="__blank"
href="https://remix-plugin-docs.readthedocs.io/en/latest/"
>
<FormattedMessage id="home.remixPlugin" />
</a>
<a <a
className="pl-2 remixui_home_text" className="pl-2 remixui_home_text"
onClick={() => _paq.push(['trackEvent', 'hometab', 'header', 'remixDesktop'])} onClick={() => _paq.push(['trackEvent', 'hometab', 'header', 'remixDesktop'])}
target="__blank" target="__blank"
href="https://github.com/ethereum/remix-desktop/releases" href="https://github.com/remix-project-org/remix-desktop-insiders"
> >
<FormattedMessage id="home.remixDesktop" /> <FormattedMessage id="home.remixDesktop" />
</a> </a>

@ -2,6 +2,7 @@ import React, { useEffect, useState } from 'react'
import { Dropdown, DropdownButton } from 'react-bootstrap' import { Dropdown, DropdownButton } from 'react-bootstrap'
import DropdownItem from 'react-bootstrap/DropdownItem' import DropdownItem from 'react-bootstrap/DropdownItem'
import { localeLang } from './types/carouselTypes' import { localeLang } from './types/carouselTypes'
import { FormattedMessage } from 'react-intl'
export function LanguageOptions({ plugin }: { plugin: any }) { export function LanguageOptions({ plugin }: { plugin: any }) {
const [langOptions, setLangOptions] = useState<string>() const [langOptions, setLangOptions] = useState<string>()
@ -24,7 +25,10 @@ export function LanguageOptions({ plugin }: { plugin: any }) {
return ( return (
<> <>
<div style={{ position: 'absolute', right: "1rem", paddingTop: "0.4rem" }}> <div className="d-flex justify-content-between w-100 align-items-center pt-4">
<label style={{ fontSize: '1.2rem' }} className="ml-2 pb-0 mb-0">
<FormattedMessage id="home.featured" />
</label>
<Dropdown> <Dropdown>
<Dropdown.Toggle title={langOptions} id="languagedropdown" size="sm" style={{ backgroundColor: 'var(--secondary)', color: 'var(--text)' }}> <Dropdown.Toggle title={langOptions} id="languagedropdown" size="sm" style={{ backgroundColor: 'var(--secondary)', color: 'var(--text)' }}>
{langOptions} {langOptions}

@ -114,3 +114,42 @@
background-color: var(--body-bg); background-color: var(--body-bg);
color: var(--text); color: var(--text);
} }
.remixui_recentworkspace {
height: 2.4rem;
}
.remixui_carouselImage {
flex: 1;
height: 20rem;
width: 20rem;
}
.remixui_carouselImage_remixbeta {
flex: 1;
height: 20rem;
width: 18rem;
}
.remixui_carouselbox {
min-height: 25.12rem;
}
.remix_ui-carousel-container {
container: remix_ui-carousel-container / inline-size;
}
@container remix_ui-carousel-container (inline-size < 700px) {
.remix_ui-carouselbox {
min-height: 20rem;
margin-right: 10rem;
}
.remixui_carouselImage {
height: 12.5rem;
width: 12.5rem;
}
.remixui_recentworkspace {
height: 0.2rem;
}
}

@ -32,6 +32,7 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => {
}>({ }>({
themeQuality: themes.light themeQuality: themes.light
}) })
const [carouselWidth, setCarouselWidth] = useState(65)
useEffect(() => { useEffect(() => {
plugin.call('theme', 'currentTheme').then((theme) => { plugin.call('theme', 'currentTheme').then((theme) => {
@ -54,23 +55,39 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => {
}) })
}, []) }, [])
useEffect(() => {
const checkResolution = () => {
const width = window.innerWidth
const height = window.innerHeight
if (height < 781 && width < 1150) {
setCarouselWidth(75)
}
}
checkResolution()
return () => {
checkResolution()
}
}, [])
// border-right
return ( return (
<div className="d-flex flex-column w-100" data-id="remixUIHTAll"> <div className="d-flex flex-column w-100" data-id="remixUIHTAll">
<ThemeContext.Provider value={state.themeQuality}> <ThemeContext.Provider value={state.themeQuality}>
<div className="d-flex flex-row w-100 custom_home_bg"> <div className="d-flex flex-row w-100 custom_home_bg">
<div className="px-2 pl-3 justify-content-start d-flex border-right flex-column" id="remixUIHTLeft" style={{ width: 'inherit' }}> <div className="px-2 pl-3 justify-content-start border-right d-flex flex-column" id="remixUIHTLeft" style={{ width: 'inherit' }}>
<HomeTabTitle /> <HomeTabTitle />
<HomeTabGetStarted plugin={plugin}></HomeTabGetStarted>
{!(platform === appPlatformTypes.desktop) ? {!(platform === appPlatformTypes.desktop) ?
<HomeTabFile plugin={plugin} />: <HomeTabFile plugin={plugin} />:
<HomeTabFileElectron plugin={plugin}></HomeTabFileElectron>} <HomeTabFileElectron plugin={plugin}></HomeTabFileElectron>}
<HomeTabLearn plugin={plugin} /> {/* <HomeTabLearn plugin={plugin} /> */}
</div> </div>
<div className="pl-2 pr-3 justify-content-start d-flex flex-column" style={{ width: '65%' }} id="remixUIHTRight"> <div className="pl-2 pr-3 justify-content-start d-flex flex-column" style={{ width: `${carouselWidth}%` }} id="remixUIHTRight">
<LanguageOptions plugin={plugin}/> <LanguageOptions plugin={plugin}/>
<HomeTabFeatured></HomeTabFeatured> <HomeTabFeatured></HomeTabFeatured>
<HomeTabGetStarted plugin={plugin}></HomeTabGetStarted>
<HomeTabFeaturedPlugins plugin={plugin}></HomeTabFeaturedPlugins> <HomeTabFeaturedPlugins plugin={plugin}></HomeTabFeaturedPlugins>
<HomeTabScamAlert></HomeTabScamAlert>
</div> </div>
</div> </div>
</ThemeContext.Provider> </ThemeContext.Provider>

@ -110,3 +110,7 @@ iframe {
.highlight { .highlight {
animation: highlight 2s forwards; animation: highlight 2s forwards;
} }
.remixui_height {
height: 97vh;
}

@ -1,4 +1,5 @@
import { Profile } from '@remixproject/plugin-utils' import { Profile } from '@remixproject/plugin-utils'
import EventEmitter from 'events'
export type PluginRecord = { export type PluginRecord = {
profile: Profile profile: Profile
@ -8,3 +9,22 @@ export type PluginRecord = {
class?: string class?: string
minimized?: boolean minimized?: boolean
} }
export interface PluginProfile {
name: string
displayName: string
description: string
keywords?: string[]
icon?: string
url?: string
methods?: string[]
events?: string[]
version?: string
}
export interface StatusBarInterface extends Plugin {
htmlElement: HTMLDivElement
events: EventEmitter
dispatch: React.Dispatch<any>
setDispatch(dispatch: React.Dispatch<any>): void
}

@ -1,7 +1,7 @@
import { shortenAddress } from "@remix-ui/helper" import { shortenAddress } from "@remix-ui/helper"
import { RunTab } from "../types/run-tab" import { RunTab } from "../types/run-tab"
import { clearInstances, setAccount, setExecEnv } from "./actions" import { clearInstances, setAccount, setExecEnv } from "./actions"
import { displayNotification, displayPopUp, fetchAccountsListFailed, fetchAccountsListRequest, fetchAccountsListSuccess, setMatchPassphrase, setPassphrase } from "./payload" import { displayNotification, fetchAccountsListFailed, fetchAccountsListRequest, fetchAccountsListSuccess, setMatchPassphrase, setPassphrase } from "./payload"
import { toChecksumAddress } from '@ethereumjs/util' import { toChecksumAddress } from '@ethereumjs/util'
export const updateAccountBalances = async (plugin: RunTab, dispatch: React.Dispatch<any>) => { export const updateAccountBalances = async (plugin: RunTab, dispatch: React.Dispatch<any>) => {
@ -39,7 +39,7 @@ export const fillAccountsList = async (plugin: RunTab, dispatch: React.Dispatch<
dispatch(fetchAccountsListFailed(e.message)) dispatch(fetchAccountsListFailed(e.message))
} }
} catch (e) { } catch (e) {
dispatch(displayPopUp(`Cannot get account list: ${e}`)) plugin.call('notification', 'toast', `Cannot get account list: ${e}`)
} }
} }
@ -78,9 +78,9 @@ export const createNewBlockchainAccount = async (plugin: RunTab, dispatch: React
}, },
async (error, address) => { async (error, address) => {
if (error) { if (error) {
return dispatch(displayPopUp('Cannot create an account: ' + error)) return plugin.call('notification', 'toast', 'Cannot create an account: ' + error)
} }
dispatch(displayPopUp(`account ${address} created`)) plugin.call('notification', 'toast', `account ${address} created`)
await fillAccountsList(plugin, dispatch) await fillAccountsList(plugin, dispatch)
} }
) )
@ -89,7 +89,8 @@ export const createNewBlockchainAccount = async (plugin: RunTab, dispatch: React
export const signMessageWithAddress = (plugin: RunTab, dispatch: React.Dispatch<any>, account: string, message: string, modalContent: (hash: string, data: string) => JSX.Element, passphrase?: string) => { export const signMessageWithAddress = (plugin: RunTab, dispatch: React.Dispatch<any>, account: string, message: string, modalContent: (hash: string, data: string) => JSX.Element, passphrase?: string) => {
plugin.blockchain.signMessage(message, account, passphrase, (err, msgHash, signedData) => { plugin.blockchain.signMessage(message, account, passphrase, (err, msgHash, signedData) => {
if (err) { if (err) {
return displayPopUp(err) console.error(err)
return plugin.call('notification', 'toast', typeof err === 'string' ? err : err.message)
} }
dispatch(displayNotification('Signed Message', modalContent(msgHash, signedData), 'OK', null, () => {}, null)) dispatch(displayNotification('Signed Message', modalContent(msgHash, signedData), 'OK', null, () => {}, null))
}) })

@ -6,7 +6,7 @@ import { SolcInput, SolcOutput } from "@openzeppelin/upgrades-core"
// Used direct path to UpgradeableContract class to fix cyclic dependency error from @openzeppelin/upgrades-core library // Used direct path to UpgradeableContract class to fix cyclic dependency error from @openzeppelin/upgrades-core library
import { UpgradeableContract } from '../../../../../../node_modules/@openzeppelin/upgrades-core/dist/standalone' import { UpgradeableContract } from '../../../../../../node_modules/@openzeppelin/upgrades-core/dist/standalone'
import { DeployMode, MainnetPrompt } from "../types" import { DeployMode, MainnetPrompt } from "../types"
import { displayNotification, displayPopUp, fetchProxyDeploymentsSuccess, setDecodedResponse, updateInstancesBalance } from "./payload" import { displayNotification, fetchProxyDeploymentsSuccess, setDecodedResponse, updateInstancesBalance } from "./payload"
import { addInstance } from "./actions" import { addInstance } from "./actions"
import { addressToString, logBuilder } from "@remix-ui/helper" import { addressToString, logBuilder } from "@remix-ui/helper"
import Web3 from "web3" import Web3 from "web3"
@ -256,7 +256,7 @@ export const loadAddress = (plugin: RunTab, dispatch: React.Dispatch<any>, contr
const contractData = { name: '<at address>', abi, contract: { file: plugin.REACT_API.contracts.currentFile } } as ContractData const contractData = { name: '<at address>', abi, contract: { file: plugin.REACT_API.contracts.currentFile } } as ContractData
return addInstance(dispatch, { contractData, address, name: '<at address>' }) return addInstance(dispatch, { contractData, address, name: '<at address>' })
} else if (loadType === 'instance') { } else if (loadType === 'instance') {
if (!contract) return dispatch(displayPopUp('No compiled contracts found.')) if (!contract) return plugin.call('notification', 'toast', 'No compiled contracts found.')
const currentFile = plugin.REACT_API.contracts.currentFile const currentFile = plugin.REACT_API.contracts.currentFile
const compiler = plugin.REACT_API.contracts.contractList[currentFile].find(item => item.alias === contract.name) const compiler = plugin.REACT_API.contracts.contractList[currentFile].find(item => item.alias === contract.name)
const contractData = getSelectedContract(contract.name, compiler.compiler) const contractData = getSelectedContract(contract.name, compiler.compiler)

@ -391,7 +391,6 @@ export function ContractDropdownUI(props: ContractDropdownProps) {
/> />
</label> </label>
)} )}
</div>
{props.remixdActivated ? ( {props.remixdActivated ? (
<CustomTooltip <CustomTooltip
placement={'right'} placement={'right'}
@ -403,20 +402,16 @@ export function ContractDropdownUI(props: ContractDropdownProps) {
</span> </span>
} }
> >
<button <i style={{ cursor: 'pointer' }} onClick={(_) => {
className="btn d-flex py-0"
onClick={(_) => {
props.syncContracts() props.syncContracts()
_paq.push(['trackEvent', 'udapp', 'syncContracts', compilationSource ? compilationSource : 'compilationSourceNotYetSet']) _paq.push(['trackEvent', 'udapp', 'syncContracts', compilationSource ? compilationSource : 'compilationSourceNotYetSet'])
}} }} className="udapp_syncFramework udapp_icon fa fa-refresh" aria-hidden="true"></i>
>
<i style={{ cursor: 'pointer' }} className="fa fa-refresh mr-2 mt-2" aria-hidden="true"></i>
</button>
</CustomTooltip> </CustomTooltip>
) : null} ) : null}
</div> </div>
</div>
<div className="udapp_subcontainer"> <div className="udapp_subcontainer">
<CustomTooltip placement={'right'} tooltipClasses="text-nowrap text-left" tooltipId="remixUdappContractNamesTooltip" tooltipText={contractOptions.title}> <CustomTooltip placement={'auto-end'} tooltipClasses="text-nowrap text-left" tooltipId="remixUdappContractNamesTooltip" tooltipText={contractOptions.title}>
<select <select
ref={contractsRef} ref={contractsRef}
value={currentContract} value={currentContract}
@ -447,7 +442,7 @@ export function ContractDropdownUI(props: ContractDropdownProps) {
</div> </div>
{evmVersion && loadedContractData && ( {evmVersion && loadedContractData && (
<CustomTooltip <CustomTooltip
placement={'right'} placement={'auto-end'}
tooltipClasses="text-wrap text-left" tooltipClasses="text-wrap text-left"
tooltipId="info-evm-version-warn" tooltipId="info-evm-version-warn"
tooltipText={ tooltipText={
@ -483,6 +478,9 @@ export function ContractDropdownUI(props: ContractDropdownProps) {
isValidProxyUpgrade={isValidProxyUpgrade} isValidProxyUpgrade={isValidProxyUpgrade}
modal={props.modal} modal={props.modal}
disabled={props.selectedAccount === ''} disabled={props.selectedAccount === ''}
solcVersion={props.solCompilerVersion}
setSolcVersion={props.setCompilerVersion}
getVersion={props.getCompilerVersion}
/> />
<div className="d-flex py-1 align-items-center custom-control custom-checkbox"> <div className="d-flex py-1 align-items-center custom-control custom-checkbox">
<input <input
@ -494,7 +492,7 @@ export function ContractDropdownUI(props: ContractDropdownProps) {
checked={props.ipfsCheckedState} checked={props.ipfsCheckedState}
/> />
<CustomTooltip <CustomTooltip
placement={'right'} placement={'auto-end'}
tooltipClasses="text-wrap text-left" tooltipClasses="text-wrap text-left"
tooltipId="remixIpfsUdappTooltip" tooltipId="remixIpfsUdappTooltip"
tooltipText={ tooltipText={

@ -32,7 +32,6 @@ export function ContractGUI(props: ContractGUIProps) {
const initializeFields = useRef<Array<HTMLInputElement | null>>([]) const initializeFields = useRef<Array<HTMLInputElement | null>>([])
const basicInputRef = useRef<HTMLInputElement>() const basicInputRef = useRef<HTMLInputElement>()
const intl = useIntl() const intl = useIntl()
useEffect(() => { useEffect(() => {
if (props.deployOption && Array.isArray(props.deployOption)) { if (props.deployOption && Array.isArray(props.deployOption)) {
if (props.deployOption[0] && props.deployOption[0].title === 'Deploy with Proxy' && props.deployOption[0].active) handleDeployProxySelect(true) if (props.deployOption[0] && props.deployOption[0].title === 'Deploy with Proxy' && props.deployOption[0].active) handleDeployProxySelect(true)
@ -173,6 +172,7 @@ export function ContractGUI(props: ContractGUIProps) {
} }
const handleActionClick = async () => { const handleActionClick = async () => {
props.getVersion()
if (deployState.deploy) { if (deployState.deploy) {
const proxyInitializeString = getMultiValsString(initializeFields.current) const proxyInitializeString = getMultiValsString(initializeFields.current)
props.clickCallBack(props.initializerOptions.inputs.inputs, proxyInitializeString, ['Deploy with Proxy']) props.clickCallBack(props.initializerOptions.inputs.inputs, proxyInitializeString, ['Deploy with Proxy'])
@ -285,7 +285,7 @@ export function ContractGUI(props: ContractGUIProps) {
<div className="udapp_contractActionsContainerSingle pt-2" style={{ display: toggleContainer ? 'none' : 'flex' }}> <div className="udapp_contractActionsContainerSingle pt-2" style={{ display: toggleContainer ? 'none' : 'flex' }}>
<CustomTooltip <CustomTooltip
delay={0} delay={0}
placement={'right'} placement={'auto-end'}
tooltipClasses="text-wrap" tooltipClasses="text-wrap"
tooltipId="remixUdappInstanceButtonTooltip" tooltipId="remixUdappInstanceButtonTooltip"
tooltipText={ tooltipText={
@ -299,8 +299,8 @@ export function ContractGUI(props: ContractGUIProps) {
<div className="d-flex p-0 wrapperElement" onClick={handleActionClick} data-id={buttonOptions.dataId} data-title={buttonOptions.title}> <div className="d-flex p-0 wrapperElement" onClick={handleActionClick} data-id={buttonOptions.dataId} data-title={buttonOptions.title}>
<button <button
className={`udapp_instanceButton text-nowrap overflow-hidden text-truncate ${props.widthClass} btn btn-sm ${buttonOptions.classList}`} className={`udapp_instanceButton text-nowrap overflow-hidden text-truncate ${props.widthClass} btn btn-sm ${buttonOptions.classList}`}
data-id={buttonOptions.dataId} data-id={`${buttonOptions.dataId}`}
data-title={buttonOptions.title} data-title={`${buttonOptions.title}`}
disabled={(toggleUpgradeImp && !proxyAddress) || props.disabled || (props.inputs !== '' && basicInput === '')} disabled={(toggleUpgradeImp && !proxyAddress) || props.disabled || (props.inputs !== '' && basicInput === '')}
> >
{title} {title}
@ -368,7 +368,7 @@ export function ContractGUI(props: ContractGUIProps) {
</label> </label>
</button> </button>
</CopyToClipboard> </CopyToClipboard>
<CustomTooltip placement={'right'} tooltipClasses="text-nowrap" tooltipId="remixUdappInstanceButtonTooltip" tooltipText={buttonOptions.title}> <CustomTooltip placement={'auto-end'} tooltipClasses="text-nowrap" tooltipId="remixUdappInstanceButtonTooltip" tooltipText={buttonOptions.title}>
<div onClick={handleExpandMultiClick}> <div onClick={handleExpandMultiClick}>
<button <button
type="button" type="button"

@ -24,12 +24,12 @@ export function EnvironmentUI(props: EnvironmentProps) {
<label id="selectExEnv" className="udapp_settingsLabel"> <label id="selectExEnv" className="udapp_settingsLabel">
<FormattedMessage id="udapp.environment" /> <FormattedMessage id="udapp.environment" />
<CustomTooltip placement={'right'} tooltipClasses="text-nowrap" tooltipId="info-recorder" tooltipText={<FormattedMessage id="udapp.tooltipText2" />}> <CustomTooltip placement={'auto-end'} tooltipClasses="text-nowrap" tooltipId="info-recorder" tooltipText={<FormattedMessage id="udapp.tooltipText2" />}>
<a href="https://chainlist.org/" target="_blank"> <a href="https://chainlist.org/" target="_blank">
<i className={'ml-2 fas fa-plug'} aria-hidden="true"></i> <i className={'ml-2 fas fa-plug'} aria-hidden="true"></i>
</a> </a>
</CustomTooltip> </CustomTooltip>
<CustomTooltip placement={'right'} tooltipClasses="text-wrap" tooltipId="runAndDeployAddresstooltip" tooltipText={<FormattedMessage id="udapp.environmentDocs" />}> <CustomTooltip placement={'auto-end'} tooltipClasses="text-wrap" tooltipId="runAndDeployAddresstooltip" tooltipText={<FormattedMessage id="udapp.environmentDocs" />}>
<a href="https://remix-ide.readthedocs.io/en/latest/run.html#environment" target="_blank" rel="noreferrer"> <a href="https://remix-ide.readthedocs.io/en/latest/run.html#environment" target="_blank" rel="noreferrer">
<i className="udapp_infoDeployAction ml-2 fas fa-info-circle"></i> <i className="udapp_infoDeployAction ml-2 fas fa-info-circle"></i>
</a> </a>
@ -41,7 +41,7 @@ export function EnvironmentUI(props: EnvironmentProps) {
{isL2(currentProvider && currentProvider.displayName)} {isL2(currentProvider && currentProvider.displayName)}
{currentProvider && currentProvider.displayName} {currentProvider && currentProvider.displayName}
{currentProvider && bridges[currentProvider.displayName] && ( {currentProvider && bridges[currentProvider.displayName] && (
<CustomTooltip placement={'right'} tooltipClasses="text-nowrap" tooltipId="info-recorder" tooltipText={<FormattedMessage id="udapp.tooltipText3" />}> <CustomTooltip placement={'auto-end'} tooltipClasses="text-nowrap" tooltipId="info-recorder" tooltipText={<FormattedMessage id="udapp.tooltipText3" />}>
<i <i
style={{ fontSize: 'medium' }} style={{ fontSize: 'medium' }}
className={'ml-2 fa fa-rocket-launch'} className={'ml-2 fa fa-rocket-launch'}

@ -64,7 +64,7 @@ export function GasLimitUI(props: GasPriceProps) {
<label className="mb-1 w-50 form-check-label custom-control-label" htmlFor="glManualConfig" data-id="glManualConfiguration"> <label className="mb-1 w-50 form-check-label custom-control-label" htmlFor="glManualConfig" data-id="glManualConfiguration">
<FormattedMessage id="udapp.gasLimitManual" /> <FormattedMessage id="udapp.gasLimitManual" />
</label> </label>
<CustomTooltip placement={'right'} tooltipClasses="text-nowrap" tooltipId="remixGasPriceTooltip" tooltipText={<FormattedMessage id="udapp.tooltipText4" />}> <CustomTooltip placement={'auto-end'} tooltipClasses="text-nowrap" tooltipId="remixGasPriceTooltip" tooltipText={<FormattedMessage id="udapp.tooltipText4" />}>
<input <input
type="number" type="number"
ref={inputComponent} ref={inputComponent}

@ -44,6 +44,8 @@ export function InstanceContainerUI(props: InstanceContainerProps) {
plugin={props.plugin} plugin={props.plugin}
exEnvironment={props.exEnvironment} exEnvironment={props.exEnvironment}
editInstance={props.editInstance} editInstance={props.editInstance}
solcVersion={props.solcVersion}
getVersion={props.getVersion}
/> />
) )
})} })}
@ -62,7 +64,7 @@ export function InstanceContainerUI(props: InstanceContainerProps) {
</CustomTooltip> </CustomTooltip>
{instanceList.length > 0 ? ( {instanceList.length > 0 ? (
<CustomTooltip <CustomTooltip
placement="right" placement={'auto-end'}
tooltipClasses="text-nowrap" tooltipClasses="text-nowrap"
tooltipId="deployAndRunClearInstancesTooltip" tooltipId="deployAndRunClearInstancesTooltip"
tooltipText={<FormattedMessage id="udapp.deployAndRunClearInstances" />} tooltipText={<FormattedMessage id="udapp.deployAndRunClearInstances" />}
@ -92,6 +94,8 @@ export function InstanceContainerUI(props: InstanceContainerProps) {
plugin={props.plugin} plugin={props.plugin}
exEnvironment={props.exEnvironment} exEnvironment={props.exEnvironment}
editInstance={props.editInstance} editInstance={props.editInstance}
solcVersion={props.solcVersion}
getVersion={props.getVersion}
/> />
) )
})} })}

@ -41,7 +41,7 @@ export function RecorderUI(props: RecorderProps) {
<FormattedMessage id="udapp.transactionsRecorded" /> <FormattedMessage id="udapp.transactionsRecorded" />
</label> </label>
<CustomTooltip <CustomTooltip
placement={'right'} placement={'auto-end'}
tooltipClasses="text-nowrap" tooltipClasses="text-nowrap"
tooltipId="recordedTransactionsCounttooltip" tooltipId="recordedTransactionsCounttooltip"
tooltipText={<FormattedMessage id="udapp.transactionsCountTooltip" />} tooltipText={<FormattedMessage id="udapp.transactionsCountTooltip" />}
@ -51,7 +51,7 @@ export function RecorderUI(props: RecorderProps) {
</div> </div>
</CustomTooltip> </CustomTooltip>
<CustomTooltip <CustomTooltip
placement={'right'} placement={'auto-end'}
tooltipClasses="text-nowrap" tooltipClasses="text-nowrap"
tooltipId="recordedTransactionsWalkthroughtooltip" tooltipId="recordedTransactionsWalkthroughtooltip"
tooltipText={<FormattedMessage id="udapp.transactionsWalkthroughTooltip" />} tooltipText={<FormattedMessage id="udapp.transactionsWalkthroughTooltip" />}
@ -77,7 +77,7 @@ export function RecorderUI(props: RecorderProps) {
<div className="mb-1 mt-1 custom-control custom-checkbox mb-1" id='udappRecorderUseLatest'> <div className="mb-1 mt-1 custom-control custom-checkbox mb-1" id='udappRecorderUseLatest'>
<input ref={inputLive} type="checkbox" id="livemode-recorder" className="custom-control-input custom-select" name="input-livemode" /> <input ref={inputLive} type="checkbox" id="livemode-recorder" className="custom-control-input custom-select" name="input-livemode" />
<CustomTooltip <CustomTooltip
placement={'right'} placement={'auto-end'}
tooltipClasses="text-wrap" tooltipClasses="text-wrap"
tooltipId="tooltip-livemode-recorder" tooltipId="tooltip-livemode-recorder"
tooltipText={ tooltipText={

@ -6,10 +6,7 @@ import { FuncABI } from '@remix-project/core-plugin'
import { CopyToClipboard } from '@remix-ui/clipboard' import { CopyToClipboard } from '@remix-ui/clipboard'
import * as remixLib from '@remix-project/remix-lib' import * as remixLib from '@remix-project/remix-lib'
import * as ethJSUtil from '@ethereumjs/util' import * as ethJSUtil from '@ethereumjs/util'
import axios from 'axios'
import { AppModal } from '@remix-ui/app'
import { ContractGUI } from './contractGUI' import { ContractGUI } from './contractGUI'
import { SolScanTable } from './solScanTable'
import { TreeView, TreeViewItem } from '@remix-ui/tree-view' import { TreeView, TreeViewItem } from '@remix-ui/tree-view'
import { BN } from 'bn.js' import { BN } from 'bn.js'
import { CustomTooltip, is0XPrefixed, isHexadecimal, isNumeric, shortenAddress } from '@remix-ui/helper' import { CustomTooltip, is0XPrefixed, isHexadecimal, isNumeric, shortenAddress } from '@remix-ui/helper'
@ -28,8 +25,6 @@ export function UniversalDappUI(props: UdappProps) {
const [evmBC, setEvmBC] = useState(null) const [evmBC, setEvmBC] = useState(null)
const [instanceBalance, setInstanceBalance] = useState(0) const [instanceBalance, setInstanceBalance] = useState(0)
const getVersion = () => window.location.href.split('=')[5].split('+')[0].split('-')[1]
useEffect(() => { useEffect(() => {
if (!props.instance.abi) { if (!props.instance.abi) {
const abi = txHelper.sortAbiFunction(props.instance.contractData.abi) const abi = txHelper.sortAbiFunction(props.instance.contractData.abi)
@ -220,100 +215,6 @@ export function UniversalDappUI(props: UdappProps) {
setCalldataValue(value) setCalldataValue(value)
} }
const handleScanContinue = async () => {
await props.plugin.call('notification', 'toast', 'Processing data to scan...')
_paq.push(['trackEvent', 'udapp', 'solidityScan', 'initiateScan'])
const workspace = await props.plugin.call('filePanel', 'getCurrentWorkspace')
const fileName = props.instance.filePath || `${workspace.name}/${props.instance.contractData.contract.file}`
const filePath = `.workspaces/${fileName}`
const file = await props.plugin.call('fileManager', 'readFile', filePath)
const urlResponse = await axios.post(`https://solidityscan.remixproject.org/uploadFile`, { file, fileName })
if (urlResponse.data.status === 'success') {
const ws = new WebSocket('wss://solidityscan.remixproject.org/solidityscan')
ws.addEventListener('error', console.error);
ws.addEventListener('open', async (event) => {
await props.plugin.call('notification', 'toast', 'Initiating scan...')
})
ws.addEventListener('message', async (event) => {
const data = JSON.parse(event.data)
if (data.type === "auth_token_register" && data.payload.message === "Auth token registered.") {
// Message on Bearer token successful registration
const reqToInitScan = {
"action": "message",
"payload": {
"type": "private_project_scan_initiate",
"body": {
"file_urls": [
urlResponse.data.result.url
],
"project_name": "RemixProject",
"project_type": "new"
}
}
}
ws.send(JSON.stringify(reqToInitScan))
} else if (data.type === "scan_status" && data.payload.scan_status === "download_failed") {
// Message on failed scan
_paq.push(['trackEvent', 'udapp', 'solidityScan', 'scanFailed'])
const modal: AppModal = {
id: 'SolidityScanError',
title: <FormattedMessage id="udapp.solScan.errModalTitle" />,
message: data.payload.scan_status_err_message,
okLabel: 'Close'
}
await props.plugin.call('notification', 'modal', modal)
} else if (data.type === "scan_status" && data.payload.scan_status === "scan_done") {
// Message on successful scan
_paq.push(['trackEvent', 'udapp', 'solidityScan', 'scanSuccess'])
const url = data.payload.scan_details.link
const { data: scanData } = await axios.post('https://solidityscan.remixproject.org/downloadResult', { url })
const scanDetails: Record<string, any>[] = scanData.scan_report.multi_file_scan_details
let modal: AppModal
if (scanDetails && scanDetails.length) {
modal = {
id: 'SolidityScanSuccess',
title: <FormattedMessage id="udapp.solScan.successModalTitle" />,
message: <SolScanTable scanDetails={scanDetails} fileName={fileName}/>,
okLabel: 'Close',
modalParentClass: 'modal-xl'
}
} else {
modal = {
id: 'SolidityScanError',
title: <FormattedMessage id="udapp.solScan.errModalTitle" />,
message: "Some error occurred! Please try again",
okLabel: 'Close'
}
}
await props.plugin.call('notification', 'modal', modal)
}
})
}
}
const askPermissionToScan = async () => {
_paq.push(['trackEvent', 'udapp', 'solidityScan', 'askPermissionToScan'])
const modal: AppModal = {
id: 'SolidityScanPermissionHandler',
title: <FormattedMessage id="udapp.solScan.modalTitle" />,
message: <FormattedMessage id="udapp.solScan.modalMessage" />,
okLabel: <FormattedMessage id="udapp.solScan.modalOkLabel" />,
okFn: handleScanContinue,
cancelLabel: <FormattedMessage id="udapp.solScan.modalCancelLabel" />
}
await props.plugin.call('notification', 'modal', modal)
}
const label = (key: string | number, value: string) => { const label = (key: string | number, value: string) => {
return ( return (
<div className="d-flex mt-2 flex-row label_item"> <div className="d-flex mt-2 flex-row label_item">
@ -406,9 +307,6 @@ export function UniversalDappUI(props: UdappProps) {
></i> ></i>
</CustomTooltip> </CustomTooltip>
)} )}
<CustomTooltip placement="top" tooltipClasses="text-nowrap" tooltipId="udapp_udappSolScanTooltip" tooltipText={<FormattedMessage id="udapp.solScan.iconTooltip" />}>
<i className="fas fa-qrcode p-0" onClick={askPermissionToScan}></i>
</CustomTooltip>
</div> </div>
</div> </div>
{ props.isPinnedContract && props.instance.pinnedAt ? ( { props.isPinnedContract && props.instance.pinnedAt ? (
@ -435,6 +333,7 @@ export function UniversalDappUI(props: UdappProps) {
return ( return (
<div key={index}> <div key={index}>
<ContractGUI <ContractGUI
getVersion={props.getVersion}
funcABI={funcABI} funcABI={funcABI}
clickCallBack={(valArray: {name: string; type: string}[], inputsValues: string) => { clickCallBack={(valArray: {name: string; type: string}[], inputsValues: string) => {
runTransaction(lookupOnly, funcABI, valArray, inputsValues, index) runTransaction(lookupOnly, funcABI, valArray, inputsValues, index)
@ -469,10 +368,21 @@ export function UniversalDappUI(props: UdappProps) {
<div className="py-2 border-top d-flex justify-content-start flex-grow-1"> <div className="py-2 border-top d-flex justify-content-start flex-grow-1">
<FormattedMessage id="udapp.lowLevelInteractions" /> <FormattedMessage id="udapp.lowLevelInteractions" />
</div> </div>
<CustomTooltip placement={'bottom-end'} tooltipClasses="text-wrap" tooltipId="receiveEthDocstoolTip" tooltipText={<FormattedMessage id="udapp.tooltipText8" />}> <CustomTooltip
<a href={`https://solidity.readthedocs.io/en/${getVersion()}/contracts.html#receive-ether-function`} target="_blank" rel="noreferrer"> placement={'bottom-end'}
tooltipClasses="text-wrap"
tooltipId="receiveEthDocstoolTip"
tooltipText={<FormattedMessage id="udapp.tooltipText8" />}
>
{ // receive method added to solidity v0.6.x. use this as diff.
props.solcVersion.canReceive === false ? (
<a href={`https://solidity.readthedocs.io/en/v${props.solcVersion.version}/contracts.html`} target="_blank" rel="noreferrer">
<i aria-hidden="true" className="fas fa-info my-2 mr-1"></i> <i aria-hidden="true" className="fas fa-info my-2 mr-1"></i>
</a> </a>
) :<a href={`https://solidity.readthedocs.io/en/v${props.solcVersion.version}/contracts.html#receive-ether-function`} target="_blank" rel="noreferrer">
<i aria-hidden="true" className="fas fa-info my-2 mr-1"></i>
</a>
}
</CustomTooltip> </CustomTooltip>
</div> </div>
<div className="d-flex flex-column align-items-start"> <div className="d-flex flex-column align-items-start">

@ -25,6 +25,9 @@
.udapp_settingsCompiledBy { .udapp_settingsCompiledBy {
margin-bottom: 4px; margin-bottom: 4px;
} }
.udapp_syncFramework {
margin-bottom: 4px;
}
.udapp_environment { .udapp_environment {
display: flex; display: flex;
align-items: center; align-items: center;

@ -1,5 +1,6 @@
// eslint-disable-next-line no-use-before-define // eslint-disable-next-line no-use-before-define
import React, { Fragment, useEffect, useReducer, useState } from 'react' import React, { Fragment, useEffect, useReducer, useState } from 'react'
import semver from 'semver'
import { FormattedMessage } from 'react-intl' import { FormattedMessage } from 'react-intl'
import { ModalDialog } from '@remix-ui/modal-dialog' import { ModalDialog } from '@remix-ui/modal-dialog'
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
@ -80,6 +81,23 @@ export function RunTabUI(props: RunTabProps) {
const [runTab, dispatch] = useReducer(runTabReducer, initialState) const [runTab, dispatch] = useReducer(runTabReducer, initialState)
const REACT_API = { runTab } const REACT_API = { runTab }
const currentfile = plugin.config.get('currentFile') const currentfile = plugin.config.get('currentFile')
const [solcVersion, setSolcVersion] = useState<{version: string, canReceive: boolean}>({ version: '', canReceive: true })
const getVersion = () => {
let version = '0.8.25'
try {
const regVersion = window.location.href.match(/soljson-v(.*)\+commit/g)
if (regVersion && regVersion[1]) version = regVersion[1]
if (semver.lt(version, '0.6.0')) {
setSolcVersion({ version: version, canReceive: false })
} else {
setSolcVersion({ version: version, canReceive: true })
}
} catch (e) {
setSolcVersion({ version, canReceive: true })
console.log(e)
}
}
useEffect(() => { useEffect(() => {
if (!props.initialState) { if (!props.initialState) {
@ -306,6 +324,9 @@ export function RunTabUI(props: RunTabProps) {
isValidProxyAddress={isValidProxyAddress} isValidProxyAddress={isValidProxyAddress}
isValidProxyUpgrade={isValidProxyUpgrade} isValidProxyUpgrade={isValidProxyUpgrade}
proxy={runTab.proxy} proxy={runTab.proxy}
solCompilerVersion={solcVersion}
setCompilerVersion={setSolcVersion}
getCompilerVersion={getVersion}
/> />
<RecorderUI <RecorderUI
plugin={plugin} plugin={plugin}
@ -330,6 +351,8 @@ export function RunTabUI(props: RunTabProps) {
mainnetPrompt={mainnetPrompt} mainnetPrompt={mainnetPrompt}
runTransactions={executeTransactions} runTransactions={executeTransactions}
sendValue={runTab.sendValue} sendValue={runTab.sendValue}
solcVersion={solcVersion}
getVersion={getVersion}
getFuncABIInputs={getFuncABIValues} getFuncABIInputs={getFuncABIValues}
exEnvironment={runTab.selectExEnv} exEnvironment={runTab.selectExEnv}
editInstance={(instance) => { editInstance={(instance) => {

@ -279,6 +279,11 @@ export interface ContractDropdownProps {
isValidProxyAddress?: (address: string) => Promise<boolean>, isValidProxyAddress?: (address: string) => Promise<boolean>,
isValidProxyUpgrade?: (proxyAddress: string, contractName: string, solcInput: SolcInput, solcOuput: SolcOutput, solcVersion: string) => Promise<LayoutCompatibilityReport | { ok: boolean, pass: boolean, warning: boolean }>, isValidProxyUpgrade?: (proxyAddress: string, contractName: string, solcInput: SolcInput, solcOuput: SolcOutput, solcVersion: string) => Promise<LayoutCompatibilityReport | { ok: boolean, pass: boolean, warning: boolean }>,
proxy: { deployments: { address: string, date: string, contractName: string }[] } proxy: { deployments: { address: string, date: string, contractName: string }[] }
solCompilerVersion: { version: string, canReceive: boolean }
setCompilerVersion: React.Dispatch<React.SetStateAction<{
version: string;
canReceive: boolean;}>>
getCompilerVersion: () => void
} }
export interface RecorderProps { export interface RecorderProps {
@ -343,6 +348,8 @@ export interface InstanceContainerProps {
exEnvironment: string exEnvironment: string
editInstance: (instance) => void editInstance: (instance) => void
plugin: RunTab plugin: RunTab
solcVersion: { version: string, canReceive: boolean }
getVersion: any
} }
export interface Modal { export interface Modal {
@ -397,6 +404,11 @@ export interface ContractGUIProps {
isValidProxyAddress?: (address: string) => Promise<boolean>, isValidProxyAddress?: (address: string) => Promise<boolean>,
isValidProxyUpgrade?: (proxyAddress: string) => Promise<LayoutCompatibilityReport | { ok: boolean, pass: boolean, warning: boolean }>, isValidProxyUpgrade?: (proxyAddress: string) => Promise<LayoutCompatibilityReport | { ok: boolean, pass: boolean, warning: boolean }>,
modal?: (title: string, message: string | JSX.Element, okLabel: string, okFn: () => void, cancelLabel?: string, cancelFn?: () => void, okBtnClass?: string, cancelBtnClass?: string) => void modal?: (title: string, message: string | JSX.Element, okLabel: string, okFn: () => void, cancelLabel?: string, cancelFn?: () => void, okBtnClass?: string, cancelBtnClass?: string) => void
solcVersion?: { version: string, canReceive: boolean }
setSolcVersion?: React.Dispatch<React.SetStateAction<{
version: string;
canReceive: boolean;}>>
getVersion: () => void
} }
export interface MainnetProps { export interface MainnetProps {
network: Network, network: Network,
@ -452,6 +464,8 @@ export interface UdappProps {
exEnvironment: string exEnvironment: string
editInstance: (instance) => void editInstance: (instance) => void
plugin: RunTab plugin: RunTab
solcVersion: { version: string, canReceive: boolean }
getVersion: () => string
} }
export interface DeployButtonProps { export interface DeployButtonProps {

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save