Merge branch 'master' into solcoder/explain_contract

pull/5370/head
STetsing 11 months ago committed by GitHub
commit a76fcb68c3
  1. 4
      .github/workflows/pr-reminder.yml
  2. 6
      apps/circuit-compiler/src/app/actions/index.ts
  3. 1
      apps/learneth/src/redux/models/workshop.ts
  4. 4
      apps/remix-ide-e2e/package.json
  5. 2
      apps/remix-ide-e2e/src/commands/openFile.ts
  6. 1
      apps/remix-ide-e2e/src/local-plugin/src/app/app.tsx
  7. 2
      apps/remix-ide-e2e/src/tests/etherscan_api.test.ts
  8. 2
      apps/remix-ide-e2e/src/tests/generalSettings.test.ts
  9. 42
      apps/remix-ide-e2e/src/tests/gist.test.ts
  10. 353
      apps/remix-ide-e2e/src/tests/proxy_oz_v5_non_shanghai_runtime.test.ts
  11. 115
      apps/remix-ide-e2e/src/tests/runAndDeploy.test.ts
  12. 2
      apps/remix-ide-e2e/src/tests/solidityUnittests.test.ts
  13. 2
      apps/remix-ide-e2e/src/tests/specialFunctions.test.ts
  14. 40
      apps/remix-ide-e2e/src/tests/url.test.ts
  15. 2
      apps/remix-ide-e2e/src/tests/vyper_api.test.ts
  16. 22
      apps/remix-ide-e2e/yarn.lock
  17. 3
      apps/remix-ide/src/app/editor/editor.js
  18. 10
      apps/remix-ide/src/app/files/fileManager.ts
  19. 16
      apps/remix-ide/src/app/panels/tab-proxy.js
  20. 41
      apps/remix-ide/src/app/panels/terminal.tsx
  21. 4
      apps/remix-ide/src/app/tabs/locales/en/filePanel.json
  22. 3
      apps/remix-ide/src/app/tabs/locales/en/settings.json
  23. 4
      apps/remix-ide/src/app/tabs/locales/en/terminal.json
  24. 22
      apps/remix-ide/src/app/tabs/locales/en/udapp.json
  25. 2
      apps/remix-ide/src/app/tabs/locales/es/filePanel.json
  26. 3
      apps/remix-ide/src/app/tabs/locales/es/settings.json
  27. 2
      apps/remix-ide/src/app/tabs/locales/es/udapp.json
  28. 2
      apps/remix-ide/src/app/tabs/locales/fr/filePanel.json
  29. 3
      apps/remix-ide/src/app/tabs/locales/fr/settings.json
  30. 2
      apps/remix-ide/src/app/tabs/locales/fr/udapp.json
  31. 2
      apps/remix-ide/src/app/tabs/locales/it/filePanel.json
  32. 3
      apps/remix-ide/src/app/tabs/locales/it/settings.json
  33. 2
      apps/remix-ide/src/app/tabs/locales/it/udapp.json
  34. 2
      apps/remix-ide/src/app/tabs/locales/zh/filePanel.json
  35. 3
      apps/remix-ide/src/app/tabs/locales/zh/settings.json
  36. 2
      apps/remix-ide/src/app/tabs/locales/zh/udapp.json
  37. 18
      apps/remix-ide/src/app/tabs/web3-provider.js
  38. 4
      apps/remix-ide/src/app/udapp/run-tab.js
  39. BIN
      apps/remix-ide/src/assets/img/vyperLogo2.webp
  40. 35
      apps/remix-ide/src/blockchain/blockchain.tsx
  41. 86
      apps/remix-ide/src/blockchain/execution-context.js
  42. 32
      apps/remix-ide/src/blockchain/providers/vm.ts
  43. 2
      apps/remix-ide/src/blockchain/providers/worker-vm.ts
  44. 2
      apps/remix-ide/src/remixAppManager.js
  45. 2
      apps/remix-ide/team-best-practices.md
  46. 39
      apps/remixdesktop/yarn.lock
  47. 2
      apps/solhint/src/profile.json
  48. 6
      apps/solhint/yarn.lock
  49. 44
      apps/vyper/src/app/app.css
  50. 81
      apps/vyper/src/app/app.tsx
  51. 23
      apps/vyper/src/app/components/CompileErrorCard.tsx
  52. 2
      apps/vyper/src/app/components/CompilerButton.tsx
  53. 26
      apps/vyper/src/app/components/CustomAccordionToggle.tsx
  54. 17
      apps/vyper/src/app/components/VyperResult.tsx
  55. 192
      apps/vyper/src/app/utils/compiler.tsx
  56. 18
      apps/vyper/src/app/utils/remix-client.tsx
  57. 4
      apps/vyper/src/index.html
  58. 6
      apps/vyper/src/profile.json
  59. 8
      libs/ghaction-helper/package.json
  60. 8
      libs/remix-analyzer/package.json
  61. 6
      libs/remix-astwalker/package.json
  62. 1
      libs/remix-core-plugin/src/index.ts
  63. 6
      libs/remix-core-plugin/src/lib/compiler-fetch-and-compile.ts
  64. 6
      libs/remix-core-plugin/src/lib/constants/uups.ts
  65. 7
      libs/remix-core-plugin/src/lib/gist-handler.ts
  66. 66
      libs/remix-core-plugin/src/lib/helpers/fetch-blockscout.ts
  67. 12
      libs/remix-debug/package.json
  68. 4
      libs/remix-lib/package.json
  69. 1
      libs/remix-lib/src/execution/logsManager.ts
  70. 27
      libs/remix-lib/src/execution/txRunnerVM.ts
  71. 6
      libs/remix-simulator/package.json
  72. 25
      libs/remix-simulator/src/methods/transactions.ts
  73. 35
      libs/remix-simulator/src/provider.ts
  74. 54
      libs/remix-simulator/src/vm-context.ts
  75. 6
      libs/remix-solidity/package.json
  76. 2
      libs/remix-solidity/src/compiler/types.ts
  77. 10
      libs/remix-tests/package.json
  78. 3
      libs/remix-ui/editor/src/lib/remix-ui-editor.tsx
  79. 2
      libs/remix-ui/panel/src/lib/plugins/panel.css
  80. 4
      libs/remix-ui/run-tab/src/lib/actions/actions.ts
  81. 3
      libs/remix-ui/run-tab/src/lib/actions/deploy.ts
  82. 4
      libs/remix-ui/run-tab/src/lib/actions/events.ts
  83. 2
      libs/remix-ui/run-tab/src/lib/actions/index.ts
  84. 5
      libs/remix-ui/run-tab/src/lib/actions/payload.ts
  85. 2
      libs/remix-ui/run-tab/src/lib/components/environment.tsx
  86. 65
      libs/remix-ui/run-tab/src/lib/components/instanceContainerUI.tsx
  87. 102
      libs/remix-ui/run-tab/src/lib/components/universalDappUI.tsx
  88. 26
      libs/remix-ui/run-tab/src/lib/reducers/runTab.ts
  89. 4
      libs/remix-ui/run-tab/src/lib/types/index.ts
  90. 5
      libs/remix-ui/settings/src/lib/constants.ts
  91. 33
      libs/remix-ui/settings/src/lib/remix-ui-settings.tsx
  92. 5
      libs/remix-ui/settings/src/lib/settingsAction.ts
  93. 18
      libs/remix-ui/settings/src/lib/settingsReducer.ts
  94. 77
      libs/remix-ui/settings/src/lib/sindri-settings.tsx
  95. 13
      libs/remix-ui/settings/src/types/index.ts
  96. 10
      libs/remix-ui/solidity-compiler/src/lib/api/compiler-api.ts
  97. 2
      libs/remix-ui/terminal/src/index.ts
  98. 41
      libs/remix-ui/terminal/src/lib/components/remix-ui-terminal-bar.tsx
  99. 3
      libs/remix-ui/terminal/src/lib/components/remix-ui-terminal-menu-buttons.css
  100. 31
      libs/remix-ui/terminal/src/lib/components/remix-ui-terminal-menu-buttons.tsx
  101. Some files were not shown because too many files have changed in this diff Show More

@ -2,7 +2,7 @@ name: PRs reviews reminder
on:
schedule:
- cron: "0 8-17/8 * * 1-5"
- cron: "0 8 * * 1-5"
workflow_dispatch:
jobs:
@ -14,4 +14,4 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }}
freeze-date: '2024-02-26T18:00:00Z'
freeze-date: '2024-03-25T18:00:00Z'

@ -6,7 +6,7 @@ export const compileCircuit = async (plugin: CircomPluginClient, appState: AppSt
if (appState.status !== "compiling") {
await plugin.compile(appState.filePath, { version: appState.version, prime: appState.primeValue })
} else {
console.log('Exisiting circuit compilation in progress')
console.log('Existing circuit compilation in progress')
}
} catch (e) {
plugin.internalEvents.emit('circuit_compiling_errored', e)
@ -19,7 +19,7 @@ export const generateR1cs = async (plugin: CircomPluginClient, appState: AppStat
if (appState.status !== "generating") {
await plugin.generateR1cs(appState.filePath, { version: appState.version, prime: appState.primeValue })
} else {
console.log('Exisiting r1cs generation in progress')
console.log('Existing r1cs generation in progress')
}
} catch (e) {
plugin.internalEvents.emit('circuit_generating_r1cs_errored', e)
@ -34,7 +34,7 @@ export const computeWitness = async (plugin: CircomPluginClient, status: string,
await plugin.computeWitness(input)
} else {
console.log('Exisiting witness computation in progress')
console.log('Existing witness computation in progress')
}
} catch (e) {
plugin.internalEvents.emit('circuit_computing_witness_errored', e)

@ -56,7 +56,6 @@ const Model: ModelType = {
const url = `${apiUrl}/clone/${encodeURIComponent(payload.name)}/${payload.branch}?${Math.random()}`
console.log('loading ', url)
const {data} = yield axios.get(url)
console.log(data)
const repoId = `${payload.name}-${payload.branch}`
for (let i = 0; i < data.ids.length; i++) {

@ -6,8 +6,8 @@
"npm": "^6.14.15"
},
"dependencies": {
"@openzeppelin/contracts": "^5.0.0",
"@openzeppelin/contracts-upgradeable": "^5.0.0",
"@openzeppelin/contracts": "^5.0.2",
"@openzeppelin/contracts-upgradeable": "^5.0.2",
"@openzeppelin/upgrades-core": "^1.30.0",
"@openzeppelin/wizard": "^0.4.0",
"@remix-project/remixd": "../../dist/libs/remixd",

@ -21,7 +21,7 @@ function openFile (browser: NightwatchBrowser, name: string, done: VoidFunction)
// if side panel is shown, check this is the file panel
browser.element('css selector', '[data-id="verticalIconsKindfilePanel"] img[data-id="selected"]', (result) => {
if (result.status === 0) {
done()
done()
} else browser.clickLaunchIcon('filePanel').perform(() => {
done()
})

@ -46,7 +46,6 @@ function App() {
const customProfiles = ['menuicons', 'tabs', 'solidityUnitTesting', 'hardhat-provider', 'notification']
client.testCommand = async (data: any) => {
console.log(data)
methodLog(data)
}

@ -33,7 +33,7 @@ module.exports = {
.execute(() => {
(document.querySelector('*[data-id="basic-http-providerModalDialogContainer-react"] input[data-id="modalDialogCustomPromp"]') as any).focus()
}, [], () => {})
.setValue('[data-id="modalDialogCustomPromp"]', 'https://remix-goerli.ethdevops.io')
.setValue('[data-id="modalDialogCustomPromp"]', 'https://rpc.ankr.com/eth_goerli')
.modalFooterOKClick('basic-http-provider')
.clickLaunchIcon('solidity') // compile
.testContracts('Owner_1.sol', { content: verifiedContract }, ['Owner'])

@ -152,7 +152,7 @@ module.exports = {
.scrollAndClick('*[data-id="settingsTabLocaleLabelen"]')
.pause(2000)
.assert.containsText('*[data-id="sidePanelSwapitTitle"]', 'SETTINGS')
.assert.containsText('*[data-id="listenNetworkCheckInput"]', 'listen on all transactions')
.assert.containsText('*[data-id="listenNetworkCheckInput"]', 'Listen on all transactions')
.assert.containsText('*[data-id="settingsTabGenerateContractMetadataLabel"]', 'Generate contract metadata')
.assert.containsText('*[data-id="settingsAutoCompleteLabel"]', 'Enable code completion in editor')
.assert.containsText('*[data-id="settingsShowGasLabel"]', 'Display gas estimates in editor')

@ -38,7 +38,7 @@ module.exports = {
.executeScriptInTerminal(`remix.loadgist('${gistid}')`)
// .perform((done) => { if (runtimeBrowser === 'chrome') { browser.openFile('gists') } done() })
.waitForElementVisible(`[data-id="treeViewLitreeViewItemREADME.txt"]`)
.openFile(`README.txt`)
// Remix publish to gist
/* .click('*[data-id="fileExplorerNewFilepublishToGist"]')
@ -111,7 +111,7 @@ module.exports = {
.click('[data-id="settingsTabRemoveGistToken"]')
.clickLaunchIcon('filePanel')
.click('*[data-id="workspacesMenuDropdown"]')
.click('*[data-id="workspacepublishToGist"]')
.click('*[data-id="workspacepublishToGist"]')
.waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok')
.execute(function () { (document.querySelector('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') as HTMLElement).click() })
.pause(10000)
@ -145,24 +145,24 @@ module.exports = {
.openFile(`README.txt`)
.waitForElementVisible(`div[data-path='gist ${testData.validGistId}/README.txt']`)
.assert.containsText(`div[data-path='gist ${testData.validGistId}/README.txt'] > span`, 'README.txt')
},
},
'Load Gist from URL and verify truncated files are loaded #group3': function (browser: NightwatchBrowser) {
const gistId = '1b179bf1b92c8b0664b4cbe61774e15d'
browser
.url('http://127.0.0.1:8080/#gist=' + gistId) // loading the gist
.refreshPage()
.currentWorkspaceIs('gist ' + gistId)
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 15000)
.waitForElementVisible(`#fileExplorerView li[data-path='contracts']`, 30000)
.openFile(`contracts/2_Owner.sol`)
.getEditorValue((content) => {
browser.assert.ok(content.indexOf('contract Owner {') !== -1)
})
.click('*[data-id="workspacesMenuDropdown"]')
.click('*[data-id="workspacepublishToGist"]')
.modalFooterOKClick('fileSystem')
.waitForElementVisible('*[data-shared="tooltipPopup"]', 5000)
.assert.containsText('*[data-shared="tooltipPopup"]', 'Saving gist (' + gistId + ') ...')
}
'Load Gist from URL and verify truncated files are loaded #group3': function (browser: NightwatchBrowser) {
const gistId = '1b179bf1b92c8b0664b4cbe61774e15d'
browser
.url('http://127.0.0.1:8080/#gist=' + gistId) // loading the gist
.refreshPage()
.currentWorkspaceIs('gist ' + gistId)
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 15000)
.waitForElementVisible(`#fileExplorerView li[data-path='contracts']`, 30000)
.openFile(`contracts/2_Owner.sol`)
.getEditorValue((content) => {
browser.assert.ok(content.indexOf('contract Owner {') !== -1)
})
.click('*[data-id="workspacesMenuDropdown"]')
.click('*[data-id="workspacepublishToGist"]')
.modalFooterOKClick('fileSystem')
.waitForElementVisible('*[data-shared="tooltipPopup"]', 5000)
.assert.containsText('*[data-shared="tooltipPopup"]', 'Saving gist (' + gistId + ') ...')
}
}

@ -0,0 +1,353 @@
'use strict'
import { NightwatchBrowser } from 'nightwatch'
import init from '../helpers/init'
let firstProxyAddress: string
let lastProxyAddress: string
let shortenedFirstAddress: string
let shortenedLastAddress: string
module.exports = {
'@disabled': true,
before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done)
},
'@sources': function () {
return sources
},
'Should show deploy proxy option for UUPS upgradeable contract #group1': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('udapp')
.switchEnvironment('vm-merge') // this runtime doesn't have the PUSH0 opcode.
.clickLaunchIcon('solidity')
.click('.remixui_compilerConfigSection')
.setValue('#evmVersionSelector', 'paris') // set an evm version which doesn't have PUSH0 opcode.
.clickLaunchIcon('filePanel')
.addFile('myTokenV1.sol', sources[0]['myTokenV1.sol'])
.clickLaunchIcon('solidity')
.pause(2000)
// because the compilatiom imports are slow and sometimes stop loading (not sure why, it's bug) we need to recompile and check to see if the files are really in de FS
.click('[data-id="compilerContainerCompileBtn"]')
.clickLaunchIcon('filePanel')
.isVisible({
selector: '*[data-id="treeViewDivtreeViewItem.deps/npm/@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol"]',
timeout: 120000,
suppressNotFoundErrors: true
})
.clickLaunchIcon('solidity')
.click('[data-id="compilerContainerCompileBtn"]')
.clickLaunchIcon('filePanel')
.isVisible({
selector: '*[data-id="treeViewDivtreeViewItem.deps/npm/@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol"]',
timeout: 120000,
suppressNotFoundErrors: true
})
.clickLaunchIcon('solidity')
.click('[data-id="compilerContainerCompileBtn"]')
.clickLaunchIcon('filePanel')
.waitForElementVisible({
selector: '*[data-id="treeViewDivtreeViewItem.deps/npm/@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol"]',
timeout: 120000,
})
.clickLaunchIcon('solidity')
.waitForElementPresent('select[id="compiledContracts"] option[value=MyToken]', 60000)
.clickLaunchIcon('udapp')
.click('select.udapp_contractNames')
.click('select.udapp_contractNames option[value=MyToken]')
.waitForElementPresent('[data-id="contractGUIDeployWithProxyLabel"]')
.waitForElementPresent('[data-id="contractGUIUpgradeImplementationLabel"]')
},
'Should show upgrade proxy option for child contract inheriting UUPS parent contract #group1': function (browser: NightwatchBrowser) {
browser
.addFile('myTokenV2.sol', sources[1]['myTokenV2.sol'])
.clickLaunchIcon('solidity')
.assert.visible('[data-id="compilerContainerCompileBtn"]')
.click('[data-id="compilerContainerCompileBtn"]')
.waitForElementPresent('select[id="compiledContracts"] option[value=MyTokenV2]', 60000)
.clickLaunchIcon('udapp')
.click('select.udapp_contractNames')
.click('select.udapp_contractNames option[value=MyTokenV2]')
.waitForElementPresent('[data-id="contractGUIDeployWithProxyLabel"]')
.waitForElementPresent('[data-id="contractGUIUpgradeImplementationLabel"]')
},
'Should deploy proxy without initialize parameters #group1': function (browser: NightwatchBrowser) {
browser
.openFile('myTokenV1.sol')
.clickLaunchIcon('solidity')
.assert.visible('[data-id="compilerContainerCompileBtn"]')
.click('[data-id="compilerContainerCompileBtn"]')
.waitForElementPresent('select[id="compiledContracts"] option[value=MyToken]', 60000)
.clickLaunchIcon('udapp')
.click('select.udapp_contractNames')
.click('select.udapp_contractNames option[value=MyToken]')
.verify.visible('[data-id="contractGUIDeployWithProxyLabel"]')
.waitForElementPresent('[data-id="contractGUIDeployWithProxyLabel"]')
.click('[data-id="contractGUIDeployWithProxyLabel"]')
.setValue('[data-id="initializeInputs-initialOwner"]', '0x5B38Da6a701c568545dCfcB03FcB875f56beddC4')
.createContract('')
.waitForElementContainsText('[data-id="udappNotifyModalDialogModalTitle-react"]', 'Deploy Implementation & Proxy (ERC1967)')
.waitForElementVisible('[data-id="udappNotify-modal-footer-ok-react"]')
.click('[data-id="udappNotify-modal-footer-ok-react"]')
.waitForElementContainsText('[data-id="confirmProxyDeploymentModalDialogModalTitle-react"]', 'Confirm Deploy Proxy (ERC1967)')
.waitForElementVisible('[data-id="confirmProxyDeployment-modal-footer-ok-react"]')
.click('[data-id="confirmProxyDeployment-modal-footer-ok-react"]')
.waitForElementPresent('[data-id="universalDappUiTitleExpander0"]')
.waitForElementPresent('[data-id="universalDappUiTitleExpander1"]')
.waitForElementContainsText('*[data-id="terminalJournal"]', 'Deploying ERC1967 >= 5.0.0 as proxy...')
},
'Should interact with deployed contract via ERC1967 (proxy) #group1': function (browser: NightwatchBrowser) {
browser
.getAddressAtPosition(1, (address) => {
firstProxyAddress = address
shortenedFirstAddress = address.slice(0, 5) + '...' + address.slice(address.length - 5, address.length)
})
.clickInstance(1)
.perform((done) => {
browser.testConstantFunction(firstProxyAddress, 'name - call', null, '0:\nstring: MyToken').perform(() => {
done()
})
})
.perform((done) => {
browser.testConstantFunction(firstProxyAddress, 'symbol - call', null, '0:\nstring: MTK').perform(() => {
done()
})
})
},
'Should deploy proxy with initialize parameters #group1': function (browser: NightwatchBrowser) {
browser
.waitForElementPresent('[data-id="deployAndRunClearInstances"]')
.click('[data-id="deployAndRunClearInstances"]')
.addFile('initializeProxy.sol', sources[2]['initializeProxy.sol'])
.clickLaunchIcon('solidity')
.assert.visible('[data-id="compilerContainerCompileBtn"]')
.click('[data-id="compilerContainerCompileBtn"]')
.waitForElementPresent('select[id="compiledContracts"] option[value=MyInitializedToken]', 60000)
.clickLaunchIcon('udapp')
.click('select.udapp_contractNames')
.click('select.udapp_contractNames option[value=MyInitializedToken]')
.waitForElementPresent('[data-id="contractGUIDeployWithProxyLabel"]')
.click('[data-id="contractGUIDeployWithProxyLabel"]')
.useXpath()
.waitForElementPresent('//*[@id="runTabView"]/div/div[2]/div[3]/div[1]/div/div[1]/div[4]/div/div[1]/input')
.waitForElementPresent('//*[@id="runTabView"]/div/div[2]/div[3]/div[1]/div/div[1]/div[4]/div/div[2]/input')
.setValue('//*[@id="runTabView"]/div/div[2]/div[3]/div[1]/div/div[1]/div[4]/div/div[1]/input', 'Remix')
.setValue('//*[@id="runTabView"]/div/div[2]/div[3]/div[1]/div/div[1]/div[4]/div/div[2]/input', "R")
.useCss()
.setValue('[data-id="initializeInputs-initialOwner"]', '0x5B38Da6a701c568545dCfcB03FcB875f56beddC4')
.createContract('')
.waitForElementContainsText('[data-id="udappNotifyModalDialogModalTitle-react"]', 'Deploy Implementation & Proxy (ERC1967)')
.waitForElementVisible('[data-id="udappNotify-modal-footer-ok-react"]')
.click('[data-id="udappNotify-modal-footer-ok-react"]')
.waitForElementContainsText('[data-id="confirmProxyDeploymentModalDialogModalTitle-react"]', 'Confirm Deploy Proxy (ERC1967)')
.waitForElementVisible('[data-id="confirmProxyDeployment-modal-footer-ok-react"]')
.click('[data-id="confirmProxyDeployment-modal-footer-ok-react"]')
.waitForElementPresent('[data-id="universalDappUiTitleExpander0"]')
.waitForElementPresent('[data-id="universalDappUiTitleExpander1"]')
.waitForElementContainsText('*[data-id="terminalJournal"]', 'Deploying ERC1967 >= 5.0.0 as proxy...')
},
'Should interact with initialized contract to verify parameters #group1': function (browser: NightwatchBrowser) {
browser
.getAddressAtPosition(1, (address) => {
lastProxyAddress = address
shortenedLastAddress = address.slice(0, 5) + '...' + address.slice(address.length - 5, address.length)
})
.clickInstance(1)
.perform((done) => {
browser.testConstantFunction(lastProxyAddress, 'name - call', null, '0:\nstring: Remix').perform(() => {
done()
})
})
.perform((done) => {
browser.testConstantFunction(lastProxyAddress, 'symbol - call', null, '0:\nstring: R').perform(() => {
done()
})
})
},
'Should upgrade contract by selecting a previously deployed proxy address from dropdown (MyTokenV1 to MyTokenV2) #group1': function (browser: NightwatchBrowser) {
browser
.click('*[data-id="terminalClearConsole"]')
.waitForElementPresent('[data-id="deployAndRunClearInstances"]')
.click('[data-id="deployAndRunClearInstances"]')
.openFile('myTokenV2.sol')
.clickLaunchIcon('solidity')
.assert.visible('[data-id="compilerContainerCompileBtn"]')
.click('[data-id="compilerContainerCompileBtn"]')
.waitForElementPresent('select[id="compiledContracts"] option[value=MyTokenV2]', 60000)
.clickLaunchIcon('udapp')
.click('select.udapp_contractNames')
.click('select.udapp_contractNames option[value=MyTokenV2]')
.waitForElementPresent('[data-id="contractGUIUpgradeImplementationLabel"]')
.click('[data-id="contractGUIUpgradeImplementationLabel"]')
.waitForElementPresent('[data-id="toggleProxyAddressDropdown"]')
.click('[data-id="toggleProxyAddressDropdown"]')
.waitForElementVisible('[data-id="proxy-dropdown-items"]')
.assert.textContains('[data-id="proxy-dropdown-items"]', shortenedFirstAddress)
.assert.textContains('[data-id="proxy-dropdown-items"]', shortenedLastAddress)
.click('[data-id="proxyAddress1"]')
.createContract('')
.waitForElementContainsText('[data-id="udappNotifyModalDialogModalTitle-react"]', 'Deploy Implementation & Update Proxy')
.waitForElementVisible('[data-id="udappNotify-modal-footer-ok-react"]')
.click('[data-id="udappNotify-modal-footer-ok-react"]')
.waitForElementContainsText('[data-id="confirmProxyDeploymentModalDialogModalTitle-react"]', 'Confirm Update Proxy (ERC1967)')
.waitForElementVisible('[data-id="confirmProxyDeployment-modal-footer-ok-react"]')
.click(
{
selector: '[data-id="confirmProxyDeployment-modal-footer-ok-react"]',
})
.waitForElementPresent('[data-id="universalDappUiTitleExpander0"]')
.waitForElementPresent('[data-id="universalDappUiTitleExpander1"]')
.waitForElementContainsText('*[data-id="terminalJournal"]', 'Using ERC1967 >= 5.0.0 for the proxy upgrade...')
},
'Should interact with upgraded function in contract MyTokenV2 #group1': function (browser: NightwatchBrowser) {
browser
.clickInstance(1)
.perform((done) => {
browser.testConstantFunction(lastProxyAddress, 'version - call', null, '0:\nstring: MyTokenV2!').perform(() => {
done()
})
})
},
'Should upgrade contract by providing proxy address in input field (MyTokenV1 to MyTokenV2) #group1': function (browser: NightwatchBrowser) {
browser
.click('*[data-id="terminalClearConsole"]')
.waitForElementPresent('[data-id="deployAndRunClearInstances"]')
.click('[data-id="deployAndRunClearInstances"]')
.openFile('myTokenV2.sol')
.clickLaunchIcon('solidity')
.assert.visible('[data-id="compilerContainerCompileBtn"]')
.click('[data-id="compilerContainerCompileBtn"]')
.waitForElementPresent('select[id="compiledContracts"] option[value=MyTokenV2]', 60000)
.clickLaunchIcon('udapp')
.click('select.udapp_contractNames')
.click('select.udapp_contractNames option[value=MyTokenV2]')
.waitForElementPresent('[data-id="contractGUIUpgradeImplementationLabel"]')
.waitForElementPresent('[data-id="toggleProxyAddressDropdown"]')
.clearValue('[data-id="ERC1967AddressInput"]')
.setValue('[data-id="ERC1967AddressInput"]', firstProxyAddress)
.createContract('')
.waitForElementContainsText('[data-id="udappNotifyModalDialogModalTitle-react"]', 'Deploy Implementation & Update Proxy')
.waitForElementVisible('[data-id="udappNotify-modal-footer-ok-react"]')
.click('[data-id="udappNotify-modal-footer-ok-react"]')
.waitForElementContainsText('[data-id="confirmProxyDeploymentModalDialogModalTitle-react"]', 'Confirm Update Proxy (ERC1967)')
.waitForElementVisible('[data-id="confirmProxyDeployment-modal-footer-ok-react"]')
.click('[data-id="confirmProxyDeployment-modal-footer-ok-react"]')
.waitForElementPresent('[data-id="universalDappUiTitleExpander0"]')
.waitForElementPresent('[data-id="universalDappUiTitleExpander1"]')
.waitForElementContainsText('*[data-id="terminalJournal"]', 'Using ERC1967 >= 5.0.0 for the proxy upgrade...')
},
'Should interact with upgraded contract through provided proxy address #group1': function (browser: NightwatchBrowser) {
browser
.clearConsole()
.clickInstance(1)
.perform((done) => {
browser.testConstantFunction(firstProxyAddress, 'version - call', null, '0:\nstring: MyTokenV2!').perform(() => {
done()
})
})
},
'Should debug the call': function(browser: NightwatchBrowser) {
browser
.debugTransaction(0)
.waitForElementVisible({
locateStrategy: 'xpath',
selector: '//*[@data-id="treeViewLivm trace step" and contains(.,"7")]',
timeout: 60000
})
.goToVMTraceStep(129)
.waitForElementContainsText('*[data-id="functionPanel"]', 'version()', 60000)
.end()
}
}
const sources = [
{
'myTokenV1.sol': {
content: `
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
contract MyToken is Initializable, ERC721Upgradeable, OwnableUpgradeable, UUPSUpgradeable {
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
function initialize(address initialOwner) initializer public {
__ERC721_init("MyToken", "MTK");
__Ownable_init(initialOwner);
__UUPSUpgradeable_init();
}
function _authorizeUpgrade(address newImplementation)
internal
onlyOwner
override
{}
}
`
}
}, {
'myTokenV2.sol': {
content: `
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "./myTokenV1.sol";
contract MyTokenV2 is MyToken {
function version () public view returns (string memory) {
return "MyTokenV2!";
}
}
`
}
}, {
'initializeProxy.sol': {
content: `
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
contract MyInitializedToken is Initializable, ERC721Upgradeable, OwnableUpgradeable, UUPSUpgradeable {
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
function initialize(string memory tokenName, string memory tokenSymbol, address initialOwner) initializer public {
__ERC721_init(tokenName, tokenSymbol);
__Ownable_init(initialOwner);
__UUPSUpgradeable_init();
}
function _authorizeUpgrade(address newImplementation)
internal
onlyOwner
override
{}
}
`
}
}
]

@ -82,21 +82,21 @@ module.exports = {
instanceAddress = address
console.log('instanceAddress', instanceAddress)
browser
.waitForElementVisible(`#instance${instanceAddress} [data-id="instanceContractBal"]`)
.waitForElementVisible(`#instance${instanceAddress} [data-id="instanceContractBal"]`)
//*[@id="instance0xbBF289D846208c16EDc8474705C748aff07732dB" and contains(.,"Balance") and contains(.,'0.000000000000000111')]
.waitForElementVisible({
locateStrategy: 'xpath',
selector: `//*[@id="instance${instanceAddress}" and contains(.,"Balance") and contains(.,'0.000000000000000111')]`,
timeout: 60000
})
.waitForElementVisible({
locateStrategy: 'xpath',
selector: `//*[@id="instance${instanceAddress}" and contains(.,"Balance") and contains(.,'0.000000000000000111')]`,
timeout: 60000
})
//.waitForElementContainsText(`#instance${instanceAddress} [data-id="instanceContractBal"]`, 'Balance: 0.000000000000000111 ETH', 60000)
.clickFunction('sendSomeEther - transact (not payable)', { types: 'uint256 num', values: '2' })
.pause(1000)
.waitForElementVisible({
locateStrategy: 'xpath',
selector: `//*[@id="instance${instanceAddress}" and contains(.,"Balance") and contains(.,'0.000000000000000109')]`,
timeout: 60000
})
.clickFunction('sendSomeEther - transact (not payable)', { types: 'uint256 num', values: '2' })
.pause(1000)
.waitForElementVisible({
locateStrategy: 'xpath',
selector: `//*[@id="instance${instanceAddress}" and contains(.,"Balance") and contains(.,'0.000000000000000109')]`,
timeout: 60000
})
})
},
@ -238,6 +238,95 @@ module.exports = {
.executeScriptInTerminal('web3.eth.getAccounts()')
.journalLastChildIncludes('[ "0x76a3ABb5a12dcd603B52Ed22195dED17ee82708f" ]')
.end()
},
'Should ensure that save environment state is checked by default #group4 #group5': function (browser: NightwatchBrowser) {
browser.waitForElementPresent('*[data-id="remixIdeSidePanel"]')
.clickLaunchIcon('settings')
.waitForElementPresent('[data-id="settingsEnableSaveEnvStateLabel"]')
.scrollInto('[data-id="settingsEnableSaveEnvStateLabel"]')
.verify.elementPresent('[data-id="settingsEnableSaveEnvState"]:checked')
},
'Should deploy default storage contract; store value and ensure that state is saved. #group4 #group5': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('filePanel')
.click('*[data-id="treeViewLitreeViewItemcontracts"]')
.openFile('contracts/1_Storage.sol')
.pause(5000)
.clickLaunchIcon('udapp')
.waitForElementPresent('*[data-id="Deploy - transact (not payable)"]')
.click('*[data-id="Deploy - transact (not payable)"]')
.waitForElementPresent('#instance0xd9145CCE52D386f254917e481eB44e9943F39138')
.clickInstance(0)
.clickFunction('store - transact (not payable)', { types: 'uint256 num', values: '10' })
.clickFunction('retrieve - call')
.waitForElementContainsText('[data-id="treeViewLi0"]', 'uint256: 10')
.clickLaunchIcon('filePanel')
.openFile('.states/vm-shanghai/state.json')
.getEditorValue((content) => {
browser
.assert.ok(content.includes('"latestBlockNumber": "0x02"'), 'State is saved')
})
},
'Should load state after page refresh #group4': function (browser: NightwatchBrowser) {
browser.refreshPage()
.waitForElementVisible('*[data-id="remixIdeSidePanel"]')
.click('*[data-id="treeViewLitreeViewItemcontracts"]')
.openFile('contracts/1_Storage.sol')
.addAtAddressInstance('0xd9145CCE52D386f254917e481eB44e9943F39138', true, true, false)
.clickInstance(0)
.clickFunction('retrieve - call')
.waitForElementContainsText('[data-id="treeViewLi0"]', 'uint256: 10')
},
'Should save state after running web3 script #group4': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('settings')
.waitForElementPresent('[data-id="settingsTabGenerateContractMetadataLabel"]')
.click('[data-id="settingsTabGenerateContractMetadataLabel"]')
.verify.elementPresent('[data-id="settingsTabGenerateContractMetadata"]:checked')
.clickLaunchIcon('solidity')
.click('.remixui_compilerConfigSection')
.setValue('#evmVersionSelector', 'london')
.click('*[data-id="compilerContainerCompileBtn"]')
.pause(5000)
.clickLaunchIcon('udapp')
.switchEnvironment('vm-london')
.clickLaunchIcon('filePanel')
.click('*[data-id="treeViewLitreeViewItemscripts"]')
.openFile('scripts/deploy_with_web3.ts')
.click('[data-id="play-editor"]')
.waitForElementPresent('[data-id="treeViewDivDraggableItem.states/vm-london/state.json"]')
.click('[data-id="treeViewDivDraggableItem.states/vm-london/state.json"]')
.pause(100000)
.getEditorValue((content) => {
browser
.assert.ok(content.includes('"latestBlockNumber": "0x01"'), 'State is saved')
})
},
'Should ensure that .states is not updated when save env option is unchecked #group5': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('settings')
.waitForElementPresent('[data-id="settingsEnableSaveEnvStateLabel"]')
.click('[data-id="settingsEnableSaveEnvStateLabel"]')
.verify.elementNotPresent('[data-id="settingsEnableSaveEnvState"]:checked')
.clickLaunchIcon('filePanel')
.openFile('contracts/1_Storage.sol')
.pause(5000)
.clickLaunchIcon('udapp')
.waitForElementPresent('*[data-id="Deploy - transact (not payable)"]')
.click('*[data-id="Deploy - transact (not payable)"]')
.pause(5000)
.clickLaunchIcon('filePanel')
.openFile('.states/vm-shanghai/state.json')
.getEditorValue((content) => {
browser
.assert.ok(content.includes('"latestBlockNumber": "0x02"'), 'State is unchanged')
})
.end()
}
}

@ -341,6 +341,8 @@ module.exports = {
browser
.clickLaunchIcon('solidity')
.setSolidityCompilerVersion('builtin')
.click('.remixui_compilerConfigSection')
.setValue('#evmVersionSelector', 'shanghai') // Temporary fix
.clickLaunchIcon('filePanel')
.click('*[data-id="treeViewLitreeViewItemcontracts"]')
.openFile('contracts/3_Ballot.sol')

@ -129,7 +129,7 @@ module.exports = {
})
})
},
'Use special functions receive/fallback - only fallback is diclared and is payable, sending data and wei #group3': function (browser: NightwatchBrowser) {
'Use special functions receive/fallback - only fallback is declared and is payable, sending data and wei #group3': function (browser: NightwatchBrowser) {
// don't need to redeploy it, same contract
browser.perform((done) => {
browser.getAddressAtPosition(0, (address) => {

@ -107,6 +107,46 @@ module.exports = {
})
},
'Should load Blockscout verified contracts from URL "address" and "blockscout" params (single source)': function (browser: NightwatchBrowser) {
browser
.url('http://127.0.0.1:8080/#address=0xdAC17F958D2ee523a2206206994597C13D831ec7&blockscout=eth.blockscout.com')
.refreshPage()
.pause(7000)
.currentWorkspaceIs('code-sample')
.assert.elementPresent('*[data-id="treeViewLitreeViewItemeth.blockscout.com"]')
.assert.elementPresent('*[data-id="treeViewLitreeViewItemeth.blockscout.com/0xdAC17F958D2ee523a2206206994597C13D831ec7"]')
.getEditorValue((content) => {
browser.assert.ok(content && content.indexOf(
'contract TetherToken is Pausable, StandardToken, BlackList {') !== -1)
})
},
'Should load Blockscout verified contracts from URL "address" and "blockscout" params (multiple sources)': function (browser: NightwatchBrowser) {
browser
.url('http://127.0.0.1:8080/#address=0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9&blockscout=eth.blockscout.com')
.refreshPage()
.pause(7000)
.currentWorkspaceIs('code-sample')
.assert.elementPresent('*[data-id="treeViewLitreeViewItemeth.blockscout.com"]')
.assert.elementPresent('*[data-id="treeViewLitreeViewItemeth.blockscout.com/0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9"]')
.assert.elementPresent('*[data-id="treeViewLitreeViewItemeth.blockscout.com/0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9/contracts"]')
.assert.elementPresent('*[data-id="treeViewLitreeViewItemeth.blockscout.com/0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9/contracts/open-zeppelin"]')
.assert.elementPresent('*[data-id="treeViewLitreeViewItemeth.blockscout.com/0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9/contracts/open-zeppelin/Address.sol"]')
.assert.elementPresent('*[data-id="treeViewLitreeViewItemeth.blockscout.com/0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9/contracts/open-zeppelin/BaseAdminUpgradeabilityProxy.sol"]')
.assert.elementPresent('*[data-id="treeViewLitreeViewItemeth.blockscout.com/0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9/contracts/open-zeppelin/BaseUpgradeabilityProxy.sol"]')
.assert.elementPresent('*[data-id="treeViewLitreeViewItemeth.blockscout.com/0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9/contracts/open-zeppelin/InitializableAdminUpgradeabilityProxy.sol"]')
.assert.elementPresent('*[data-id="treeViewLitreeViewItemeth.blockscout.com/0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9/contracts/open-zeppelin/InitializableUpgradeabilityProxy.sol"]')
.assert.elementPresent('*[data-id="treeViewLitreeViewItemeth.blockscout.com/0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9/contracts/open-zeppelin/Proxy.sol"]')
.assert.elementPresent('*[data-id="treeViewLitreeViewItemeth.blockscout.com/0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9/contracts/open-zeppelin/UpgradeabilityProxy.sol"]')
.openFile('eth.blockscout.com/0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9/contracts/open-zeppelin/InitializableAdminUpgradeabilityProxy.sol')
.getEditorValue((content) => {
browser.assert.ok(content && content.indexOf(
'contract InitializableAdminUpgradeabilityProxy is BaseAdminUpgradeabilityProxy, InitializableUpgradeabilityProxy {') !== -1)
})
},
'Should load the code from URL & code params #group1': function (browser: NightwatchBrowser) {
browser
.url('http://127.0.0.1:8080/#optimize=true&runs=300&url=https://github.com/ethereum/remix-project/blob/master/apps/remix-ide/contracts/app/solidity/mode.sol&code=cHJhZ21hIHNvbGlkaXR5ID49MC42LjAgPDAuNy4wOwoKaW1wb3J0ICJodHRwczovL2dpdGh1Yi5jb20vT3BlblplcHBlbGluL29wZW56ZXBwZWxpbi1jb250cmFjdHMvYmxvYi9tYXN0ZXIvY29udHJhY3RzL2FjY2Vzcy9Pd25hYmxlLnNvbCI7Cgpjb250cmFjdCBHZXRQYWlkIGlzIE93bmFibGUgewogIGZ1bmN0aW9uIHdpdGhkcmF3KCkgZXh0ZXJuYWwgb25seU93bmVyIHsKICB9Cn0')

@ -67,7 +67,6 @@ module.exports = {
browser
// @ts-ignore
.frame(0)
.click('[data-id="remote-compiler"]')
.click('[data-id="compile"]')
.waitForElementVisible({
selector:'[data-id="compilation-details"]',
@ -89,7 +88,6 @@ module.exports = {
chromeBrowser.setPermission('clipboard-write', 'granted')
browser
.frame(0)
.click('[data-id="remote-compiler"]')
.click('[data-id="compile"]')
.waitForElementVisible({
selector:'[data-id="compilation-details"]',

@ -14,15 +14,15 @@
pathval "1.1.1"
type-detect "4.0.8"
"@openzeppelin/contracts-upgradeable@^5.0.0":
version "5.0.0"
resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-5.0.0.tgz#859c00c55f04b6dda85b3c88bce507d65019888f"
integrity sha512-D54RHzkOKHQ8xUssPgQe2d/U92mwaiBDY7qCCVGq6VqwQjsT3KekEQ3bonev+BLP30oZ0R1U6YC8/oLpizgC5Q==
"@openzeppelin/contracts-upgradeable@^5.0.2":
version "5.0.2"
resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-5.0.2.tgz#3e5321a2ecdd0b206064356798c21225b6ec7105"
integrity sha512-0MmkHSHiW2NRFiT9/r5Lu4eJq5UJ4/tzlOgYXNAIj/ONkQTVnz22pLxDvp4C4uZ9he7ZFvGn3Driptn1/iU7tQ==
"@openzeppelin/contracts@^5.0.0":
version "5.0.0"
resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-5.0.0.tgz#ee0e4b4564f101a5c4ee398cd4d73c0bd92b289c"
integrity sha512-bv2sdS6LKqVVMLI5+zqnNrNU/CA+6z6CmwFXm/MzmOPBRSO5reEJN7z0Gbzvs0/bv/MZZXNklubpwy3v2+azsw==
"@openzeppelin/contracts@^5.0.2":
version "5.0.2"
resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-5.0.2.tgz#b1d03075e49290d06570b2fd42154d76c2a5d210"
integrity sha512-ytPc6eLGcHHnapAZ9S+5qsdomhjo6QBHTDRRBFfTxXIpsicMhVPouPgmUPebZZZGX7vt9USA+Z+0M0dSVtSUEA==
"@openzeppelin/upgrades-core@^1.30.0":
version "1.30.1"
@ -1437,9 +1437,9 @@ flat@^5.0.2:
integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==
follow-redirects@^1.0.0, follow-redirects@^1.15.0:
version "1.15.4"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.4.tgz#cdc7d308bf6493126b17ea2191ea0ccf3e535adf"
integrity sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==
version "1.15.6"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b"
integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==
for-each@^0.3.3:
version "0.3.3"

@ -52,7 +52,8 @@ class Editor extends Plugin {
cairo: 'cairo',
ts: 'typescript',
move: 'move',
circom: 'circom'
circom: 'circom',
nr: 'rust'
}
this.activated = false

@ -24,7 +24,7 @@ const profile = {
methods: ['closeAllFiles', 'closeFile', 'file', 'exists', 'open', 'writeFile', 'writeMultipleFiles', 'writeFileNoRewrite',
'readFile', 'copyFile', 'copyDir', 'rename', 'mkdir', 'readdir', 'dirList', 'fileList', 'remove', 'getCurrentFile', 'getFile',
'getFolder', 'setFile', 'switchFile', 'refresh', 'getProviderOf', 'getProviderByName', 'getPathFromUrl', 'getUrlFromPath',
'saveCurrentFile', 'setBatchFiles', 'isGitRepo', 'isFile', 'isDirectory', 'hasGitSubmodule'
'saveCurrentFile', 'setBatchFiles', 'isGitRepo', 'isFile', 'isDirectory', 'hasGitSubmodule', 'copyFolderToJson'
],
kind: 'file-system'
}
@ -1041,6 +1041,14 @@ class FileManager extends Plugin {
throw new Error(e)
}
}
async copyFolderToJson(folder: string) {
const provider = this.currentFileProvider()
if (provider && provider.copyFolderToJson) {
return await provider.copyFolderToJson(folder)
}
throw new Error('copyFolderToJson not available')
}
}
module.exports = FileManager

@ -224,9 +224,23 @@ export class TabProxy extends Plugin {
this.removeTab(oldName)
}
/**
*
* @param {string} name
* @param {string} title
* @param {Function} switchTo
* @param {Function} close
* @param {string} icon
* @param {string} description
* @returns
*/
addTab (name, title, switchTo, close, icon, description = '') {
if (this._handlers[name]) return this.renderComponent()
if ((name.endsWith('.vy') && icon === undefined) || title.includes('Vyper')) {
icon = 'assets/img/vyperLogo2.webp'
}
var slash = name.split('/')
const tabPath = slash.reverse()
const tempTitle = []
@ -292,7 +306,7 @@ export class TabProxy extends Plugin {
if (!previous && tab.name === name) {
if(index - 1 >= 0 && this.loadedTabs[index - 1])
previous = this.loadedTabs[index - 1]
else if (index + 1 && this.loadedTabs[index + 1])
else if (index + 1 && this.loadedTabs[index + 1])
previous = this.loadedTabs[index + 1]
}
return tab.name !== name

@ -1,12 +1,12 @@
/* global Node, requestAnimationFrame */ // eslint-disable-line
import React from 'react' // eslint-disable-line
import { RemixUiTerminal } from '@remix-ui/terminal' // eslint-disable-line
import { RemixUiTerminal, RemixUITerminalWrapper } from '@remix-ui/terminal' // eslint-disable-line
import { Plugin } from '@remixproject/engine'
import * as packageJson from '../../../../../package.json'
import {Registry} from '@remix-project/remix-lib'
import { PluginViewWrapper } from '@remix-ui/helper'
import vm from 'vm'
const EventManager = require('../../lib/events')
import EventManager from '../../lib/events'
import { CompilerImports } from '@remix-project/core-plugin' // eslint-disable-line
import { RemixUiXterminals } from '@remix-ui/xterm'
@ -26,6 +26,34 @@ const profile = {
}
class Terminal extends Plugin {
fileImport: CompilerImports
event: any
globalRegistry: Registry
element: HTMLDivElement
eventsDecoder: any
txListener: any
_deps: { fileManager: any; editor: any; compilersArtefacts: any; offsetToLineColumnConverter: any }
commandHelp: { 'remix.loadgist(id)': string; 'remix.loadurl(url)': string; 'remix.execute(filepath)': string; 'remix.exeCurrent()': string; 'remix.help()': string }
blockchain: any
vm: typeof vm
_api: any
_opts: any
config: any
version: string
data: {
lineLength: any // ????
session: any[]; activeFilters: { commands: any; input: string }; filterFns: any
}
_view: { el: any; bar: any; input: any; term: any; journal: any; cli: any }
_components: any
_commands: any
commands: any
_JOURNAL: any[]
_jobs: any[]
_INDEX: any
_shell: any
dispatch: any
terminalApi: any
constructor(opts, api) {
super(profile)
this.fileImport = new CompilerImports()
@ -75,7 +103,7 @@ class Terminal extends Plugin {
this._INDEX.commandsMain = {}
if (opts.shell) this._shell = opts.shell // ???
register(this)
this.event.register('debuggingRequested', async (hash) => {
this.event.register('debuggingRequested', async (hash: any) => {
// TODO should probably be in the run module
if (!await this._opts.appManager.isActive('debugger')) await this._opts.appManager.activatePlugin('debugger')
this.call('menuicons', 'select', 'debugger')
@ -114,13 +142,12 @@ class Terminal extends Plugin {
}
updateComponent(state) {
return (Registry.getInstance().get('platform').api.isDesktop()) ? <RemixUiXterminals onReady={state.onReady} plugin={state.plugin}>
</RemixUiXterminals>
: <RemixUiTerminal
return(
<RemixUITerminalWrapper
plugin={state.plugin}
onReady={state.onReady}
visible={true}
/>
/>)
}
renderComponent() {

@ -34,6 +34,8 @@
"filePanel.tssoltestghaction": "Mocha Chai Test Workflow",
"filePanel.workspace.addscriptetherscan": "Adds scripts which can be used to interact with the Etherscan API",
"filePanel.addscriptetherscan": "Add Etherscan scripts",
"filePanel.workspace.addscriptsindri": "Adds scripts for interacting with Sindri, a zk proof generation remote service",
"filePanel.addscriptsindri": "Add Sindri ZK scripts",
"filePanel.workspace.addscriptdeployer": "Adds scripts which can be used to deploy contracts",
"filePanel.addscriptdeployer": "Add contract deployer scripts",
"filePanel.workspace.slitherghaction": "Adds a preset yml file to run slither analysis on github actions CI",
@ -94,7 +96,7 @@
"filePanel.copyFolderFailed": "Copy Folder Failed",
"filePanel.copyFolderFailedMsg": "Unexpected error while copying folder: {src}",
"filePanel.runScriptFailed": "Run script failed",
"filePanel.createPublicGist": "Create a public gist",
"filePanel.createPublicGist": "Publish to a public gist",
"filePanel.createPublicGistMsg1": "Are you sure you want to push changes to remote gist file on github.com?",
"filePanel.createPublicGistMsg2": "Are you sure you want to anonymously publish all your files in the {path} folder as a public gist on github.com?",
"filePanel.createPublicGistMsg3": "Are you sure you want to anonymously publish {path} file as a public gist on github.com?",

@ -17,6 +17,9 @@
"settings.etherscanTokenTitle": "EtherScan Access Token",
"settings.etherscanAccessTokenText": "Manage the api key used to interact with Etherscan.",
"settings.etherscanAccessTokenText2": "Go to Etherscan api key page (link below) to create a new api key and save it in Remix.",
"settings.sindriAccessTokenTitle": "Sindri Credentials",
"settings.sindriAccessTokenText": "The access token is used to compile ZKP circuits and generate proofs with Sindri.",
"settings.sindriAccessTokenText2":"Go to the Sindri account creation page (link below) to create a new token and save it in Remix.",
"settings.save": "Save",
"settings.remove": "Remove",
"settings.themes": "Themes",

@ -1,8 +1,8 @@
{
"terminal.listen": "listen on all transactions",
"terminal.listen": "Listen on all transactions",
"terminal.listenVM": "Listen on all transactions is disabled for VM environment",
"terminal.listenTitle": "If checked Remix will listen on all transactions mined in the current environment and not only transactions created by you",
"terminal.search": "Search with transaction hash or address",
"terminal.search": "Filter with transaction hash or address",
"terminal.used": "used",
"terminal.debug": "Debug",
"terminal.welcomeText1": "Welcome to",

@ -65,16 +65,18 @@
"udapp.tooltipText3": "Click to open a bridge for converting L1 mainnet ETH to the selected network currency.",
"udapp._comment_instanceContainerUI.tsx": "libs/remix-ui/run-tab/src/lib/components/instanceContainerUI.tsx",
"udapp.deployedContracts": "Deployed Contracts",
"udapp.deployedContracts": "Deployed/Unpinned Contracts",
"udapp.deployAndRunClearInstances": "Clear instances list and reset recorder",
"udapp.deployAndRunNoInstanceText": "Currently you have no deployed contracts to interact with.",
"udapp.tooltipText6": "Autogenerated generic user interfaces for interaction with deployed contracts",
"udapp.deployAndRunNoInstanceText": "Currently you have no unpinned contracts to interact with.",
"udapp.tooltipText6": "Autogenerated generic user interfaces for interaction with deployed/unpinned contracts",
"udapp.savedContracts": "Saved Contracts",
"udapp.NoSavedInstanceText": "Currently you have no saved contracts to interact with.",
"udapp.tooltipTextUnsave": "Unsave & move to Deployed Contracts list",
"udapp.savedOn": "Saved On",
"udapp.filePath": "File Path",
"udapp.savedContracts": "Pinned Contracts",
"udapp.tooltipTextPinnedContracts": "List of pinned contracts for selected workspace & network",
"udapp.NoSavedInstanceText": "No pinned contracts found for selected workspace & network",
"udapp.tooltipTextDelete": "Delete pinned contract",
"udapp.tooltipTextUnpin": "Unpin contract",
"udapp.savedOn": "Pinned at",
"udapp.filePath": "File path",
"udapp._comment_recorderCardUI.tsx": "libs/remix-ui/run-tab/src/lib/components/recorderCardUI.tsx",
"udapp.transactionsRecorded": "Transactions recorded",
@ -104,8 +106,8 @@
"udapp.tooltipText13": "Deployed {date}",
"udapp._comment_universalDappUI.tsx": "libs/remix-ui/run-tab/src/lib/components/universalDappUI.tsx",
"udapp.tooltipText7": "Remove from the list",
"udapp.tooltipText14": "Save & move to Saved Contracts list",
"udapp.tooltipTextRemove": "Remove from the list",
"udapp.tooltipTextPin": "Pin contract",
"udapp.tooltipText8": "Click for docs about using 'receive'/'fallback'",
"udapp.tooltipText9": "The Calldata to send to fallback function of the contract.",
"udapp.tooltipText10": "Send data to contract.",

@ -86,7 +86,7 @@
"filePanel.copyFolderFailed": "Copia de Carpeta Fallida",
"filePanel.copyFolderFailedMsg": "Error inesperado al copiar la carpeta: {src}",
"filePanel.runScriptFailed": "Error al ejecutar el script",
"filePanel.createPublicGist": "Crear una lista pública",
"filePanel.createPublicGist": "Publicar una lista pública",
"filePanel.createPublicGistMsg1": "¿Está seguro que desea empujar cambios al archivo gist remoto en github.com?",
"filePanel.createPublicGistMsg2": "¿Estás seguro que quieres publicar todos tus archivos de forma anónima en la carpeta {path} como un gist público en github.com?",
"filePanel.createPublicGistMsg3": "¿Estás seguro de que quieres publicar de forma anónima el archivo {path} como una gist público en github.com?",

@ -36,5 +36,6 @@
"settings.port": "PUERTO",
"settings.projectID": "ID DEL PROYECTO",
"settings.projectSecret": "SECRETO DE PROYECTO",
"settings.analyticsInRemix": "Analíticas en IDE Remix"
"settings.analyticsInRemix": "Analíticas en IDE Remix",
"settings.enableSaveEnvState": "Save environment state"
}

@ -88,7 +88,7 @@
"udapp.tooltipText12": "Entrada requerida",
"udapp.tooltipText13": "Publicado en {date}",
"udapp._comment_universalDappUI.tsx": "libs/remix-ui/run-tab/src/lib/components/universalDappUI.tsx",
"udapp.tooltipText7": "Eliminar de la lista",
"udapp.tooltipTextRemove": "Eliminar de la lista",
"udapp.tooltipText8": "Haga clic para ver los documentos sobre el uso de 'receive'/'fallback'",
"udapp.tooltipText9": "El datos de llamada a enviar a la función fallback del contrato.",
"udapp.tooltipText10": "Enviar datos al contrato.",

@ -86,7 +86,7 @@
"filePanel.copyFolderFailed": "Échec de la copie du dossier",
"filePanel.copyFolderFailedMsg": "Erreur inattendue lors de la copie du fichier : {src}",
"filePanel.runScriptFailed": "Échec de l'exécution du script",
"filePanel.createPublicGist": "Créer un gist public",
"filePanel.createPublicGist": "Publier un gist public",
"filePanel.createPublicGistMsg1": "Êtes-vous sûr de vouloir envoyer les changements dans le fichier gist distant sur github.com?",
"filePanel.createPublicGistMsg2": "Êtes-vous sûr de vouloir publier anonymement tous vos fichiers dans le dossier {path} en tant que gist public sur github.com?",
"filePanel.createPublicGistMsg3": "Êtes-vous sûr de vouloir publier anonymement tous vos fichiers dans le dossier {path} en tant que gist public sur github.com?",

@ -36,5 +36,6 @@
"settings.port": "PORT",
"settings.projectID": "ID du projet",
"settings.projectSecret": "SECRET DU PROJET",
"settings.analyticsInRemix": "Analytics dans l'IDE de Remix"
"settings.analyticsInRemix": "Analytics dans l'IDE de Remix",
"settings.enableSaveEnvState": "Save environment state"
}

@ -88,7 +88,7 @@
"udapp.tooltipText12": "Entrée nécessaire",
"udapp.tooltipText13": "Déployé {date}",
"udapp._comment_universalDappUI.tsx": "libs/remix-ui/run-tab/src/lib/components/universalDappUI.tsx",
"udapp.tooltipText7": "Supprimer de la liste",
"udapp.tooltipTextRemove": "Supprimer de la liste",
"udapp.tooltipText8": "Cliquez sur la documentation à propos de l'utilisation de 'receive'/'fallback'",
"udapp.tooltipText9": "Les Calldata à envoyer à la fonction fallback du contrat.",
"udapp.tooltipText10": "Envoyer des données au contrat.",

@ -86,7 +86,7 @@
"filePanel.copyFolderFailed": "Copia Cartella Non Riuscita",
"filePanel.copyFolderFailedMsg": "Errore inatteso durante la copia della cartella: {src}",
"filePanel.runScriptFailed": "Esecuzione dello script non riuscita",
"filePanel.createPublicGist": "Crea Gist Pubblico",
"filePanel.createPublicGist": "Pubblicare Gist Pubblico",
"filePanel.createPublicGistMsg1": "Sei sicuro di voler inviare le modifiche al file gist remoto su github.com?",
"filePanel.createPublicGistMsg2": "Sei sicuro di voler pubblicare in modo anonimo tutti i tuoi file nella cartella {path} come gist pubblico su github.com?",
"filePanel.createPublicGistMsg3": "Sei sicuro di voler pubblicare in modo anonimo tutti i tuoi file nella cartella {path} come gist pubblico su github.com?",

@ -36,5 +36,6 @@
"settings.port": "PORTA",
"settings.projectID": "ID PROGETTO",
"settings.projectSecret": "SEGRETO DEL PROGETTO",
"settings.analyticsInRemix": "Analytics nella Remix IDE"
"settings.analyticsInRemix": "Analytics nella Remix IDE",
"settings.enableSaveEnvState": "Save environment state"
}

@ -88,7 +88,7 @@
"udapp.tooltipText12": "Input richiesto",
"udapp.tooltipText13": "Deploiato",
"udapp._comment_universalDappUI.tsx": "libs/remix-ui/run-tab/src/lib/components/universalDappUI.tsx",
"udapp.tooltipText7": "Rimuovi dalla lista",
"udapp.tooltipTextRemove": "Rimuovi dalla lista",
"udapp.tooltipText8": "Fare clic per i documenti sull'uso di 'receive'/'fallback'",
"udapp.tooltipText9": "Il Calldata per inviare alla funzione di fallback del contratto.",
"udapp.tooltipText10": "Invia dati al contratto.",

@ -86,7 +86,7 @@
"filePanel.copyFolderFailed": "复制文件夹失败",
"filePanel.copyFolderFailedMsg": "复制文件夹时出现意外错误:{src}",
"filePanel.runScriptFailed": "执行脚本失败",
"filePanel.createPublicGist": "创建一个公开的 gist",
"filePanel.createPublicGist": "发布到公共 gist",
"filePanel.createPublicGistMsg1": "您确定要将更改推送到 github.com 上的远程 gist 文件吗?",
"filePanel.createPublicGistMsg2": "您确定要在 github.com 上以匿名方式将 {path} 文件夹中的所有文件发布为公开的 gist?",
"filePanel.createPublicGistMsg3": "您确定要将 {path} 文件匿名发布为 github.com 上的公开 gist?",

@ -36,5 +36,6 @@
"settings.port": "端口",
"settings.projectID": "项目 ID",
"settings.projectSecret": "项目密钥",
"settings.analyticsInRemix": "Remix IDE 中的分析功能"
"settings.analyticsInRemix": "Remix IDE 中的分析功能",
"settings.enableSaveEnvState": "Save environment state"
}

@ -88,7 +88,7 @@
"udapp.tooltipText12": "必填",
"udapp.tooltipText13": "已部署 {date}",
"udapp._comment_universalDappUI.tsx": "libs/remix-ui/run-tab/src/lib/components/universalDappUI.tsx",
"udapp.tooltipText7": "从列表中删除",
"udapp.tooltipTextRemove": "从列表中删除",
"udapp.tooltipText8": "点击查看有关使用 'receive'/'fallback' 的文档",
"udapp.tooltipText9": "发送到合约 fallback 函数的 Calldata。",
"udapp.tooltipText10": "将数据发送到合约。",

@ -58,11 +58,27 @@ export class Web3ProviderModule extends Plugin {
const contractAddressStr = addressToString(receipt.contractAddress)
const contractData = await this.call('compilerArtefacts', 'getContractDataFromAddress', contractAddressStr)
if (contractData) {
this.call('udapp', 'addInstance', contractAddressStr, contractData.contract.abi, contractData.name)
const data = await this.call('compilerArtefacts', 'getCompilerAbstract', contractData.file)
const contractObject = {
name: contractData.name,
abi: contractData.contract.abi,
compiler: data,
contract: {
file : contractData.file,
object: contractData.contract
}
}
this.call('udapp', 'addInstance', contractAddressStr, contractData.contract.abi, contractData.name, contractObject)
await this.call('compilerArtefacts', 'addResolvedContract', contractAddressStr, data)
}
}, 50)
const isVM = this.blockchain.executionContext.isVM()
if (isVM && this.blockchain.config.get('settings/save-evm-state')) {
await this.blockchain.executionContext.getStateDetails().then((state) => {
this.call('fileManager', 'writeFile', `.states/${this.blockchain.executionContext.getProvider()}/state.json`, state)
})
}
}
}
resolve(message)

@ -85,8 +85,8 @@ export class RunTab extends ViewPlugin {
this.emit('clearAllSavedInstancesReducer')
}
addInstance(address, abi, name) {
this.emit('addInstanceReducer', address, abi, name)
addInstance(address, abi, name, contractData) {
this.emit('addInstanceReducer', address, abi, name, contractData)
}
addSavedInstance(address, abi, name, savedOn, filePath) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

@ -135,7 +135,8 @@ export class Blockchain extends Plugin {
setupEvents() {
this.executionContext.event.register('contextChanged', async (context) => {
await this.resetEnvironment()
// reset environment to last known state of the context
await this.loadContext(context)
this._triggerEvent('contextChanged', [context])
this.detectNetwork((error, network) => {
this.networkStatus = {network, error}
@ -286,7 +287,7 @@ export class Blockchain extends Plugin {
await this.saveDeployedContractStorageLayout(implementationContractObject, address, networkInfo)
this.events.emit('newProxyDeployment', address, new Date().toISOString(), implementationContractObject.contractName)
_paq.push(['trackEvent', 'blockchain', 'Deploy With Proxy', 'Proxy deployment successful'])
this.call('udapp', 'addInstance', addressToString(address), implementationContractObject.abi, implementationContractObject.name)
this.call('udapp', 'addInstance', addressToString(address), implementationContractObject.abi, implementationContractObject.name, implementationContractObject)
}
this.runTx(args, confirmationCb, continueCb, promptCb, finalCb)
@ -336,7 +337,7 @@ export class Blockchain extends Plugin {
}
await this.saveDeployedContractStorageLayout(newImplementationContractObject, proxyAddress, networkInfo)
_paq.push(['trackEvent', 'blockchain', 'Upgrade With Proxy', 'Upgrade Successful'])
this.call('udapp', 'addInstance', addressToString(proxyAddress), newImplementationContractObject.abi, newImplementationContractObject.name)
this.call('udapp', 'addInstance', addressToString(proxyAddress), newImplementationContractObject.abi, newImplementationContractObject.name, newImplementationContractObject)
}
this.runTx(args, confirmationCb, continueCb, promptCb, finalCb)
}
@ -643,8 +644,23 @@ export class Blockchain extends Plugin {
})
}
async resetEnvironment() {
await this.getCurrentProvider().resetEnvironment()
async loadContext(context: string) {
const saveEvmState = this.config.get('settings/save-evm-state')
if (saveEvmState) {
const contextExists = await this.call('fileManager', 'exists', `.states/${context}/state.json`)
if (contextExists) {
const stateDb = await this.call('fileManager', 'readFile', `.states/${context}/state.json`)
await this.getCurrentProvider().resetEnvironment(stateDb)
} else {
await this.getCurrentProvider().resetEnvironment()
}
} else {
await this.getCurrentProvider().resetEnvironment()
}
// TODO: most params here can be refactored away in txRunner
const web3Runner = new TxRunnerWeb3(
{
@ -677,7 +693,7 @@ export class Blockchain extends Plugin {
view on etherscan
</a>
)
}
}
})
})
this.txRunner = new TxRunner(web3Runner, {})
@ -889,8 +905,13 @@ export class Blockchain extends Plugin {
let execResult
let returnValue = null
if (isVM) {
const hhlogs = await this.web3().remix.getHHLogsForTx(txResult.transactionHash)
if (!tx.useCall && this.config.get('settings/save-evm-state')) {
await this.executionContext.getStateDetails().then((state) => {
this.call('fileManager', 'writeFile', `.states/${this.executionContext.getProvider()}/state.json`, state)
})
}
const hhlogs = await this.web3().remix.getHHLogsForTx(txResult.transactionHash)
if (hhlogs && hhlogs.length) {
const finalLogs = (
<div>

@ -3,6 +3,7 @@
import Web3 from 'web3'
import { execution } from '@remix-project/remix-lib'
import EventManager from '../lib/events'
import {bufferToHex} from '@ethereumjs/util'
const _paq = window._paq = window._paq || []
let web3
@ -71,35 +72,44 @@ export class ExecutionContext {
}
detectNetwork (callback) {
if (this.isVM()) {
callback(null, { id: '-', name: 'VM' })
} else {
if (!web3.currentProvider) {
return callback('No provider set')
}
const cb = (err, id) => {
let name = null
if (err) name = 'Unknown'
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md
else if (id === 1) name = 'Main'
else if (id === 3) name = 'Ropsten'
else if (id === 4) name = 'Rinkeby'
else if (id === 5) name = 'Goerli'
else if (id === 42) name = 'Kovan'
else if (id === 11155111) name = 'Sepolia'
else name = 'Custom'
if (id === 1) {
web3.eth.getBlock(0).then((block) => {
if (block && block.hash !== this.mainNetGenesisHash) name = 'Custom'
callback(err, { id, name, lastBlock: this.lastBlock, currentFork: this.currentFork })
}).catch((error) => callback(error))
} else {
callback(err, { id, name, lastBlock: this.lastBlock, currentFork: this.currentFork })
return new Promise((resolve, reject) => {
if (this.isVM()) {
callback && callback(null, { id: '-', name: 'VM' })
return resolve({ id: '-', name: 'VM' })
} else {
if (!web3.currentProvider) {
callback && callback('No provider set')
return reject('No provider set')
}
const cb = (err, id) => {
let name = null
if (err) name = 'Unknown'
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md
else if (id === 1) name = 'Main'
else if (id === 3) name = 'Ropsten'
else if (id === 4) name = 'Rinkeby'
else if (id === 5) name = 'Goerli'
else if (id === 42) name = 'Kovan'
else if (id === 11155111) name = 'Sepolia'
else name = 'Custom'
if (id === 1) {
web3.eth.getBlock(0).then((block) => {
if (block && block.hash !== this.mainNetGenesisHash) name = 'Custom'
callback && callback(err, { id, name, lastBlock: this.lastBlock, currentFork: this.currentFork })
return resolve({ id, name, lastBlock: this.lastBlock, currentFork: this.currentFork })
}).catch((error) => {
callback && callback(error)
return reject(error)
})
} else {
callback && callback(err, { id, name, lastBlock: this.lastBlock, currentFork: this.currentFork })
return resolve({ id, name, lastBlock: this.lastBlock, currentFork: this.currentFork })
}
}
web3.eth.net.getId().then(id=>cb(null,parseInt(id))).catch(err=>cb(err))
}
web3.eth.net.getId().then(id=>cb(null,parseInt(id))).catch(err=>cb(err))
}
})
}
removeProvider (name) {
@ -195,4 +205,26 @@ export class ExecutionContext {
return transactionDetailsLinks[network] + hash
}
}
async getStateDetails() {
const db = await this.web3().remix.getStateDb()
const blocksData = await this.web3().remix.getBlocksData()
const state = {
db: Object.fromEntries(db._database),
blocks: blocksData.blocks,
latestBlockNumber: blocksData.latestBlockNumber
}
const stringifyed = JSON.stringify(state, (key, value) => {
if (key === 'db') {
return value
} else if (key === 'blocks') {
return value.map(block => bufferToHex(block))
}else if (key === '') {
return value
}
return bufferToHex(value)
}, '\t')
return stringifyed
}
}

@ -2,6 +2,7 @@ import Web3, { FMT_BYTES, FMT_NUMBER, LegacySendAsyncProvider } from 'web3'
import { fromWei, toBigInt } from 'web3-utils'
import { privateToAddress, hashPersonalMessage, isHexString } from '@ethereumjs/util'
import { extend, JSONRPCRequestPayload, JSONRPCResponseCallback } from '@remix-project/remix-simulator'
import {toBuffer} from '@ethereumjs/util'
import { ExecutionContext } from '../execution-context'
export class VMProvider {
@ -12,9 +13,7 @@ export class VMProvider {
sendAsync: (query: JSONRPCRequestPayload, callback: JSONRPCResponseCallback) => void
}
newAccountCallback: {[stamp: number]: (error: Error, address: string) => void}
constructor (executionContext: ExecutionContext) {
this.executionContext = executionContext
this.worker = null
this.provider = null
@ -29,7 +28,7 @@ export class VMProvider {
})
}
async resetEnvironment () {
async resetEnvironment (stringifiedState?: string) {
if (this.worker) this.worker.terminate()
this.worker = new Worker(new URL('./worker-vm', import.meta.url))
const provider = this.executionContext.getProviderObject()
@ -76,10 +75,35 @@ export class VMProvider {
}
}
})
this.worker.postMessage({ cmd: 'init', fork: this.executionContext.getCurrentFork(), nodeUrl: provider?.options['nodeUrl'], blockNumber: provider?.options['blockNumber']})
if (stringifiedState) {
try {
const blockchainState = JSON.parse(stringifiedState)
const blockNumber = parseInt(blockchainState.latestBlockNumber, 16)
const stateDb = blockchainState.db
this.worker.postMessage({
cmd: 'init',
fork: this.executionContext.getCurrentFork(),
nodeUrl: provider?.options['nodeUrl'],
blockNumber,
stateDb,
blocks: blockchainState.blocks
})
} catch (e) {
console.error(e)
}
} else {
this.worker.postMessage({
cmd: 'init',
fork: this.executionContext.getCurrentFork(),
nodeUrl: provider?.options['nodeUrl'],
blockNumber: provider?.options['blockNumber']
})
}
})
}
// TODO: is still here because of the plugin API
// can be removed later when we update the API
createVMAccount (newAccount) {

@ -6,7 +6,7 @@ self.onmessage = (e: MessageEvent) => {
switch (data.cmd) {
case 'init':
{
provider = new Provider({ fork: data.fork, nodeUrl: data.nodeUrl, blockNumber: data.blockNumber })
provider = new Provider({ fork: data.fork, nodeUrl: data.nodeUrl, blockNumber: data.blockNumber, stateDb: data.stateDb, blocks: data.blocks})
provider.init().then(() => {
self.postMessage({
cmd: 'initiateResult',

@ -12,6 +12,7 @@ let requiredModules = [ // services + layout views + system views
'config',
'compilerArtefacts',
'compilerMetadata',
'compilerloader',
'contextualListener',
'editor',
'offsetToLineColumnConverter',
@ -108,6 +109,7 @@ export function isNative(name) {
'solidity',
'solidity-logic',
'solidityStaticAnalysis',
'solhint',
'solidityUnitTesting',
'layout',
'notification',

@ -168,7 +168,7 @@ Before starting coding, we should ensure all devs / contributors are aware of:
### 3) Documentation:
- The documentation is done / updated just after the feature / release in a team effort.
- Documentation work is filable as a github issue.
- Documentation work is fileable as a github issue.
- It is encouraged to find and link associated doc produced by the community (blog posts, videos, tutorials, ...)
---

@ -520,9 +520,9 @@
semver "^7.3.5"
"@openzeppelin/contracts@^4.7.3":
version "4.9.3"
resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.9.3.tgz#00d7a8cf35a475b160b3f0293a6403c511099364"
integrity sha512-He3LieZ1pP2TNt5JbkPA4PNT9WC3gOTOlDcFGJW4Le4QKqwmiNJCRt44APfxMxvq7OugU/cqYuPcSBzOw38DAg==
version "4.9.6"
resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.9.6.tgz#2a880a24eb19b4f8b25adc2a5095f2aa27f39677"
integrity sha512-xSmezSupL+y9VkHZJGDoCBpmnB2ogM13ccaYDWqJTfS3dbuHkgjuwDFUmaFauBCboQMGB/S5UqUl2y54X99BmA==
"@openzeppelin/wizard@^0.1.1":
version "0.1.1"
@ -2173,13 +2173,14 @@ es-to-primitive@^1.2.1:
is-date-object "^1.0.1"
is-symbol "^1.0.2"
es5-ext@^0.10.35, es5-ext@^0.10.50:
version "0.10.62"
resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.62.tgz#5e6adc19a6da524bf3d1e02bbc8960e5eb49a9a5"
integrity sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==
es5-ext@^0.10.35, es5-ext@^0.10.50, es5-ext@^0.10.62, es5-ext@~0.10.14:
version "0.10.63"
resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.63.tgz#9c222a63b6a332ac80b1e373b426af723b895bd6"
integrity sha512-hUCZd2Byj/mNKjfP9jXrdVZ62B8KuA/VoK7X8nUh5qT+AxDmcbvZz041oDVZdbIN1qW6XY9VDNwzkvKnZvK2TQ==
dependencies:
es6-iterator "^2.0.3"
es6-symbol "^3.1.3"
esniff "^2.0.1"
next-tick "^1.1.0"
es6-error@^4.1.1:
@ -2224,6 +2225,16 @@ escape-string-regexp@^4.0.0:
resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz"
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
esniff@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/esniff/-/esniff-2.0.1.tgz#a4d4b43a5c71c7ec51c51098c1d8a29081f9b308"
integrity sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==
dependencies:
d "^1.0.1"
es5-ext "^0.10.62"
event-emitter "^0.3.5"
type "^2.7.2"
etag@~1.8.1:
version "1.8.1"
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
@ -2351,6 +2362,14 @@ ethjs-unit@0.1.6:
bn.js "4.11.6"
number-to-bn "1.7.0"
event-emitter@^0.3.5:
version "0.3.5"
resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39"
integrity sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==
dependencies:
d "1"
es5-ext "~0.10.14"
eventemitter3@4.0.4:
version "4.0.4"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.4.tgz#b5463ace635a083d018bdc7c917b4c5f10a85384"
@ -2489,9 +2508,9 @@ finalhandler@1.2.0:
unpipe "~1.0.0"
follow-redirects@^1.15.0:
version "1.15.3"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a"
integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==
version "1.15.6"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b"
integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==
for-each@^0.3.3:
version "0.3.3"

@ -13,7 +13,7 @@
"icon": "https://raw.githubusercontent.com/protofire/solhint/master/solhint-icon.png",
"location": "hiddenPanel",
"url": "",
"documentation": "https://remix-plugins.readthedocs.io/en/latest/",
"documentation": "https://remix-ide.readthedocs.io/en/latest/static_analysis.html",
"repo": "https://github.com/ethereum/remix-project",
"maintainedBy": "Remix",
"authorContact": "remix@ethereum.org"

@ -245,9 +245,9 @@ fast-json-stable-stringify@^2.0.0:
integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
follow-redirects@^1.14.0:
version "1.15.4"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.4.tgz#cdc7d308bf6493126b17ea2191ea0ccf3e535adf"
integrity sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==
version "1.15.6"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b"
integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==
fs.realpath@^1.0.0:
version "1.0.0"

@ -222,3 +222,47 @@ html, body, #root, main {
margin: 15px;
padding: 15px;
}
.cursor-status {
}
.cursor-status :hover {
cursor: pointer;
}
.accordion-background {
background-color: var(--body-bg);
}
.accordion-background:hover {
cursor: pointer;
}
.vyper-compile-warning,
.vyper-compile-error {
white-space: pre-line;
word-wrap: break-word;
cursor: pointer;
position: relative;
margin: 0.5em 0 1em 0;
border-radius: 5px;
line-height: 20px;
padding: 8px 15px;
}
.vyper-compile-warning pre,
.vyper-compile-error pre {
white-space: pre-line;
overflow-y: hidden;
background-color: transparent;
margin: 0;
font-size: 12px;
border: 0 none;
padding: 0;
border-radius: 0;
}
.vyper-panel-width {
width: 94%;
}

@ -1,4 +1,4 @@
import React, {useState, useEffect} from 'react'
import React, {useState, useEffect, useRef} from 'react'
import {remixClient} from './utils'
import {CompilationResult} from '@remixproject/plugin-api'
@ -11,11 +11,14 @@ import LocalUrlInput from './components/LocalUrl'
import ToggleButtonGroup from 'react-bootstrap/ToggleButtonGroup'
import ToggleButton from 'react-bootstrap/ToggleButton'
import Button from 'react-bootstrap/Button'
import Accordion from 'react-bootstrap/Accordion'
import Card from 'react-bootstrap/Card'
import './app.css'
import {CustomTooltip} from '@remix-ui/helper'
import {Form} from 'react-bootstrap'
import {CompileErrorCard} from './components/CompileErrorCard'
import CustomAccordionToggle from './components/CustomAccordionToggle'
interface AppState {
status: 'idle' | 'inProgress'
@ -37,11 +40,16 @@ const App = () => {
localUrl: 'http://localhost:8000/',
})
const spinnerIcon = useRef(null)
useEffect(() => {
async function start() {
try {
await remixClient.loaded()
remixClient.onFileChange((name) => setContract(name))
remixClient.onFileChange((name) => {
setOutput({})
setContract(name)
})
remixClient.onNoFileSelected(() => setContract(''))
} catch (err) {
console.log(err)
@ -68,9 +76,6 @@ const App = () => {
useEffect(() => {
remixClient.eventEmitter.on('setOutput', (payload) => {
if (payload.status === 'failed') {
console.error('Error in the compiler', payload)
}
setOutput(payload)
})
@ -98,17 +103,17 @@ const App = () => {
setOutput(remixClient.compilerOutput)
}
const startingCompilation = () => {
if (!spinnerIcon.current) return
spinnerIcon.current.setAttribute('title', 'compiling...')
spinnerIcon.current.classList.remove('remixui_bouncingIcon')
spinnerIcon.current.classList.add('remixui_spinningIcon')
}
const [toggleAccordion, setToggleAccordion] = useState(false)
return (
<main id="vyper-plugin">
<header>
<div className="title">
<img src={'assets/vyperLogo_v2.webp'} alt="Vyper logo" />
<h4 className="pl-1">yper Compiler</h4>
</div>
<a rel="noopener noreferrer" href="https://github.com/ethereum/remix-project/tree/master/apps/vyper" target="_blank">
<i className="fab fa-github"></i>
</a>
</header>
<section>
<div className="px-3 pt-3 mb-3 w-100">
<CustomTooltip placement="bottom" tooltipText="Clone Vyper examples. Switch to the File Explorer to see the examples.">
@ -117,25 +122,55 @@ const App = () => {
</Button>
</CustomTooltip>
</div>
<Form>
<div className="d-flex flex-row gap-5 mb-3 mt-2">
<Form.Check inline data-id="remote-compiler" type="radio" value={state.environment} checked={state.environment === 'remote'} onChange={() => setEnvironment('remote')} label="Remote Compiler" style={{cursor: state.environment === 'remote' ? 'default' : 'pointer'}} className="d-flex mr-4" />
<Form.Check inline id="local-compiler" data-id="local-compiler" checked={state.environment === 'local'} type="radio" name="local" value={state.environment} onChange={() => setEnvironment('local')} label="Local Compiler" style={{cursor: state.environment === 'local' ? 'default' : 'pointer'}} />
<Accordion className="border-0 w-100 mb-3 accordion-background">
<div className="border-0">
<div className="">
<CustomAccordionToggle eventKey="0">
<span className="">Advanced Compiler Settings</span>
</CustomAccordionToggle>
</div>
<Accordion.Collapse eventKey="0">
<div className="pt-2">
<Form>
<div className="d-flex flex-row justify-content-around mb-1 mt-2">
<div className={`custom-control custom-radio ${state.environment === 'remote' ? 'd-flex' : 'd-flex cursor-status'}`}>
<input type="radio" id="remote-compiler" data-id="remote-compiler" name="remote" value={state.environment} checked={state.environment === 'remote'} onChange={() => setEnvironment('remote')} className={`custom-control-input ${state.environment === 'remote' ? 'd-flex mr-1' : 'd-flex mr-1 cursor-status'}`} />
<label htmlFor="remote-compiler" className="form-check-label custom-control-label">Remote Compiler</label>
</div>
<div className={`custom-control custom-radio ${state.environment === 'local' ? 'mr-2' : `cursor-status`}`}>
<input id="local-compiler" data-id="local-compiler" checked={state.environment === 'local'} type="radio" name="local" value={state.environment} onChange={() => setEnvironment('local')} className={`custom-control-input ${state.environment === 'local' ? '' : `cursor-status`}`} />
<label htmlFor="local-compiler" className="form-check-label custom-control-label">Local Compiler</label>
</div>
</div>
</Form>
<LocalUrlInput url={state.localUrl} setUrl={setLocalUrl} environment={state.environment} />
</div>
</Accordion.Collapse>
</div>
</Form>
<span className="px-3 mt-1 mb-1 small text-warning">Specify the compiler version & EVM version in the .vy file</span>
<LocalUrlInput url={state.localUrl} setUrl={setLocalUrl} environment={state.environment} />
</Accordion>
<span className="px-3 mt-3 mb-3 small text-warning">
Specify the{' '}
<a className="text-warning" target="_blank" href="https://remix-ide.readthedocs.io/en/latest/vyper.html#specify-vyper-version">
compiler version
</a>{' '}
&{' '}
<a className="text-warning" href="http://docs.vyperlang.org/en/stable/compiling-a-contract.html#setting-the-target-evm-version" target="_blank" rel="noopener noreferrer">
EVM version
</a>{' '}
in the .vy file.
</span>
<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} />
</div>
<article id="result" className="px-2 mx-2 border-top mt-3">
<article id="result" className="p-2 mx-3 border-top mt-2">
{output && Object.keys(output).length > 0 && output.status !== 'failed' ? (
<>
<VyperResult output={output} plugin={remixClient} />
</>
) : output.status === 'failed' ? (
<CompileErrorCard output={output} />
<CompileErrorCard output={output} plugin={remixClient} />
) : null}
</article>
</section>

@ -1,12 +1,17 @@
import {CopyToClipboard} from '@remix-ui/clipboard'
import Reaact from 'react'
import { RemixClient } from '../utils'
export function CompileErrorCard(props: any) {
export function CompileErrorCard(props: { output: any, plugin: RemixClient }) {
return (
<div id="vyperErrorResult" className="px-2 mx-3 alert alert-danger error" title={props.output.message}>
<i className="fas fa-exclamation-circle text-danger"></i>
<div
id="vyperErrorResult"
className=" d-flex flex-column p-2 alert alert-danger error vyper-compile-error vyper-panel-width"
title={props.output?.title}
>
<span
data-id="error-message"
className="text-center"
className="text-left"
style={{
overflowX: 'hidden',
textOverflow: 'ellipsis',
@ -14,6 +19,16 @@ export function CompileErrorCard(props: any) {
>
{props.output.message.trim()}
</span>
<div className="d-flex flex-column pt-3 align-items-end mb-2">
<div>
<span className="border border-success text-success btn-sm" onClick={async () => await props.plugin.askGpt(props.output.message)}>
Ask GPT
</span>
<span className="ml-3 pt-1 py-1">
<CopyToClipboard content={props.output.message} className={`p-0 m-0 far fa-copy alert alert-danger`} direction={'top'} />
</span>
</div>
</div>
</div>
)
}

@ -29,7 +29,7 @@ function CompilerButton({contract, setOutput, compilerUrl, resetCompilerState}:
className="btn btn-primary w-100 d-block btn-block text-break remixui_disabled mb-1 mt-3"
>
<div className="d-flex align-items-center justify-content-center fa-1x">
<span className="fas fa-sync fa-pulse mr-1" />
{/* <span className="fas fa-sync fa-pulse mr-1" /> */}
<div className="text-truncate overflow-hidden text-nowrap">
<span>Compile</span>
<span className="ml-1 text-nowrap">{contract}</span>

@ -0,0 +1,26 @@
import React, { useState } from 'react'
import { useAccordionToggle } from 'react-bootstrap/AccordionToggle'
export type CustomAccordionToggleProps = {
children: React.ReactNode
eventKey: string
callback?: any
}
export default function CustomAccordionToggle({ children, eventKey }: CustomAccordionToggleProps) {
const [toggleAccordion, setToggleAccordion] = useState(false)
const decoratedOnClick = useAccordionToggle(eventKey, () =>
setToggleAccordion(!toggleAccordion)
)
return (
<div
onClick={decoratedOnClick}
className="d-flex flex-row justify-content-between align-items-center mx-3"
>
{children}
<i className={toggleAccordion ? 'far fa-angle-down' : 'far fa-angle-right'}></i>
</div>
)
}

@ -58,9 +58,8 @@ function VyperResult({ output, plugin }: VyperResultProps) {
return (
<>
<div className="border border-top"></div>
<div className="d-flex justify-content-center px-2 w-100 flex-column border border-bottom">
<button data-id="compilation-details" className="btn btn-secondary w-100" onClick={async () => {
<div className="d-flex justify-content-center mx-3 mb-3 mt-1 vyper-panel-width flex-column">
<button data-id="compilation-details" className="btn btn-secondary d-block btn-block" onClick={async () => {
await plugin?.call('vyperCompilationDetails', 'showDetails', output)
}}>
<span>Compilation Details</span>
@ -68,17 +67,17 @@ function VyperResult({ output, plugin }: VyperResultProps) {
<div className="mt-1">
<div className="input-group input-group mt-3 d-flex flex-row-reverse">
<div className="btn-group align-self-start" role="group" aria-label="Copy to Clipboard">
<CopyToClipboard tip={'Copy ABI to clipboard'} getContent={() => (Object.values(output)[0] as OutputType).abi} direction="bottom" icon="far fa-copy">
<button className="btn remixui_copyButton">
<CopyToClipboard tip={'Copy ABI to clipboard'} getContent={() => (Object.values(output)[1] as OutputType)?.abi} direction="bottom" icon="far fa-copy">
<span className="btn remixui_copyButton">
<i className="remixui_copyIcon far fa-copy" aria-hidden="true"></i>
<span>ABI</span>
</button>
</span>
</CopyToClipboard>
<CopyToClipboard tip={'Copy Bytecode to clipboard'} getContent={() => (Object.values(output)[0] as OutputType).bytecode.object} direction="bottom" icon="far fa-copy">
<button className="btn remixui_copyButton">
<CopyToClipboard tip={'Copy Bytecode to clipboard'} getContent={() => (Object.values(output)[1] as OutputType)?.bytecode.object} direction="bottom" icon="far fa-copy">
<span className="btn remixui_copyButton">
<i className="remixui_copyIcon far fa-copy" aria-hidden="true"></i>
<span>Bytecode</span>
</button>
</span>
</CopyToClipboard>
</div>
</div>

@ -31,9 +31,7 @@ export interface VyperCompilationError {
export type VyperCompilationOutput = VyperCompilationResult | VyperCompilationError
/** Check if the output is an error */
export function isCompilationError(output: VyperCompilationOutput): output is VyperCompilationError {
return output.status === 'failed'
}
export const isCompilationError = (output: VyperCompilationOutput): output is VyperCompilationError => output.status === 'failed'
export function normalizeContractPath(contractPath: string): string[] {
const paths = contractPath.split('/')
@ -52,22 +50,113 @@ function parseErrorString(errorString) {
// Split the string into lines
let lines = errorString.trim().split('\n')
// Extract the line number and message
let message = lines[1].trim()
let message = errorString.trim()
let targetLine = lines[2].split(',')
let lineColumn = targetLine[targetLine.length - 1].split(' ')[2].split(':')
let tline = lines[2].trim().split(' ')[1].split(':')
const errorObject = {
status: 'failed',
message: message,
column: parseInt(lineColumn[1]),
line: parseInt(lineColumn[0])
column: tline[1],
line: tline[0]
}
message = null
targetLine = null
lineColumn = null
lines = null
tline = null
return errorObject
}
const buildError = (output) => {
if (isCompilationError(output)) {
const line = output.line
if (line) {
const lineColumnPos = {
start: {line: line - 1, column: 10},
end: {line: line - 1, column: 10}
}
// remixClient.highlight(lineColumnPos as any, _contract.name, '#e0b4b4')
} else {
const regex = output?.message?.match(/line ((\d+):(\d+))+/g)
const errors = output?.message?.split(/line ((\d+):(\d+))+/g) // extract error message
if (regex) {
let errorIndex = 0
regex.map((errorLocation) => {
const location = errorLocation?.replace('line ', '').split(':')
let message = errors[errorIndex]
errorIndex = errorIndex + 4
if (message && message?.split('\n\n').length > 0) {
try {
message = message?.split('\n\n')[message.split('\n\n').length - 1]
} catch (e) {}
}
if (location?.length > 0) {
const lineColumnPos = {
start: {line: parseInt(location[0]) - 1, column: 10},
end: {line: parseInt(location[0]) - 1, column: 10}
}
// remixClient.highlight(lineColumnPos as any, _contract.name, message)
}
})
}
}
throw new Error(output.message)
}
}
const compileReturnType = (output, contract) => {
const t: any = toStandardOutput(contract, output)
const temp = _.merge(t['contracts'][contract])
const normal = normalizeContractPath(contract)[2]
const abi = temp[normal]['abi']
const evm = _.merge(temp[normal]['evm'])
const dpb = evm.deployedBytecode
const runtimeBytecode = evm.bytecode
const methodIdentifiers = evm.methodIdentifiers
const version = output?.compilers[0]?.version ?? '0.3.10'
const optimized = output?.compilers[0]?.settings?.optimize ?? true
const evmVersion = ''
const result: {
contractName: any,
abi: any,
bytecode: any,
runtimeBytecode: any,
ir: '',
methodIdentifiers: any,
version?: '',
evmVersion?: ''
optimized?: boolean
} = {
contractName: normal,
abi,
bytecode: dpb,
runtimeBytecode,
ir: '',
methodIdentifiers,
version,
evmVersion,
optimized
}
return result
}
const fixContractContent = (content: string) => {
if (content.length === 0) return
const pragmaFound = content.includes('#pragma version ^0.3.10')
const evmVerFound = content.includes('#pragma evm-version shanghai')
const pragma = '#pragma version ^0.3.10'
const evmVer = '#pragma evm-version shanghai'
if (!evmVerFound) {
content = `${evmVer}\n${content}`
}
if (!pragmaFound) {
content = `${pragma}\n${content}`
}
return content
}
/**
* Compile the a contract
* @param url The url of the compiler
@ -82,11 +171,13 @@ export async function compile(url: string, contract: Contract): Promise<any> {
throw new Error('Use extension .vy for Vyper.')
}
let contractName = contract['name']
const compilePackage = {
manifest: 'ethpm/3',
sources: {
[contractName] : {content : contract.content}
[contractName] : {content : fixContractContent(contract.content)}
}
}
let response = await axios.post(`${url}compile`, compilePackage )
@ -181,15 +272,11 @@ export async function compileContract(contract: string, compilerUrl: string, set
try {
_contract = await remixClient.getContract()
} catch (e: any) {
// if (setOutput === null || setOutput === undefined) {
const compileResult = {
const errorGettingContract = {
status: 'failed',
message: e.message
}
remixClient.eventEmitter.emit('setOutput', compileResult)
// } else {
// setOutput('', {status: 'failed', message: e.message})
// }
remixClient.eventEmitter.emit('setOutput', errorGettingContract)
return
}
remixClient.changeStatus({
@ -198,76 +285,19 @@ export async function compileContract(contract: string, compilerUrl: string, set
title: 'Compiling'
})
let output
try {
output = await compile(compilerUrl, _contract)
console.log('checking compile result', output)
remixClient.eventEmitter.emit('setOutput', output)
} catch (e: any) {
// try {
output = await compile(compilerUrl, _contract)
if (output.status === 'failed') {
remixClient.changeStatus({
key: 'failed',
type: 'error',
title: `${e.message} debugging`
title: 'Compilation failed...'
})
// setOutput !== null || setOutput !== undefined && setOutput('', {status: 'failed', message: e.message})
remixClient.eventEmitter.emit('setOutput', {status: 'failed', message: e.message})
remixClient.eventEmitter.emit('setOutput', {status: 'failed', message: output.message, title: 'Error compiling...', line: output.line, column: output.column})
output = null
return
}
const compileReturnType = () => {
const t: any = toStandardOutput(contract, output)
const temp = _.merge(t['contracts'][contract])
const normal = normalizeContractPath(contract)[2]
const abi = temp[normal]['abi']
const evm = _.merge(temp[normal]['evm'])
const dpb = evm.deployedBytecode
const runtimeBytecode = evm.bytecode
const methodIdentifiers = evm.methodIdentifiers
const result = {
contractName: normal,
abi: abi,
bytecode: dpb,
runtimeBytecode: runtimeBytecode,
ir: '',
methodIdentifiers: methodIdentifiers
}
return result
}
// ERROR
if (isCompilationError(output)) {
const line = output.line
if (line) {
const lineColumnPos = {
start: {line: line - 1, column: 10},
end: {line: line - 1, column: 10}
}
// remixClient.highlight(lineColumnPos as any, _contract.name, '#e0b4b4')
} else {
const regex = output?.message?.match(/line ((\d+):(\d+))+/g)
const errors = output?.message?.split(/line ((\d+):(\d+))+/g) // extract error message
if (regex) {
let errorIndex = 0
regex.map((errorLocation) => {
const location = errorLocation?.replace('line ', '').split(':')
let message = errors[errorIndex]
errorIndex = errorIndex + 4
if (message && message?.split('\n\n').length > 0) {
try {
message = message?.split('\n\n')[message.split('\n\n').length - 1]
} catch (e) {}
}
if (location?.length > 0) {
const lineColumnPos = {
start: {line: parseInt(location[0]) - 1, column: 10},
end: {line: parseInt(location[0]) - 1, column: 10}
}
// remixClient.highlight(lineColumnPos as any, _contract.name, message)
}
})
}
}
throw new Error(output.message)
}
// SUCCESS
// remixClient.discardHighlight()
remixClient.changeStatus({
@ -278,12 +308,12 @@ export async function compileContract(contract: string, compilerUrl: string, set
const data = toStandardOutput(_contract.name, output)
remixClient.compilationFinish(_contract.name, _contract.content, data)
const contractName = _contract['name']
const compileResult = compileReturnType(output, contractName)
if (setOutput === null || setOutput === undefined) {
const contractName = _contract['name']
const compileResult = compileReturnType()
remixClient.eventEmitter.emit('setOutput', { contractName, compileResult })
} else {
setOutput(_contract.name, compileReturnType())
remixClient.eventEmitter.emit('setOutput', { contractName, compileResult })
}
} catch (err: any) {
remixClient.changeStatus({

@ -60,6 +60,23 @@ export class RemixClient extends PluginClient {
}
}
async askGpt(message: string) {
if (message.length === 0) {
this.client.call('terminal', 'log', { type: 'log', value: 'kindly send a proper message so I can respond please' })
return
}
try {
const formattedMessage = `
${message}
can you explain why this error occurred and how to fix it?
`
await this.client.call('openaigpt' as any, 'message', formattedMessage)
} catch (err) {
console.error('unable to askGpt')
console.error(err)
}
}
async cloneVyperRepo() {
try {
// @ts-ignore
@ -144,4 +161,3 @@ export class RemixClient extends PluginClient {
}
export const remixClient = new RemixClient()
// export const RemixClientContext = React.createContext(new RemixClient())

@ -7,8 +7,12 @@
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/x-icon" href="favicon.ico" />
<link rel="stylesheet" integrity="ha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf"
crossorigin="anonymous" href="https://use.fontawesome.com/releases/v5.8.1/css/all.css">
</head>
<body>
<div id="root"></div>
<script src="https://kit.fontawesome.com/41dd021e94.js" crossorigin="anonymous"></script>
</body>
</html>

@ -1,12 +1,12 @@
{
"name": "vyper",
"displayName": "Vyper",
"displayName": "Vyper Compiler",
"methods": ["getCompilationResult", "compile", "vyperCompileCustomAction"],
"url": "https://ipfs-cluster.ethdevops.io/ipfs/QmbmPzUg7ghTKcF2eo64zm1k1LKdibYfqYmiqXkHKXks8r",
"documentation": "https://remix-ide.readthedocs.io/en/latest/plugin_list.html",
"documentation": "https://remix-ide.readthedocs.io/en/latest/vyper.html",
"description": "Compile vyper contracts",
"kind": "compiler",
"icon": "data:image/svg+xml;base64,PHN2ZyBpZD0iRmxhdF9Mb2dvIiBkYXRhLW5hbWU9IkZsYXQgTG9nbyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgMjA0OCAxNzczLjYyIj4gIDx0aXRsZT52eXBlci1sb2dvLWZsYXQ8L3RpdGxlPiAgPHBvbHlsaW5lIHBvaW50cz0iMTAyNCA4ODYuODEgNzY4IDEzMzAuMjIgMTAyNCAxNzczLjYyIDEyODAgMTMzMC4yMiAxMDI0IDg4Ni44MSIgc3R5bGU9ImZpbGw6IzMzMyIvPiAgPHBvbHlsaW5lIHBvaW50cz0iMTI4MCA0NDMuNDEgMTAyNCA4ODYuODEgMTI4MCAxMzMwLjIyIDE1MzYgODg2LjgxIDEyODAgNDQzLjQxIiBzdHlsZT0iZmlsbDojNjY2Ii8+ICA8cG9seWxpbmUgcG9pbnRzPSI3NjggNDQzLjQxIDUxMiA4ODYuODEgNzY4IDEzMzAuMjIgMTAyNCA4ODYuODEgNzY4IDQ0My40MSIgc3R5bGU9ImZpbGw6IzY2NiIvPiAgPHBvbHlsaW5lIHBvaW50cz0iMTUzNiAwIDEyODAgNDQzLjQxIDE1MzYgODg2LjgxIDE3OTIgNDQzLjQxIDE1MzYgMCIgc3R5bGU9ImZpbGw6IzhjOGM4YyIvPiAgPHBvbHlsaW5lIHBvaW50cz0iMTE1MiAyMjEuNyA4OTYgMjIxLjcgNzY4IDQ0My40MSAxMDI0IDg4Ni44MSAxMjgwIDQ0My40MSAxMTUyIDIyMS43IiBzdHlsZT0iZmlsbDojOGM4YzhjIi8+ICA8cG9seWxpbmUgcG9pbnRzPSI1MTIgMCAyNTYgNDQzLjQxIDUxMiA4ODYuODEgNzY4IDQ0My40MSA1MTIgMCIgc3R5bGU9ImZpbGw6IzhjOGM4YyIvPiAgPHBvbHlsaW5lIHBvaW50cz0iMjA0OCAwIDE1MzYgMCAxNzkyIDQ0My40IDIwNDggMCIgc3R5bGU9ImZpbGw6I2IyYjJiMiIvPiAgPHBvbHlsaW5lIHBvaW50cz0iNTEyIDAgMCAwIDI1NiA0NDMuNCA1MTIgMCIgc3R5bGU9ImZpbGw6I2IyYjJiMiIvPjwvc3ZnPg==",
"icon": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzM4IiBoZWlnaHQ9IjM3NiIgdmlld0JveD0iMCAwIDMzOCAzNzYiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik0xOTUuMzA0IDMxMS44MzdMODIuNTE4IDE5OS4wODRDNzcuOTI0MSAxOTQuMzA0IDc1LjM5MjcgMTg3LjgzNyA3NS4zOTI3IDE4MC42MlYxOS42OTE0Qzc1LjM5MjcgMTAuMzE4OCA2Ni4wMTc0IDAuOTQ2MTI3IDU2LjY0MiAwLjk0NjEyN0gxOS4wNDY3QzkuNjcxMjggMC45NDYxMjcgMC4yOTU4OTggMTAuMzE4OCAwLjI5NTg5OCAxOS42OTE0VjIwNy4xNDVDMC4yOTU4OTggMjE3LjA4IDMuOTUyMyAyMjYuNjQgMTAuOTgzOCAyMzMuNjY5TDE0Mi4yMzkgMzY0Ljg4NkMxNDkuNTUyIDM3Mi4xOTcgMTU5LjExNSAzNzUuODUyIDE2OC43NzIgMzc1Ljg1MkgxODcuNTIyQzE5Ny45MjkgMzc1Ljg1MiAyMDYuMjczIDM2Ny40MTcgMjA2LjI3MyAzNTcuMTA3VjMzOC40NTZDMjA2LjI3MyAzMjguODAyIDIwMi42MTcgMzE5LjI0MiAxOTUuMzA0IDMxMS45MzFWMzExLjgzN1oiIGZpbGw9IiMxODBDMjUiLz4KPHBhdGggZD0iTTMzNy44MSAxOS41OTc3QzMzNy44MSAxMC4xMzEzIDMyOC41MjkgMC44NTI0MTcgMzE5LjA1OSAwLjg1MjQxN0gyODEuNDY0QzI3Mi4wODkgMC44NTI0MTcgMjYyLjcxMyAxMC4yMjUxIDI2Mi43MTMgMTkuNTk3N1YxODAuNDMzQzI2Mi43MTMgMTg3LjY1IDI1OS45MDEgMTk0LjExNyAyNTUuMjEzIDE5OC45OUwyMTcuNDMgMjM2Ljc2MkMyMTAuMTE4IDI0NC4wNzMgMjA2LjQ2MSAyNTMuNjMzIDIwNi40NjEgMjYzLjI4N1YyODIuMDMyQzIwNi40NjEgMjkyLjQzNiAyMTQuODk5IDMwMC43NzggMjI1LjIxMiAzMDAuNzc4SDI0My45NjNDMjUzLjUyNiAzMDAuNzc4IDI2My4xODIgMjk3LjEyMiAyNzAuNDk1IDI4OS44MTFMMzI2Ljc0NyAyMzMuNTc2QzMzMy43NzkgMjI2LjU0NiAzMzcuNzE2IDIxNi45ODYgMzM3LjcxNiAyMDcuMDUxVjE5LjU5NzdIMzM3LjgxWiIgZmlsbD0iIzE4MEMyNSIvPgo8L3N2Zz4K",
"location": "sidePanel",
"repo": "https://github.com/ethereum/remix-project/tree/master/apps/vyper",
"maintainedBy": "Remix",

@ -1,6 +1,6 @@
{
"name": "@remix-project/ghaction-helper",
"version": "0.1.22",
"version": "0.1.24",
"description": "Solidity Tests GitHub Action Helper",
"main": "src/index.js",
"scripts": {
@ -19,17 +19,17 @@
},
"homepage": "https://github.com/ethereum/remix-project#readme",
"devDependencies": {
"@remix-project/remix-solidity": "^0.5.28",
"@remix-project/remix-solidity": "^0.5.30",
"@types/chai": "^4.3.4",
"typescript": "^4.9.3"
},
"dependencies": {
"@ethereum-waffle/chai": "^3.4.4",
"@remix-project/remix-simulator": "^0.2.42",
"@remix-project/remix-simulator": "^0.2.44",
"chai": "^4.3.7",
"ethers": "^5.7.2",
"web3": "^4.1.1"
},
"types": "./src/index.d.ts",
"gitHead": "817089ab1f206f5e195756a4051afb812ec26901"
"gitHead": "751c821c0264dec832ee8739800e32b79b09c00a"
}

@ -1,6 +1,6 @@
{
"name": "@remix-project/remix-analyzer",
"version": "0.5.51",
"version": "0.5.53",
"description": "Tool to perform static analysis on Solidity smart contracts",
"scripts": {
"test": "./../../node_modules/.bin/ts-node --project ../../tsconfig.base.json --require tsconfig-paths/register ./../../node_modules/.bin/tape ./test/tests.ts"
@ -25,8 +25,8 @@
"@ethereumjs/tx": "^4.1.1",
"@ethereumjs/util": "^8.0.5",
"@ethereumjs/vm": "^6.4.1",
"@remix-project/remix-astwalker": "^0.0.72",
"@remix-project/remix-lib": "^0.5.49",
"@remix-project/remix-astwalker": "^0.0.74",
"@remix-project/remix-lib": "^0.5.51",
"async": "^2.6.2",
"ethers": "^5.4.2",
"ethjs-util": "^0.1.6",
@ -50,6 +50,6 @@
"typescript": "^3.7.5"
},
"typings": "src/index.d.ts",
"gitHead": "817089ab1f206f5e195756a4051afb812ec26901",
"gitHead": "751c821c0264dec832ee8739800e32b79b09c00a",
"main": "./src/index.js"
}

@ -1,6 +1,6 @@
{
"name": "@remix-project/remix-astwalker",
"version": "0.0.72",
"version": "0.0.74",
"description": "Tool to walk through Solidity AST",
"main": "src/index.js",
"scripts": {
@ -37,7 +37,7 @@
"@ethereumjs/tx": "^4.1.1",
"@ethereumjs/util": "^8.0.5",
"@ethereumjs/vm": "^6.4.1",
"@remix-project/remix-lib": "^0.5.49",
"@remix-project/remix-lib": "^0.5.51",
"@types/tape": "^4.2.33",
"async": "^2.6.2",
"ethers": "^5.4.2",
@ -53,6 +53,6 @@
"tap-spec": "^5.0.0"
},
"typings": "src/index.d.ts",
"gitHead": "817089ab1f206f5e195756a4051afb812ec26901",
"gitHead": "751c821c0264dec832ee8739800e32b79b09c00a",
"types": "./src/index.d.ts"
}

@ -8,3 +8,4 @@ export * from './types/contract'
export { LinkLibraries, DeployLibraries } from './lib/link-libraries'
export { OpenZeppelinProxy } from './lib/openzeppelin-proxy'
export { fetchContractFromEtherscan } from './lib/helpers/fetch-etherscan'
export { fetchContractFromBlockscout } from './lib/helpers/fetch-blockscout'

@ -4,7 +4,7 @@ import { util } from '@remix-project/remix-lib'
import { toChecksumAddress } from '@ethereumjs/util'
import { fetchContractFromEtherscan } from './helpers/fetch-etherscan'
import { fetchContractFromSourcify } from './helpers/fetch-sourcify'
import { UUPSDeployedByteCode, UUPSCompilerVersion, UUPSOptimize, UUPSRuns, UUPSEvmVersion, UUPSLanguage, UUPSDeployedByteCodeV5, UUPSCompilerVersionV5 } from './constants/uups'
import { UUPSDeployedByteCode, UUPSCompilerVersion, UUPSOptimize, UUPSRuns, UUPSEvmVersion, UUPSLanguage, UUPSDeployedByteCodeV5, UUPSCompilerVersionV5, UUPSEvmVersionv5, UUPSOptimizev5 } from './constants/uups'
const profile = {
name: 'fetchAndCompile',
@ -88,8 +88,8 @@ export class FetchAndCompile extends Plugin {
const settings = {
version: UUPSCompilerVersionV5,
language: UUPSLanguage,
evmVersion: UUPSEvmVersion,
optimize: UUPSOptimize,
evmVersion: UUPSEvmVersionv5,
optimize: UUPSOptimizev5,
runs: UUPSRuns
}
const proxyUrl = 'https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v5.0.0/contracts/proxy/ERC1967/ERC1967Proxy.sol'

File diff suppressed because one or more lines are too long

@ -117,8 +117,11 @@ export class GistHandler extends Plugin {
const gistIdWorkspace = 'gist ' + gistId
const workspaces = await this.call('filePanel', 'getWorkspaces')
const found = workspaces.find((workspace) => workspace.name === gistIdWorkspace)
if (found) {
await this.call('notification', 'alert', `workspace "${gistIdWorkspace}" already exist`)
if (found) {
await this.call('notification', 'alert', {
id: 'gistAlert',
message: `workspace "${gistIdWorkspace}" already exists`,
})
return
}
await this.call('filePanel', 'createWorkspace', 'gist ' + gistId, '', true)

@ -0,0 +1,66 @@
export const fetchContractFromBlockscout = async (plugin, endpoint, contractAddress, targetPath, shouldSetFile = true) => {
let data
const compilationTargets = {}
try {
data = await fetch('https://' + endpoint + '/api?module=contract&action=getsourcecode&address=' + contractAddress)
data = await data.json()
// blockscout api doc https://blockscout.com/poa/core/api-docs
if (data.message === 'OK' && data.status === "1") {
if (data.result.length) {
if (!data.result[0].SourceCode || data.result[0].SourceCode === '') {
throw new Error(`contract not verified on Blockscout ${endpoint} network`)
}
}
} else throw new Error('unable to retrieve contract data ' + data.message)
} catch (e) {
throw new Error('unable to retrieve contract data: ' + e.message)
}
if (!data || !data.result) {
return null
}
if (data.result[0].FileName === '') {
const fileName = `${targetPath}/${data.result[0].ContractName}.sol`
if (shouldSetFile) await plugin.call('fileManager', 'setFile', fileName, data.result[0].SourceCode)
compilationTargets[fileName] = { content: data.result[0].SourceCode }
} else {
const sources = {}
sources[data.result[0].FileName] = data.result[0].SourceCode
if (data.result[0].AdditionalSources && Array.isArray(data.result[0].AdditionalSources)) {
for (const object of data.result[0].AdditionalSources) {
sources[object.Filename] = object.SourceCode
}
}
for (let [file, source] of Object.entries(sources)) { // eslint-disable-line
file = file.replace('browser/', '') // should be fixed in the remix IDE end.
file = file.replace(/^\//g, '') // remove first slash.
if (await plugin.call('contentImport', 'isExternalUrl', file)) {
// nothing to do, the compiler callback will handle those
} else {
const path = `${targetPath}/${file}`
const content = source
if (shouldSetFile) await plugin.call('fileManager', 'setFile', path, content)
compilationTargets[path] = { content }
}
}
}
let runs = 0
try {
runs = parseInt(data.result[0].OptimizationRuns)
} catch (e) { }
const settings = {
version: data.result[0].CompilerVersion.replace(/^v/, ''),
language: 'Solidity',
evmVersion: data.result[0].EVMVersion.toLowerCase(),
optimize: data.result[0].OptimizationUsed === 'true',
runs
}
return {
settings,
compilationTargets
}
}

@ -1,6 +1,6 @@
{
"name": "@remix-project/remix-debug",
"version": "0.5.42",
"version": "0.5.44",
"description": "Tool to debug Ethereum transactions",
"contributors": [
{
@ -26,10 +26,10 @@
"@ethereumjs/tx": "^4.1.1",
"@ethereumjs/util": "^8.0.5",
"@ethereumjs/vm": "^6.4.1",
"@remix-project/remix-astwalker": "^0.0.72",
"@remix-project/remix-lib": "^0.5.49",
"@remix-project/remix-simulator": "^0.2.42",
"@remix-project/remix-solidity": "^0.5.28",
"@remix-project/remix-astwalker": "^0.0.74",
"@remix-project/remix-lib": "^0.5.51",
"@remix-project/remix-simulator": "^0.2.44",
"@remix-project/remix-solidity": "^0.5.30",
"ansi-gray": "^0.1.1",
"async": "^2.6.2",
"color-support": "^1.1.3",
@ -69,6 +69,6 @@
},
"homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-debug#readme",
"typings": "src/index.d.ts",
"gitHead": "817089ab1f206f5e195756a4051afb812ec26901",
"gitHead": "751c821c0264dec832ee8739800e32b79b09c00a",
"types": "./src/index.d.ts"
}

@ -1,6 +1,6 @@
{
"name": "@remix-project/remix-lib",
"version": "0.5.49",
"version": "0.5.51",
"description": "Library to various Remix tools",
"contributors": [
{
@ -55,6 +55,6 @@
},
"homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-lib#readme",
"typings": "src/index.d.ts",
"gitHead": "817089ab1f206f5e195756a4051afb812ec26901",
"gitHead": "751c821c0264dec832ee8739800e32b79b09c00a",
"types": "./src/index.d.ts"
}

@ -21,6 +21,7 @@ export class LogsManager {
eachOf(block.transactions, (tx: any, i, next) => {
const txHash = '0x' + tx.hash().toString('hex')
web3.eth.getTransactionReceipt(txHash, (_error, receipt) => {
if (!receipt) return next()
for (const log of receipt.logs) {
this.oldLogs.push({ type: 'block', blockNumber, block, tx, log, txNumber: i, receipt })
const subscriptions = this.getSubscriptionsFor({ type: 'block', blockNumber, block, tx, log, receipt})

@ -24,24 +24,23 @@ export class TxRunnerVM {
pendingTxs
vmaccounts
queusTxs
blocks
blocks: Buffer[]
logsManager
commonContext
blockParentHash
nextNonceForCall: number
getVMObject: () => any
constructor (vmaccounts, api, getVMObject, blockNumber) {
constructor (vmaccounts, api, getVMObject, blocks: Buffer[] = []) {
this.event = new EventManager()
this.logsManager = new LogsManager()
// has a default for now for backwards compatibility
this.getVMObject = getVMObject
this.commonContext = this.getVMObject().common
this.blockNumber = blockNumber || 0
this.blockNumber = Array.isArray(blocks) ? blocks.length : 0 // TODO: this should be set to the fetched block number count
this.pendingTxs = {}
this.vmaccounts = vmaccounts
this.queusTxs = []
this.blocks = []
/*
txHash is generated using the nonce,
in order to have unique transaction hash, we need to keep using different nonce (in case of a call)
@ -51,7 +50,15 @@ export class TxRunnerVM {
this.nextNonceForCall = 0
const vm = this.getVMObject().vm
this.blockParentHash = vm.blockchain.genesisBlock.hash()
if (Array.isArray(blocks) && (blocks || []).length > 0) {
const lastBlock = Block.fromRLPSerializedBlock(blocks[blocks.length - 1], { common: this.commonContext })
this.blockParentHash = lastBlock.hash()
this.blocks = blocks
} else {
this.blockParentHash = vm.blockchain.genesisBlock.hash()
this.blocks = [vm.blockchain.genesisBlock.serialize()]
}
}
execute (args: InternalTransaction, confirmationCb, gasEstimationForceSend, promptCb, callback: VMExecutionCallBack) {
@ -106,8 +113,7 @@ export class TxRunnerVM {
const coinbases = ['0x0e9281e9c6a0808672eaba6bd1220e144c9bb07a', '0x8945a1288dc78a6d8952a92c77aee6730b414778', '0x94d76e24f818426ae84aa404140e8d5f60e10e7e']
const difficulties = [69762765929000, 70762765929000, 71762765929000]
const difficulty = this.commonContext.consensusType() === ConsensusType.ProofOfStake ? 0 : difficulties[this.blockNumber % difficulties.length]
const blocknumber = this.blockNumber + 1
const blocknumber = this.blocks.length
const block = Block.fromBlockData({
header: {
timestamp: new Date().getTime() / 1000 | 0,
@ -122,10 +128,13 @@ export class TxRunnerVM {
}, { common: this.commonContext, hardforkByBlockNumber: false, hardforkByTTD: undefined })
if (!useCall) {
this.blockNumber = this.blockNumber + 1
this.blockNumber = blocknumber
this.blockParentHash = block.hash()
this.runBlockInVm(tx, block, (err, result) => {
if (!err) this.getVMObject().vm.blockchain.putBlock(block)
if (!err) {
this.getVMObject().vm.blockchain.putBlock(block)
this.blocks.push(block.serialize())
}
callback(err, result)
})
} else {

@ -1,6 +1,6 @@
{
"name": "@remix-project/remix-simulator",
"version": "0.2.42",
"version": "0.2.44",
"description": "Ethereum IDE and tools for the web",
"contributors": [
{
@ -22,7 +22,7 @@
"@ethereumjs/tx": "^4.1.1",
"@ethereumjs/util": "^8.0.5",
"@ethereumjs/vm": "^6.4.1",
"@remix-project/remix-lib": "^0.5.49",
"@remix-project/remix-lib": "^0.5.51",
"ansi-gray": "^0.1.1",
"async": "^3.1.0",
"body-parser": "^1.18.2",
@ -70,6 +70,6 @@
},
"homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-simulator#readme",
"typings": "src/index.d.ts",
"gitHead": "817089ab1f206f5e195756a4051afb812ec26901",
"gitHead": "751c821c0264dec832ee8739800e32b79b09c00a",
"types": "./src/index.d.ts"
}

@ -4,7 +4,7 @@ import { processTx } from './txProcess'
import { execution } from '@remix-project/remix-lib'
import { ethers } from 'ethers'
import { VMexecutionResult } from '@remix-project/remix-lib'
import { RunTxResult } from '@ethereumjs/vm'
import { VMContext } from '../vm-context'
import { Log, EvmError } from '@ethereumjs/evm'
const TxRunnerVM = execution.TxRunnerVM
const TxRunner = execution.TxRunner
@ -19,7 +19,7 @@ export type VMExecResult = {
}
export class Transactions {
vmContext
vmContext: VMContext
accounts
tags
txRunnerVMInstance
@ -32,7 +32,7 @@ export class Transactions {
this.tags = {}
}
init (accounts, blockNumber) {
init (accounts, blocksData: Buffer[]) {
this.accounts = accounts
const api = {
logMessage: (msg) => {
@ -55,11 +55,11 @@ export class Transactions {
}
}
this.txRunnerVMInstance = new TxRunnerVM(accounts, api, _ => this.vmContext.vmObject(), blockNumber)
this.txRunnerVMInstance = new TxRunnerVM(accounts, api, _ => this.vmContext.vmObject(), blocksData)
this.txRunnerInstance = new TxRunner(this.txRunnerVMInstance, {})
this.txRunnerInstance.vmaccounts = accounts
}
methods () {
return {
eth_sendTransaction: this.eth_sendTransaction.bind(this),
@ -74,7 +74,9 @@ export class Transactions {
eth_getExecutionResultFromSimulator: this.eth_getExecutionResultFromSimulator.bind(this),
eth_getHHLogsForTx: this.eth_getHHLogsForTx.bind(this),
eth_getHashFromTagBySimulator: this.eth_getHashFromTagBySimulator.bind(this),
eth_registerCallId: this.eth_registerCallId.bind(this)
eth_registerCallId: this.eth_registerCallId.bind(this),
eth_getStateDb: this.eth_getStateDb.bind(this),
eth_getBlocksData: this.eth_getBlocksData.bind(this)
}
}
@ -198,6 +200,17 @@ export class Transactions {
cb()
}
eth_getStateDb (_, cb) {
cb(null, this.vmContext.currentVm.stateManager.getDb())
}
eth_getBlocksData (_, cb) {
cb(null, {
blocks: this.txRunnerVMInstance.blocks,
latestBlockNumber: this.txRunnerVMInstance.blockNumber
})
}
eth_call (payload, cb) {
// from might be lowercased address (web3)
if (payload.params && payload.params.length > 0 && payload.params[0].from) {

@ -11,6 +11,7 @@ import { Transactions } from './methods/transactions'
import { Debug } from './methods/debug'
import { VMContext } from './vm-context'
import { Web3PluginBase } from 'web3'
import { Block } from '@ethereumjs/block'
export interface JSONRPCRequestPayload {
params: any[];
@ -27,8 +28,20 @@ export interface JSONRPCResponsePayload {
export type JSONRPCResponseCallback = (err: Error, result?: JSONRPCResponsePayload) => void
export type State = Record<string, string>
export type ProviderOptions = {
fork?: string,
nodeUrl?: string,
blockNumber?: number | 'latest',
stateDb?: State,
logDetails?: boolean
blocks?: string[],
coinbase?: string
}
export class Provider {
options: Record<string, string | number>
options: ProviderOptions
vmContext
Accounts
Transactions
@ -37,10 +50,10 @@ export class Provider {
initialized: boolean
pendingRequests: Array<any>
constructor (options: Record<string, string | number> = {}) {
constructor (options: ProviderOptions = {} as ProviderOptions) {
this.options = options
this.connected = true
this.vmContext = new VMContext(options['fork'] as string, options['nodeUrl'] as string, options['blockNumber'] as (number | 'latest'))
this.vmContext = new VMContext(options['fork'], options['nodeUrl'], options['blockNumber'], options['stateDb'], options['blocks'])
this.Accounts = new Web3Accounts(this.vmContext)
this.Transactions = new Transactions(this.vmContext)
@ -60,7 +73,7 @@ export class Provider {
this.pendingRequests = []
await this.vmContext.init()
await this.Accounts.resetAccounts()
this.Transactions.init(this.Accounts.accounts, this.vmContext.blockNumber)
this.Transactions.init(this.Accounts.accounts, this.vmContext.serializedBlocks)
this.initialized = true
if (this.pendingRequests.length > 0) {
this.pendingRequests.map((req) => {
@ -168,4 +181,18 @@ class Web3TestPlugin extends Web3PluginBase {
params: [id]
})
}
public getStateDb() {
return this.requestManager.send({
method: 'eth_getStateDb',
params: []
})
}
public getBlocksData() {
return this.requestManager.send({
method: 'eth_getBlocksData',
params: []
})
}
}

@ -2,7 +2,7 @@
'use strict'
import { Cache } from '@ethereumjs/statemanager/dist/cache'
import { hash } from '@remix-project/remix-lib'
import { bufferToHex, Account, toBuffer, bufferToBigInt} from '@ethereumjs/util'
import { bufferToHex, Account, toBuffer, bufferToBigInt, bigIntToHex } from '@ethereumjs/util'
import { keccak256 } from 'ethereum-cryptography/keccak'
import type { Address } from '@ethereumjs/util'
import { decode } from 'rlp'
@ -13,7 +13,7 @@ import { VmProxy } from './VmProxy'
import { VM } from '@ethereumjs/vm'
import type { BigIntLike } from '@ethereumjs/util'
import { Common, ConsensusType } from '@ethereumjs/common'
import { Trie } from '@ethereumjs/trie'
import { Trie, MapDB } from '@ethereumjs/trie'
import { DefaultStateManager, StateManager, EthersStateManager, EthersStateManagerOpts } from '@ethereumjs/statemanager'
import { StorageDump } from '@ethereumjs/statemanager/dist/interface'
import { EVM } from '@ethereumjs/evm'
@ -21,7 +21,7 @@ import { EEI } from '@ethereumjs/vm'
import { Blockchain } from '@ethereumjs/blockchain'
import { Block } from '@ethereumjs/block'
import { Transaction } from '@ethereumjs/tx'
import { bigIntToHex } from '@ethereumjs/util'
import { State } from './provider'
/**
* Options for constructing a {@link StateManager}.
@ -50,6 +50,11 @@ class StateManagerCommonStorageDump extends DefaultStateManager {
this.keyHashes = {}
}
getDb () {
// @ts-ignore
return this._trie.database().db
}
putContractStorage (address, key, value) {
this.keyHashes[hash.keccak(key).toString('hex')] = bufferToHex(key)
return super.putContractStorage(address, key, value)
@ -100,7 +105,6 @@ export interface CustomEthersStateManagerOpts {
class CustomEthersStateManager extends StateManagerCommonStorageDump {
private provider: ethers.providers.StaticJsonRpcProvider | ethers.providers.JsonRpcProvider
private blockTag: string
constructor(opts: CustomEthersStateManagerOpts) {
super(opts)
if (typeof opts.provider === 'string') {
@ -258,7 +262,7 @@ class CustomEthersStateManager extends StateManagerCommonStorageDump {
export type CurrentVm = {
vm: VM,
web3vm: VmProxy,
stateManager: StateManager,
stateManager: StateManagerCommonStorageDump,
common: Common
}
@ -298,12 +302,16 @@ export class VMContext {
exeResults: Record<string, Transaction>
nodeUrl: string
blockNumber: number | 'latest'
stateDb: State
rawBlocks: string[]
serializedBlocks: Buffer[]
constructor (fork?: string, nodeUrl?: string, blockNumber?: number | 'latest') {
constructor (fork?: string, nodeUrl?: string, blockNumber?: number | 'latest', stateDb?: State, blocksData?: string[]) {
this.blockGasLimitDefault = 4300000
this.blockGasLimit = this.blockGasLimitDefault
this.currentFork = fork || 'merge'
this.nodeUrl = nodeUrl
this.stateDb = stateDb
this.blockNumber = blockNumber
this.blocks = {}
this.latestBlockNumber = "0x0"
@ -311,6 +319,8 @@ export class VMContext {
this.txByHash = {}
this.exeResults = {}
this.logsManager = new LogsManager()
this.rawBlocks = blocksData
this.serializedBlocks = []
}
async init () {
@ -318,7 +328,7 @@ export class VMContext {
}
async createVm (hardfork) {
let stateManager: StateManager
let stateManager: StateManagerCommonStorageDump
if (this.nodeUrl) {
let block = this.blockNumber
if (this.blockNumber === 'latest') {
@ -335,15 +345,25 @@ export class VMContext {
blockTag: '0x' + this.blockNumber.toString(16)
})
}
} else{
const db = this.stateDb ? new Map(Object.entries(this.stateDb).map(([k, v]) => [k, toBuffer(v)])) : new Map()
const mapDb = new MapDB(db)
const trie = await Trie.create({ useKeyHashing: true, db: mapDb, useRootPersistence: true })
} else
stateManager = new StateManagerCommonStorageDump()
stateManager = new StateManagerCommonStorageDump({ trie })
}
const consensusType = hardfork === 'berlin' || hardfork === 'london' ? ConsensusType.ProofOfWork : ConsensusType.ProofOfStake
const difficulty = consensusType === ConsensusType.ProofOfStake ? 0 : 69762765929000
const common = new VMCommon({ chain: 'mainnet', hardfork })
const genesisBlock: Block = Block.fromBlockData({
const blocks = (this.rawBlocks || []).map(block => {
const serializedBlock = toBuffer(block)
this.serializedBlocks.push(serializedBlock)
return Block.fromRLPSerializedBlock(serializedBlock, { common })
})
const genesisBlock: Block = blocks.length > 0 && (blocks[0] || {}).isGenesis ? blocks[0] : Block.fromBlockData({
header: {
timestamp: (new Date().getTime() / 1000 | 0),
number: 0,
@ -352,7 +372,6 @@ export class VMContext {
gasLimit: 8000000
}
}, { common, hardforkByBlockNumber: false, hardforkByTTD: undefined })
const blockchain = await Blockchain.create({ common, validateBlocks: false, validateConsensus: false, genesisBlock })
const eei = new EEI(stateManager, common, blockchain)
const evm = new EVM({ common, eei, allowUnlimitedContractSize: true })
@ -365,13 +384,17 @@ export class VMContext {
blockchain,
evm
})
// VmProxy and VMContext are very intricated.
// VmProxy is used to track the EVM execution (to listen on opcode execution, in order for instance to generate the VM trace)
const web3vm = new VmProxy(this)
web3vm.setVM(vm)
this.addBlock(genesisBlock, true)
return { vm, web3vm, stateManager, common }
if (blocks.length > 0) blocks.splice(0, 1)
blocks.forEach(block => {
blockchain.putBlock(block)
this.addBlock(block, false, false, web3vm)
})
return { vm, web3vm, stateManager, common, blocks }
}
getCurrentFork () {
@ -390,7 +413,7 @@ export class VMContext {
return this.currentVm
}
addBlock (block: Block, genesis?: boolean, isCall?: boolean) {
addBlock (block: Block, genesis?: boolean, isCall?: boolean, web3vm?: VmProxy) {
let blockNumber = bigIntToHex(block.header.number)
if (blockNumber === '0x') {
blockNumber = '0x0'
@ -400,7 +423,8 @@ export class VMContext {
this.blocks[blockNumber] = block
this.latestBlockNumber = blockNumber
if (!isCall && !genesis) this.logsManager.checkBlock(blockNumber, block, this.web3())
if (!isCall && !genesis && web3vm) this.logsManager.checkBlock(blockNumber, block, web3vm)
if (!isCall && !genesis && !web3vm) this.logsManager.checkBlock(blockNumber, block, this.web3())
}
trackTx (txHash, block, tx) {

@ -1,6 +1,6 @@
{
"name": "@remix-project/remix-solidity",
"version": "0.5.28",
"version": "0.5.30",
"description": "Tool to load and run Solidity compiler",
"main": "src/index.js",
"types": "src/index.d.ts",
@ -19,7 +19,7 @@
"@ethereumjs/tx": "^4.1.1",
"@ethereumjs/util": "^8.0.5",
"@ethereumjs/vm": "^6.4.1",
"@remix-project/remix-lib": "^0.5.49",
"@remix-project/remix-lib": "^0.5.51",
"async": "^2.6.2",
"eslint-scope": "^5.0.0",
"ethers": "^5.4.2",
@ -57,5 +57,5 @@
},
"homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-solidity#readme",
"typings": "src/index.d.ts",
"gitHead": "817089ab1f206f5e195756a4051afb812ec26901"
"gitHead": "751c821c0264dec832ee8739800e32b79b09c00a"
}

@ -72,7 +72,7 @@ export interface CompilerInput {
// Addresses of the libraries. If not all libraries are given here,
// it can result in unlinked objects whose output data is different.
libraries?: {
// The top level key is the the name of the source file where the library is used.
// The top level key is the name of the source file where the library is used.
// If remappings are used, this source file should match the global path
// after remappings were applied.
// If this key is an empty string, that refers to a global level.

@ -1,6 +1,6 @@
{
"name": "@remix-project/remix-tests",
"version": "0.2.42",
"version": "0.2.44",
"description": "Tool to test Solidity smart contracts",
"main": "src/index.js",
"types": "./src/index.d.ts",
@ -41,9 +41,9 @@
"@ethereumjs/tx": "^4.1.1",
"@ethereumjs/util": "^8.0.5",
"@ethereumjs/vm": "^6.4.1",
"@remix-project/remix-lib": "^0.5.49",
"@remix-project/remix-simulator": "^0.2.42",
"@remix-project/remix-solidity": "^0.5.28",
"@remix-project/remix-lib": "^0.5.51",
"@remix-project/remix-simulator": "^0.2.44",
"@remix-project/remix-solidity": "^0.5.30",
"@remix-project/remix-url-resolver": "^0.0.42",
"ansi-gray": "^0.1.1",
"async": "^2.6.0",
@ -78,5 +78,5 @@
"typescript": "^3.3.1"
},
"typings": "src/index.d.ts",
"gitHead": "817089ab1f206f5e195756a4051afb812ec26901"
"gitHead": "751c821c0264dec832ee8739800e32b79b09c00a"
}

@ -886,6 +886,9 @@ export const EditorUI = (props: EditorUIProps) => {
monacoRef.current.languages.register({ id: 'remix-move' })
monacoRef.current.languages.register({ id: 'remix-circom' })
// Allow JSON schema requests
monacoRef.current.languages.json.jsonDefaults.setDiagnosticsOptions({enableSchemaRequest: true})
// Register a tokens provider for the language
monacoRef.current.languages.setMonarchTokensProvider('remix-solidity', solidityTokensProvider as any)
monacoRef.current.languages.setLanguageConfiguration('remix-solidity', solidityLanguageConfig as any)

@ -104,7 +104,5 @@ iframe {
height: 2rem !important;
}
.terminal-wrap.minimized.desktop {
height: 4.5rem !important;
}

@ -75,8 +75,8 @@ export const addSavedInstance = (dispatch: React.Dispatch<any>, instance: { cont
dispatch(addNewSavedInstance(instance))
}
export const removeInstance = (dispatch: React.Dispatch<any>, index: number, isSavedContract: boolean) => {
dispatch(removeExistingInstance(index, isSavedContract))
export const removeInstance = (dispatch: React.Dispatch<any>, index: number, isSavedContract: boolean, shouldDelete: boolean) => {
dispatch(removeExistingInstance(index, isSavedContract, shouldDelete))
}
export const clearInstances = (dispatch: React.Dispatch<any>) => {

@ -250,7 +250,8 @@ export const loadAddress = (plugin: RunTab, dispatch: React.Dispatch<any>, contr
return dispatch(displayNotification('Alert', error, 'OK', null))
}
if (loadType === 'abi') {
return addInstance(dispatch, { abi, address, name: '<at address>' })
const contractData = { name: '<at address>', abi, contract: {file: plugin.REACT_API.contracts.currentFile}} as ContractData
return addInstance(dispatch, { contractData, address, name: '<at address>' })
} else if (loadType === 'instance') {
if (!contract) return dispatch(displayPopUp('No compiled contracts found.'))
const currentFile = plugin.REACT_API.contracts.currentFile

@ -83,8 +83,8 @@ export const setupEvents = (plugin: RunTab, dispatch: React.Dispatch<any>) => {
dispatch(clearAllSavedInstances())
})
plugin.on('udapp', 'addInstanceReducer', (address, abi, name) => {
addInstance(dispatch, { abi, address, name })
plugin.on('udapp', 'addInstanceReducer', (address, abi, name, contractData?) => {
addInstance(dispatch, { contractData, abi, address, name })
})
plugin.on('udapp', 'addSavedInstanceReducer', (address, abi, name, savedOn, filePath) => {

@ -48,7 +48,7 @@ export const setGasPriceStatus = (status: boolean) => updateGasPriceStatus(dispa
export const setMaxFee = (fee: string) => updateMaxFee(dispatch, fee)
export const setMaxPriorityFee = (fee: string) => updateMaxPriorityFee(dispatch, fee)
export const removeInstances = () => clearInstances(dispatch)
export const removeSingleInstance = (index: number, isSavedContract: boolean) => removeInstance(dispatch, index, isSavedContract)
export const removeSingleInstance = (index: number, isSavedContract: boolean, shouldDelete: boolean) => removeInstance(dispatch, index, isSavedContract, shouldDelete)
export const getExecutionContext = () => getContext(plugin)
export const executeTransactions = (instanceIndex: number, isSavedContract: boolean, lookupOnly: boolean, funcABI: FuncABI, inputsValues: string, contractName: string, contractABI, contract, address, logMsg:string, mainnetPrompt: MainnetPrompt, gasEstimationPrompt: (msg: string) => JSX.Element, passphrasePrompt: (msg: string) => JSX.Element, funcIndex?: number) => runTransactions(plugin, dispatch, instanceIndex, isSavedContract, lookupOnly, funcABI, inputsValues, contractName, contractABI, contract, address, logMsg, mainnetPrompt, gasEstimationPrompt, passphrasePrompt, funcIndex)
export const loadFromAddress = (contract: ContractData, address: string) => loadAddress(plugin, dispatch, contract, address)

@ -237,12 +237,13 @@ export const addNewSavedInstance = (instance: { contractData?: ContractData, add
}
}
export const removeExistingInstance = (index: number, isSavedContract: boolean) => {
export const removeExistingInstance = (index: number, isSavedContract: boolean, shouldDelete: boolean) => {
return {
type: REMOVE_INSTANCE,
payload: {
index,
isSavedContract
isSavedContract,
shouldDelete
}
}
}

@ -14,7 +14,7 @@ export function EnvironmentUI(props: EnvironmentProps) {
const currentProvider = props.providers.providerList.find((exEnv) => exEnv.value === props.selectedEnv)
const bridges = {
'injected-optimism-provider': 'https://www.optimism.io/apps/bridges',
'injected-optimism-provider': 'https://app.optimism.io/bridge/deposit',
'injected-arbitrum-one-provider': 'https://bridge.arbitrum.io/'
}

@ -7,27 +7,57 @@ import { UniversalDappUI } from './universalDappUI'
export function InstanceContainerUI(props: InstanceContainerProps) {
const { instanceList } = props.instances
const env = useRef()
const enableSave = useRef(false)
const chainId = useRef()
useEffect(() => {
const fetchSavedContracts = async () => {
env.current = await props.plugin.call('blockchain', 'getProvider')
if(env.current && env.current === 'injected') {
if (props.plugin.REACT_API.selectExEnv && props.plugin.REACT_API.selectExEnv.startsWith('vm-')) enableSave.current = false
else enableSave.current = true
if (enableSave.current) {
const { network } = await props.plugin.call('blockchain', 'getCurrentNetworkStatus')
chainId.current = network.id
// Move contract saved in localstorage to Remix FE
const allSavedContracts = localStorage.getItem('savedContracts')
if (allSavedContracts) {
await props.plugin.call('udapp', 'clearAllSavedInstances')
const savedContracts = JSON.parse(allSavedContracts)
const { network } = await props.plugin.call('blockchain', 'getCurrentNetworkStatus')
if (savedContracts && savedContracts[network.id]) {
const instances = savedContracts[network.id]
for (const inst of instances)
if (inst) await props.plugin.call('udapp', 'addSavedInstance', inst.address, inst.abi || inst.contractData.abi, inst.name, inst.savedOn, inst.filePath)
for (const networkId in savedContracts) {
if (savedContracts[networkId].length > 0) {
for (const contractDetails of savedContracts[networkId]) {
const objToSave = {
name: contractDetails.name,
address: contractDetails.address,
abi: contractDetails.abi || contractDetails.contractData.abi,
filePath: contractDetails.filePath,
pinnedAt: contractDetails.savedOn
}
await props.plugin.call('fileManager', 'writeFile', `.deploys/pinned-contracts/${networkId}/${contractDetails.address}.json`, JSON.stringify(objToSave, null, 2))
}
}
}
localStorage.removeItem('savedContracts')
}
// Clear existing saved instance state
await props.plugin.call('udapp', 'clearAllSavedInstances')
// Load contracts from FE
const isPinnedAvailable = await props.plugin.call('fileManager', 'exists', `.deploys/pinned-contracts/${chainId.current}`)
if (isPinnedAvailable) {
try {
const list = await props.plugin.call('fileManager', 'readdir', `.deploys/pinned-contracts/${chainId.current}`)
const filePaths = Object.keys(list)
for (const file of filePaths) {
const pinnedContract = await props.plugin.call('fileManager', 'readFile', file)
const pinnedContractObj = JSON.parse(pinnedContract)
if (pinnedContractObj) await props.plugin.call('udapp', 'addSavedInstance', pinnedContractObj.address, pinnedContractObj.abi, pinnedContractObj.name, pinnedContractObj.pinnedAt, pinnedContractObj.filePath)
}
} catch(err) {
console.log(err)
}
}
}
}
fetchSavedContracts()
}, [props.plugin.REACT_API.networkName])
}, [props.plugin.REACT_API.selectExEnv, props.plugin.REACT_API.networkName])
const clearInstance = () => {
props.clearInstances()
@ -35,15 +65,16 @@ export function InstanceContainerUI(props: InstanceContainerProps) {
return (
<div className="udapp_instanceContainer mt-3 border-0 list-group-item">
{ env.current && env.current === 'injected' ? (
<div className="d-flex justify-content-between align-items-center pl-2 mb-2">
<CustomTooltip placement="top-start" tooltipClasses="text-nowrap" tooltipId="deployAndRunClearInstancesTooltip" tooltipText={<FormattedMessage id="udapp.tooltipText6" />}>
{ enableSave.current ? (
<div className="d-flex justify-content-between align-items-center pl-2">
<CustomTooltip placement="top-start" tooltipClasses="text-nowrap" tooltipId="deployAndRunPinnedContractsTooltip" tooltipText={<FormattedMessage id="udapp.tooltipTextPinnedContracts" />}>
<label className="udapp_deployedContracts">
<FormattedMessage id="udapp.savedContracts" />
<FormattedMessage id="udapp.savedContracts" />
<span style={{fontSize: '0.75rem'}}> (chain id: {chainId.current})</span>
</label>
</CustomTooltip>
</div>) : null }
{ env.current && env.current === 'injected' ? (
{ enableSave.current ? (
props.savedInstances.instanceList.length > 0 ? (
<div>
{' '}
@ -68,13 +99,13 @@ export function InstanceContainerUI(props: InstanceContainerProps) {
})}
</div>
) : (
<span className="mx-2 mt-3 alert alert-warning" data-id="NoSavedInstanceText" role="alert">
<span className="mx-2 mt-2 text-dark" data-id="NoSavedInstanceText">
<FormattedMessage id="udapp.NoSavedInstanceText" />
</span>
)
) : null }
<div className="d-flex justify-content-between align-items-center pl-2 mb-2 mt-3">
<div className="d-flex justify-content-between align-items-center pl-2 mb-2 mt-2">
<CustomTooltip placement="top-start" tooltipClasses="text-nowrap" tooltipId="deployAndRunClearInstancesTooltip" tooltipText={<FormattedMessage id="udapp.tooltipText6" />}>
<label className="udapp_deployedContracts">
<FormattedMessage id="udapp.deployedContracts" />

@ -1,5 +1,5 @@
// eslint-disable-next-line no-use-before-define
import React, {useEffect, useState, useRef} from 'react'
import React, {useEffect, useState} from 'react'
import {FormattedMessage, useIntl} from 'react-intl'
import {UdappProps} from '../types'
import {FuncABI} from '@remix-project/core-plugin'
@ -10,6 +10,7 @@ import {ContractGUI} from './contractGUI'
import {TreeView, TreeViewItem} from '@remix-ui/tree-view'
import {BN} from 'bn.js'
import {CustomTooltip, is0XPrefixed, isHexadecimal, isNumeric, shortenAddress} from '@remix-ui/helper'
const _paq = (window._paq = window._paq || [])
const txHelper = remixLib.execution.txHelper
@ -23,7 +24,6 @@ export function UniversalDappUI(props: UdappProps) {
const [calldataValue, setCalldataValue] = useState<string>('')
const [evmBC, setEvmBC] = useState(null)
const [instanceBalance, setInstanceBalance] = useState(0)
const env = useRef()
const getVersion = () => window.location.href.split('=')[5].split('+')[0].split('-')[1]
@ -58,13 +58,6 @@ export function UniversalDappUI(props: UdappProps) {
}
}, [props.instance.balance])
useEffect(() => {
const getEnv = async () => {
env.current = await props.plugin.call('blockchain', 'getProvider')
}
getEnv()
}, [])
const sendData = () => {
setLlIError('')
const fallback = txHelper.getFallbackInterface(contractABI)
@ -119,41 +112,41 @@ export function UniversalDappUI(props: UdappProps) {
setToggleExpander(!toggleExpander)
}
const unsavePinnedContract = async () => {
const {network} = await props.plugin.call('blockchain', 'getCurrentNetworkStatus')
await props.plugin.call('fileManager', 'remove', `.deploys/pinned-contracts/${network.id}/${props.instance.address}.json`)
}
const remove = async() => {
if (props.isSavedContract) {
const {network} = await props.plugin.call('blockchain', 'getCurrentNetworkStatus')
const savedContracts = localStorage.getItem('savedContracts')
const savedContractsJson = JSON.parse(savedContracts)
const instanceIndex = savedContractsJson[network.id].findIndex(instance => instance && instance.address === props.instance.address)
delete savedContractsJson[network.id][instanceIndex]
savedContractsJson[network.id] = savedContractsJson[network.id].filter(Boolean)
localStorage.setItem('savedContracts', JSON.stringify(savedContractsJson))
await unsavePinnedContract()
_paq.push(['trackEvent', 'udapp', 'pinContracts', 'unpinned'])
}
props.removeInstance(props.index, props.isSavedContract)
props.removeInstance(props.index, props.isSavedContract, false)
}
const saveContract = async() => {
const deletePinnedContract = async() => {
await unsavePinnedContract()
_paq.push(['trackEvent', 'udapp', 'pinContracts', 'deletePinned'])
props.removeInstance(props.index, props.isSavedContract, true)
}
const pinContract = async() => {
const workspace = await props.plugin.call('filePanel', 'getCurrentWorkspace')
const {network} = await props.plugin.call('blockchain', 'getCurrentNetworkStatus')
const savedContracts = localStorage.getItem('savedContracts')
let objToSave
if (!savedContracts) {
objToSave = {}
objToSave[network.id] = []
} else {
objToSave = JSON.parse(savedContracts)
if (!objToSave[network.id]) {
objToSave[network.id] = []
}
const objToSave = {
name: props.instance.name,
address: props.instance.address,
abi: props.instance.abi || props.instance.contractData.abi,
filePath: props.instance.filePath || `${workspace.name}/${props.instance.contractData.contract.file}`,
pinnedAt: Date.now()
}
props.instance.savedOn = Date.now()
props.instance.filePath = props.instance.filePath || `${workspace.name}/${props.instance.contractData.contract.file}`
objToSave[network.id].push(props.instance)
localStorage.setItem('savedContracts', JSON.stringify(objToSave))
await props.plugin.call('fileManager', 'writeFile', `.deploys/pinned-contracts/${network.id}/${props.instance.address}.json`, JSON.stringify(objToSave, null, 2))
// Add contract to saved contracts list on UI
await props.plugin.call('udapp', 'addSavedInstance', props.instance.address, props.instance.abi || props.instance.contractData.abi, props.instance.name, props.instance.savedOn, props.instance.filePath)
await props.plugin.call('udapp', 'addSavedInstance', objToSave.address, objToSave.abi, objToSave.name, objToSave.pinnedAt, objToSave.filePath)
_paq.push(['trackEvent', 'udapp', 'pinContracts', `pinned at ${network.id}`])
// Remove contract from deployed contracts list on UI
props.removeInstance(props.index, false)
props.removeInstance(props.index, false, false)
}
const runTransaction = (lookupOnly, funcABI: FuncABI, valArr, inputsValues, funcIndex?: number) => {
@ -260,27 +253,44 @@ export function UniversalDappUI(props: UdappProps) {
data-shared="universalDappUiInstance"
>
<div className="udapp_title pb-0 alert alert-secondary">
<span data-id={`universalDappUiTitleExpander${props.index}`} className="btn udapp_titleExpander" onClick={toggleClass}>
<span data-id={`universalDappUiTitleExpander${props.index}`} className="btn udapp_titleExpander" onClick={toggleClass} style={{padding: "0.45rem"}}>
<i className={`fas ${toggleExpander ? 'fa-angle-right' : 'fa-angle-down'}`} aria-hidden="true"></i>
</span>
<div className="input-group udapp_nameNbuts">
<div className="udapp_titleText input-group-prepend">
<span className="input-group-text udapp_spanTitleText">
{ props.isSavedContract ? ( <CustomTooltip placement="top" tooltipClasses="text-nowrap" tooltipId="udapp_udappUnpinTooltip" tooltipText={props.isSavedContract ? `Contract: ${props.instance.name}, Address: ${address}, Pinned at: ${new Date(props.instance.savedOn).toLocaleString()}` : '' }>
<span className="input-group-text udapp_spanTitleText">
{props.instance.name} at {shortenAddress(address)}
</span>
</CustomTooltip>) : ( <span className="input-group-text udapp_spanTitleText">
{props.instance.name} at {shortenAddress(address)} ({props.context})
</span>
</span>) }
</div>
<div className="btn" style={{padding: '0.15rem'}}>
<CopyToClipboard tip={intl.formatMessage({id: 'udapp.copy'})} content={address} direction={'top'} />
</div>
{ !props.isSavedContract && env.current === 'injected' ? ( <div className="btn" style={{padding: '0.15rem', marginLeft: '-0.5rem'}}>
<CustomTooltip placement="top" tooltipClasses="text-nowrap" tooltipId="udapp_udappSaveTooltip" tooltipText={<FormattedMessage id="udapp.tooltipText14" />}>
<i className="far fa-save p-2" aria-hidden="true" data-id="universalDappUiUdappSave" onClick={saveContract}></i>
</CustomTooltip>
</div> ) : null }
{ !(props.plugin.REACT_API.selectExEnv && props.plugin.REACT_API.selectExEnv.startsWith('vm-')) ?
props.isSavedContract ? ( <div className="btn" style={{padding: '0.15rem', marginLeft: '-0.5rem'}}>
<CustomTooltip placement="top" tooltipClasses="text-nowrap" tooltipId="udapp_udappUnpinTooltip" tooltipText={<FormattedMessage id="udapp.tooltipTextUnpin" />}>
<i className="fas fa-thumbtack p-2 text-success" aria-hidden="true" data-id="universalDappUiUdappUnpin" onClick={remove}></i>
</CustomTooltip>
</div> ) : ( <div className="btn" style={{padding: '0.15rem', marginLeft: '-0.5rem'}}>
<CustomTooltip placement="top" tooltipClasses="text-nowrap" tooltipId="udapp_udappPinTooltip" tooltipText={<FormattedMessage id="udapp.tooltipTextPin" />}>
<i className="far fa-thumbtack p-2" aria-hidden="true" data-id="universalDappUiUdappPin" onClick={pinContract}></i>
</CustomTooltip>
</div> )
: null}
</div>
<CustomTooltip placement="top" tooltipClasses="text-nowrap" tooltipId="udapp_udappCloseTooltip" tooltipText={ !props.isSavedContract ? (<FormattedMessage id="udapp.tooltipText7" />) : (<FormattedMessage id="udapp.tooltipTextUnsave" />)}>
<i className="udapp_closeIcon m-1 fas fa-times align-self-center" aria-hidden="true" data-id="universalDappUiUdappClose" onClick={remove}></i>
</CustomTooltip>
{ props.isSavedContract ? ( <div className="btn" style={{padding: '0.15rem', marginLeft: '-0.5rem'}}>
<CustomTooltip placement="top" tooltipClasses="text-nowrap" tooltipId="udapp_udappDeleteTooltip" tooltipText={<FormattedMessage id="udapp.tooltipTextDelete" />}>
<i className="far fa-trash p-2" aria-hidden="true" data-id="universalDappUiUdappDelete" onClick={deletePinnedContract}></i>
</CustomTooltip>
</div> ) : ( <div className="btn" style={{padding: '0.15rem', marginLeft: '-0.5rem'}}>
<CustomTooltip placement="top" tooltipClasses="text-nowrap" tooltipId="udapp_udappCloseTooltip" tooltipText={<FormattedMessage id="udapp.tooltipTextRemove" />}>
<i className="fas fa-times p-2" aria-hidden="true" data-id="universalDappUiUdappClose" onClick={remove}></i>
</CustomTooltip>
</div> )
}
</div>
<div className="udapp_cActionsWrapper" data-id="universalDappUiContractActionWrapper">
<div className="udapp_contractActionsContainer">
@ -292,7 +302,7 @@ export function UniversalDappUI(props: UdappProps) {
{ props.isSavedContract && props.instance.savedOn ? (
<div className="d-flex" data-id="instanceContractSavedOn">
<label>
<b><FormattedMessage id="udapp.savedOn" />:</b> {(new Date(props.instance.savedOn)).toUTCString()}
<b><FormattedMessage id="udapp.savedOn" />:</b> {(new Date(props.instance.savedOn)).toLocaleString()}
</label>
</div>
) : null }

@ -515,28 +515,34 @@ export const runTabReducer = (state: RunTabState = runTabInitialState, action: A
}
case REMOVE_INSTANCE: {
const payload: { index: number, isSavedContract: boolean } = action.payload
const payload: { index: number, isSavedContract: boolean, shouldDelete: boolean } = action.payload
if (payload.isSavedContract)
return {
if (payload.isSavedContract) {
if (payload.shouldDelete) return {
...state,
savedInstances: {
...state.savedInstances,
instanceList: state.savedInstances.instanceList.filter((_, index) => index !== payload.index)
},
instances: {
...state.instances,
instanceList: [...state.instances.instanceList, state.savedInstances.instanceList[payload.index]]
}
}
else
return {
else return {
...state,
savedInstances: {
...state.savedInstances,
instanceList: state.savedInstances.instanceList.filter((_, index) => index !== payload.index)
},
instances: {
...state.instances,
instanceList: state.instances.instanceList.filter((_, index) => index !== payload.index)
instanceList: [...state.instances.instanceList, state.savedInstances.instanceList[payload.index]]
}
}
} else return {
...state,
instances: {
...state.instances,
instanceList: state.instances.instanceList.filter((_, index) => index !== payload.index)
}
}
}
case CLEAR_INSTANCES: {

@ -321,7 +321,7 @@ export interface InstanceContainerProps {
error: string
},
clearInstances: () => void,
removeInstance: (index: number, isSavedContract:boolean) => void,
removeInstance: (index: number, isSavedContract:boolean, shouldDelete: boolean) => void,
getContext: () => 'memory' | 'blockchain',
runTransactions: (
instanceIndex: number,
@ -428,7 +428,7 @@ export interface UdappProps {
},
context: 'memory' | 'blockchain',
isSavedContract?: boolean
removeInstance: (index: number, isSavedContract: boolean) => void,
removeInstance: (index: number, isSavedContract: boolean, shouldDelete: boolean) => void,
index: number,
gasEstimationPrompt: (msg: string) => JSX.Element,
passphrasePrompt: (message: string) => JSX.Element,

@ -3,6 +3,7 @@ export const textDark = 'text-dark'
export const gitAccessTokenLink = 'https://github.com/settings/tokens/new?scopes=gist,repo&description=Remix%20IDE%20Token'
export const etherscanTokenLink = 'https://etherscan.io/myapikey'
export const sindriAccessTokenLink = 'https://sindri.app'
export const labels = {
'gist': {
'link': gitAccessTokenLink,
@ -11,5 +12,9 @@ export const labels = {
'etherscan': {
'link': etherscanTokenLink,
'key': 'etherscan-access-token'
},
'sindri': {
'link': sindriAccessTokenLink,
'key': 'sindri-access-token'
}
}

@ -19,7 +19,8 @@ import {
saveIpfsSettingsToast,
useAutoCompletion,
useShowGasInEditor,
useDisplayErrors
useDisplayErrors,
saveEnvState
} from './settingsAction'
import {initialState, toastInitialState, toastReducer, settingReducer} from './settingsReducer'
import {Toaster} from '@remix-ui/toaster' // eslint-disable-line
@ -28,6 +29,7 @@ import {RemixUiLocaleModule, LocaleModule} from '@remix-ui/locale-module'
import {FormattedMessage, useIntl} from 'react-intl'
import {GithubSettings} from './github-settings'
import {EtherscanSettings} from './etherscan-settings'
import {SindriSettings} from './sindri-settings'
/* eslint-disable-next-line */
export interface RemixUiSettingsProps {
@ -69,6 +71,9 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
const useShowGas = props.config.get('settings/show-gas')
if (useShowGas === null || useShowGas === undefined) useShowGasInEditor(props.config, true, dispatch)
const enableSaveEnvState = props.config.get('settings/save-evm-state')
if (enableSaveEnvState === null || enableSaveEnvState === undefined) saveEnvState(props.config, true, dispatch)
}
useEffect(() => initValue(), [resetState, props.config])
useEffect(() => initValue(), [])
@ -177,6 +182,10 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
useDisplayErrors(props.config, event.target.checked, dispatch)
}
const onchangeSaveEnvState= (event) => {
saveEnvState(props.config, event.target.checked, dispatch)
}
const getTextClass = (key) => {
if (props.config.get(key)) {
return textDark
@ -194,6 +203,7 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
const isAutoCompleteChecked = props.config.get('settings/auto-completion') || false
const isShowGasInEditorChecked = props.config.get('settings/show-gas') || false
const displayErrorsChecked = props.config.get('settings/display-errors') || false
const isSaveEvmStateChecked = props.config.get('settings/save-evm-state') || false
return (
<div className="$border-top">
<div className="d-flex justify-content-end pr-4">
@ -310,6 +320,18 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
</a>
</label>
</div>
<div className="custom-control custom-checkbox mb-1">
<input onChange={onchangeSaveEnvState} id="settingsEnableSaveEnvState" data-id="settingsEnableSaveEnvState" type="checkbox" className="custom-control-input" checked={isSaveEvmStateChecked} />
<label
className={`form-check-label custom-control-label align-middle ${getTextClass('settings/save-evm-state')}`}
data-id="settingsEnableSaveEnvStateLabel"
htmlFor="settingsEnableSaveEnvState"
>
<span>
<FormattedMessage id="settings.enableSaveEnvState" />
</span>
</label>
</div>
</div>
</div>
)
@ -563,6 +585,15 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
}}
config={props.config}
/>
<SindriSettings
saveToken={(sindriToken: string) => {
saveTokenToast(props.config, dispatchToast, sindriToken, 'sindri-access-token')
}}
removeToken={() => {
removeTokenToast(props.config, dispatchToast, 'sindri-access-token')
}}
config={props.config}
/>
{swarmSettings()}
{ipfsSettings()}
<RemixUiThemeModule themeModule={props._deps.themeModule} />

@ -90,3 +90,8 @@ export const saveIpfsSettingsToast = (config, dispatch, ipfsURL, ipfsProtocol, i
config.set('settings/ipfs-project-secret', ipfsProjectSecret)
dispatch({ type: 'save', payload: { message: 'IPFS settings have been saved' } })
}
export const saveEnvState = (config, checked, dispatch) => {
config.set('settings/save-evm-state', checked)
dispatch({ type: 'save-evm-state', payload: { isChecked: checked, textClass: checked ? textDark : textSecondary } })
}

@ -56,7 +56,12 @@ export const initialState = {
name: 'copilot/suggest/temperature',
value: 0.5,
textClass: textSecondary
}
},
{
name: 'save-evm-state',
isChecked: false,
textClass: textSecondary
},
]
}
@ -173,6 +178,17 @@ export const settingReducer = (state, action) => {
return {
...state
}
case 'save-evm-state':
state.elementState.map(element => {
if (element.name === 'save-evm-state') {
element.isChecked = action.payload.isChecked
element.textClass = action.payload.textClass
}
})
return {
...state
}
default:
return initialState
}

@ -0,0 +1,77 @@
import {CopyToClipboard} from '@remix-ui/clipboard'
import {CustomTooltip} from '@remix-ui/helper'
import React, {useEffect, useState} from 'react'
import {FormattedMessage, useIntl} from 'react-intl'
import {SindriSettingsProps} from '../types'
import {sindriAccessTokenLink} from './constants'
export function SindriSettings(props: SindriSettingsProps) {
const [sindriToken, setSindriToken] = useState<string>('')
const intl = useIntl()
useEffect(() => {
if (props.config) {
const sindriToken = props.config.get('settings/sindri-access-token') || ''
setSindriToken(sindriToken)
}
}, [props.config])
const handleChangeTokenState = (event) => {
const token = event.target.value ? event.target.value.trim() : event.target.value
setSindriToken(token)
}
// api key settings
const saveSindriToken = () => {
props.saveToken(sindriToken)
}
const removeToken = () => {
setSindriToken('')
props.removeToken()
}
return (
<div className="border-top">
<div className="card-body pt-3 pb-2">
<h6 className="card-title">
<FormattedMessage id="settings.sindriAccessTokenTitle" />
</h6>
<p className="mb-1">
<FormattedMessage id="settings.sindriAccessTokenText" />
</p>
<p className="">
<FormattedMessage id="settings.sindriAccessTokenText2" />
</p>
<p className="mb-1">
<a className="text-primary" target="_blank" href={sindriAccessTokenLink}>
{sindriAccessTokenLink}
</a>
</p>
<div>
<label className="mb-0 pb-0">
<FormattedMessage id="settings.token" />:
</label>
<div className="input-group text-secondary mb-0 h6">
<input id="sindriaccesstoken" data-id="settingsTabSindriAccessToken" type="password" className="form-control" onChange={(e) => handleChangeTokenState(e)} value={sindriToken} />
<div className="input-group-append">
<CopyToClipboard tip={intl.formatMessage({id: 'settings.copy'})} content={sindriToken} data-id="copyToClipboardCopyIcon" className="far fa-copy ml-1 p-2 mt-1" direction={'top'} />
</div>
</div>
</div>
<div>
<div className="text-secondary mb-0 h6">
<div className="d-flex justify-content-end pt-2">
<input className="btn btn-sm btn-primary ml-2" id="savesindritoken" data-id="settingsTabSaveSindriToken" onClick={saveSindriToken} value={intl.formatMessage({id: 'settings.save'})} type="button"></input>
<CustomTooltip tooltipText={<FormattedMessage id="settings.deleteSindriCredentials" />} tooltipClasses="text-nowrap" tooltipId="removesindritokenTooltip" placement="top-start">
<button className="btn btn-sm btn-secondary ml-2" id="removesindritoken" data-id="settingsTabRemoveSindriToken" onClick={removeToken}>
<FormattedMessage id="settings.remove" />
</button>
</CustomTooltip>
</div>
</div>
</div>
</div>
</div>
)
}

@ -23,3 +23,16 @@ export interface EtherscanSettingsProps {
setUnpersistedProperty: (key: string, value: string) => void
}
}
export interface SindriSettingsProps {
saveToken: (sindriToken: string) => void,
removeToken: () => void,
config: {
exists: (key: string) => boolean,
get: (key: string) => string,
set: (key: string, content: string) => void,
clear: () => void,
getUnpersistedProperty: (key: string) => void,
setUnpersistedProperty: (key: string, value: string) => void
}
}

@ -28,7 +28,7 @@ export const CompilerApiMixin = (Base) => class extends Base {
onContentChanged: () => void
onFileClosed: (name: string) => void
statusChanged: (data: { key: string, title?: string, type?: string }) => void
setSolJsonBinData: (urls: iSolJsonBinData) => void
initCompilerApi () {
@ -336,7 +336,7 @@ export const CompilerApiMixin = (Base) => class extends Base {
await this.call('editor', 'addAnnotation', pos, file)
}
}
}
}
}
this.compiler.event.register('compilationFinished', this.data.eventHandlers.onCompilationFinished)
@ -360,6 +360,8 @@ export const CompilerApiMixin = (Base) => class extends Base {
else this.compileTabLogic.runCompiler(undefined)
} else if (this.currentFile && this.currentFile.endsWith('.circom')) {
await this.call('circuit-compiler', 'compile', this.currentFile)
} else if (this.currentFile && this.currentFile.endsWith('.vy')) {
await this.call('vyper', 'vyperCompileCustomAction', this.currentFile)
}
}
}
@ -370,7 +372,7 @@ export const CompilerApiMixin = (Base) => class extends Base {
return new Promise((resolve) => {
if (!data.contracts || (data.contracts && Object.keys(data.contracts).length === 0)) {
return resolve({
contractMap: {},
contractMap: {},
contractsDetails: {},
target: source.target
})
@ -386,7 +388,7 @@ export const CompilerApiMixin = (Base) => class extends Base {
)
})
return resolve({
contractMap,
contractMap,
contractsDetails,
target: source.target
})

@ -1 +1,3 @@
export * from './lib/remix-ui-terminal'
export * from './lib/remix-ui-terminal-wrapper'
export * from './lib/context'

@ -0,0 +1,41 @@
import { appPlatformTypes, platformContext } from '@remix-ui/app'
import { CustomTooltip } from '@remix-ui/helper'
import React, { useState, useEffect, useRef, useContext } from 'react' // eslint-disable-line
import { FormattedMessage, useIntl } from 'react-intl'
import { listenOnNetworkAction } from '../actions/terminalAction'
import { TerminalContext } from '../context'
import { RemixUiTerminalProps } from '../types/terminalTypes'
import { RemixUITerminalMenu } from './remix-ui-terminal-menu'
import { RemixUITerminalMenuToggle } from './remix-ui-terminal-menu-toggle'
import { RemixUIXtermMenu } from '../../../../xterm/src/lib/components/remix-ui-terminal-menu-xterm'
import { RemixUITerminalMenuButtons } from './remix-ui-terminal-menu-buttons'
export const RemixUITerminalBar = (props: RemixUiTerminalProps) => {
const { terminalState, xtermState } = useContext(TerminalContext)
const platform = useContext(platformContext)
const intl = useIntl()
const terminalMenu = useRef(null)
useEffect(() => {
props.plugin.call('layout', 'minimize', props.plugin.profile.name, !terminalState.isOpen)
}, [terminalState.isOpen])
return (<>
<div className="remix_ui_terminal_bar d-flex">
<div
className="remix_ui_terminal_menu justify-content-between d-flex w-100 align-items-center position-relative border-top border-dark bg-light"
ref={terminalMenu}
data-id="terminalToggleMenu"
>
<RemixUITerminalMenuToggle {...props} />
{platform === appPlatformTypes.desktop ?
<div className='d-flex flex-row w-100 justify-content-between '>
<RemixUITerminalMenuButtons {...props} />
{xtermState.showOutput? <RemixUITerminalMenu {...props} />: <RemixUIXtermMenu {...props} />}
</div> :
<RemixUITerminalMenu {...props} />
}
</div>
</div></>
)
}

@ -0,0 +1,31 @@
import React, { useContext, useEffect } from 'react' // eslint-disable-line
import { TerminalContext } from '../context'
import { RemixUiTerminalProps, SET_OPEN } from '../types/terminalTypes'
import './remix-ui-terminal-menu-buttons.css'
export const RemixUITerminalMenuButtons = (props: RemixUiTerminalProps) => {
const { xtermState, dispatchXterm, terminalState, dispatch } = useContext(TerminalContext)
function selectOutput(event: any): void {
props.plugin.call('layout', 'minimize', props.plugin.profile.name, false)
dispatchXterm({ type: 'SHOW_OUTPUT', payload: true })
dispatch({ type: SET_OPEN, payload: true })
}
function showTerminal(event: any): void {
props.plugin.call('layout', 'minimize', props.plugin.profile.name, false)
dispatchXterm({ type: 'SHOW_OUTPUT', payload: false })
dispatch({ type: SET_OPEN, payload: true })
}
return (
<div className='d-flex flex-row align-items-center'>
<button id="tabOutput" className={`xtermButton btn btn-sm border-secondary mr-2 border ${!xtermState.showOutput ? '' : 'd-flex btn-secondary'}`} onClick={selectOutput}>
Output
</button>
<button id="tabXTerm" className={`xtermButton btn btn-sm border-secondary ${xtermState.terminalsEnabled ? 'd-block' : 'd-none'} ${xtermState.showOutput ? 'd-none' : 'btn-secondary'}`} onClick={showTerminal}>
<span className="far fa-terminal border-0 ml-1"></span>
</button>
</div>
)
}

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

Loading…
Cancel
Save