Merge branch 'master' into mixins

pull/5370/head
bunsenstraat 2 years ago committed by GitHub
commit 13e6a3a962
  1. 3
      apps/remix-ide-e2e/src/tests/solidityImport.test.ts
  2. 49
      apps/remix-ide-e2e/src/tests/transactionExecution.test.ts
  3. 4
      apps/remix-ide-e2e/src/tests/workspace.test.ts
  4. 55
      apps/remix-ide-e2e/src/tests/workspace_git.test.ts
  5. 221
      apps/remix-ide/src/app/plugins/permission-handler-plugin.tsx
  6. 2
      apps/remix-ide/src/app/providers/custom-vm-fork-provider.tsx
  7. 4
      apps/remix-ide/src/app/providers/injected-provider.tsx
  8. 4
      apps/remix-ide/src/app/tabs/debugger-tab.js
  9. 12
      apps/remix-ide/src/app/tabs/locales/en/filePanel.json
  10. 2
      apps/remix-ide/src/app/tabs/locales/en/settings.json
  11. 7
      apps/remix-ide/src/app/tabs/locales/zh/filePanel.json
  12. 27
      apps/remix-ide/src/app/udapp/run-tab.js
  13. 12
      apps/remix-ide/src/assets/css/themes/bootstrap-cyborg.min.css
  14. 2
      apps/remix-ide/src/assets/css/themes/remix-dark_tvx1s2.css
  15. 4
      apps/remix-ide/src/assets/css/themes/remix-hacker_owl.css
  16. 33
      apps/remix-ide/src/blockchain/providers/vm.js
  17. 13
      apps/remix-ide/src/blockchain/providers/worker-vm.ts
  18. 2
      apps/remix-ide/src/remixAppManager.js
  19. 12
      libs/ghaction-helper/package.json
  20. 8
      libs/remix-analyzer/package.json
  21. 6
      libs/remix-astwalker/package.json
  22. 31
      libs/remix-core-plugin/src/lib/compiler-artefacts.ts
  23. 36
      libs/remix-core-plugin/src/lib/compiler-fetch-and-compile.ts
  24. 3
      libs/remix-core-plugin/src/types/contract.ts
  25. 12
      libs/remix-debug/package.json
  26. 6
      libs/remix-lib/package.json
  27. 10
      libs/remix-lib/src/util.ts
  28. 8
      libs/remix-lib/test/util.ts
  29. 6
      libs/remix-simulator/package.json
  30. 2
      libs/remix-simulator/src/provider.ts
  31. 1
      libs/remix-simulator/src/vm-context.ts
  32. 6
      libs/remix-solidity/package.json
  33. 10
      libs/remix-tests/package.json
  34. 4
      libs/remix-ui/helper/src/lib/components/custom-dropdown.tsx
  35. 2
      libs/remix-ui/helper/src/lib/helper-components.tsx
  36. 12
      libs/remix-ui/home-tab/src/lib/components/homeTabTitle.tsx
  37. 4
      libs/remix-ui/modal-dialog/src/lib/remix-ui-modal-dialog.tsx
  38. 2
      libs/remix-ui/permission-handler/src/lib/permission-dialog.tsx
  39. 3
      libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx
  40. 4
      libs/remix-ui/run-tab/src/lib/components/contractGUI.tsx
  41. 2
      libs/remix-ui/run-tab/src/lib/components/instanceContainerUI.tsx
  42. 1
      libs/remix-ui/run-tab/src/lib/run-tab.tsx
  43. 1
      libs/remix-ui/run-tab/src/lib/types/index.ts
  44. 11
      libs/remix-ui/solidity-compiler/src/lib/contract-selection.tsx
  45. 10
      libs/remix-ui/workspace/src/lib/actions/workspace.ts
  46. 51
      libs/remix-ui/workspace/src/lib/components/workspace-hamburger-item.tsx
  47. 345
      libs/remix-ui/workspace/src/lib/components/workspace-hamburger.tsx
  48. 1
      libs/remix-ui/workspace/src/lib/contexts/index.ts
  49. 13
      libs/remix-ui/workspace/src/lib/css/remix-ui-workspace.css
  50. 7
      libs/remix-ui/workspace/src/lib/providers/FileSystemProvider.tsx
  51. 48
      libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx
  52. 4
      libs/remix-url-resolver/package.json
  53. 4
      libs/remix-ws-templates/package.json
  54. 2
      libs/remixd/package.json
  55. 8
      libs/remixd/src/services/remixdClient.ts
  56. 2
      package.json

@ -29,10 +29,9 @@ module.exports = {
.clickLaunchIcon('solidity') .clickLaunchIcon('solidity')
.click('[data-id="compilerContainerCompileBtn"]') .click('[data-id="compilerContainerCompileBtn"]')
.isVisible({ .isVisible({
selector: "//span[contains(.,'not found Untitled11')]", selector: "//span[contains(.,'not found /Untitled11')]",
locateStrategy: 'xpath', locateStrategy: 'xpath',
timeout: 120000, timeout: 120000,
suppressNotFoundErrors: true
}) })
}, },

@ -213,7 +213,28 @@ module.exports = {
'uint256 num': '24' 'uint256 num': '24'
} }
}) })
.end() },
'Should switch to the mainnet VM fork and execute a tx to query ENS #group5': function (browser: NightwatchBrowser) {
let addressRef
browser
.addFile('mainnet_ens.sol', sources[7]['mainnet_ens.sol'])
.clickLaunchIcon('solidity')
.setSolidityCompilerVersion('soljson-v0.8.17+commit.8df45f5f.js')
.clickLaunchIcon('udapp')
.switchEnvironment('vm-mainnet-fork')
.waitForElementPresent('select[data-id="runTabSelectAccount"] option[value="0xdD870fA1b7C4700F2BD7f44238821C26f7392148"]') // wait for the udapp to load the list of accounts
.selectContract('MyResolver')
.createContract('')
.clickInstance(0)
.getAddressAtPosition(0, (address) => {
addressRef = address
})
.clickFunction('resolve - call')
.perform((done) => {
browser.verifyCallReturnValue(addressRef, ['0:address: 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'])
.perform(() => done())
})
} }
} }
@ -431,5 +452,31 @@ contract C {
} }
}` }`
} }
}, {
'mainnet_ens.sol': {
content:
`
import "https://github.com/ensdomains/ens-contracts/blob/master/contracts/utils/NameEncoder.sol";
abstract contract ENS {
function resolver(bytes32 node) public virtual view returns (Resolver);
}
abstract contract Resolver {
function addr(bytes32 node) public virtual view returns (address);
}
contract MyResolver {
// Same address for Mainet, Ropsten, Rinkerby, Gorli and other networks;
ENS ens = ENS(0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e);
function resolve() public view returns(address) {
(,bytes32 node) = NameEncoder.dnsEncodeName("vitalik.eth");
Resolver resolver = ens.resolver(node);
return resolver.addr(node);
}
}
`
}
} }
] ]

@ -410,12 +410,12 @@ module.exports = {
browser browser
.useXpath() .useXpath()
.waitForElementPresent({ .waitForElementPresent({
selector: '//i[@data-icon="workspaceDropdownMenuIcon"]', selector: '//i[@data-id="workspaceDropdownMenuIcon"]',
locateStrategy: 'xpath', locateStrategy: 'xpath',
}) })
.click('//*[@id="workspacesMenuDropdown"]/span/i') .click('//*[@id="workspacesMenuDropdown"]/span/i')
.waitForElementVisible('//*[@id="workspacesMenuDropdown"]/div/ul') .waitForElementVisible('//*[@id="workspacesMenuDropdown"]/div/ul')
.click('//*[@id="workspacesMenuDropdown"]/div/ul/a[3]') // rename workspace_name .click('//*[@id="workspacesMenuDropdown"]/div/ul/a[4]') // rename workspace_name
.useCss() .useCss()
.waitForElementVisible('*[data-id="treeViewLitreeViewItemtests"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemtests"]')
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextRename"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextRename"]')

@ -58,19 +58,15 @@ module.exports = {
.waitForElementContainsText('[data-id="workspaceGitBranchesDropdown"]', 'main') .waitForElementContainsText('[data-id="workspaceGitBranchesDropdown"]', 'main')
}, },
// CLONE REPOSITORY E2E START // CLONE REPOSITORY E2E START
'Should clone a repository #group2': function (browser: NightwatchBrowser) { 'Should clone a repository #group2': function (browser: NightwatchBrowser) {
browser browser
.clickLaunchIcon('filePanel') .clickLaunchIcon('filePanel')
.useXpath() .waitForElementVisible('[data-id="workspaceMenuDropdown"]')
.click('//*[@id="workspacesMenuDropdown"]/span/i') .click('[data-id="workspaceMenuDropdown"]')
.waitForElementVisible('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') .waitForElementVisible('[data-id="workspaceclone"]')
.click('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') .click('[data-id="workspaceclone"]')
.useCss()
.waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') .waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]')
.click('[data-id="fileSystemModalDialogModalBody-react"]') .click('[data-id="fileSystemModalDialogModalBody-react"]')
.waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') .waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]')
@ -93,11 +89,9 @@ module.exports = {
'Should display non-clashing names for duplicate clone #group2': '' + function (browser: NightwatchBrowser) { 'Should display non-clashing names for duplicate clone #group2': '' + function (browser: NightwatchBrowser) {
browser browser
.useXpath() .click('[data-id="workspaceMenuDropdown"]')
.click('//*[@id="workspacesMenuDropdown"]/span/i') .waitForElementVisible('[data-id="workspaceclone"]')
.waitForElementVisible('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') .click('[data-id="workspaceclone"]')
.click('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]')
.useCss()
.waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') .waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]')
.click('[data-id="fileSystemModalDialogModalBody-react"]') .click('[data-id="fileSystemModalDialogModalBody-react"]')
.waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') .waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]')
@ -105,11 +99,9 @@ module.exports = {
.click('[data-id="fileSystem-modal-footer-ok-react"]') .click('[data-id="fileSystem-modal-footer-ok-react"]')
.pause(5000) .pause(5000)
.waitForElementContainsText('[data-id="workspacesSelect"]', 'awesome-remix1') .waitForElementContainsText('[data-id="workspacesSelect"]', 'awesome-remix1')
.useXpath() .click('[data-id="workspaceMenuDropdown"]')
.click('//*[@id="workspacesMenuDropdown"]/span/i') .waitForElementVisible('[data-id="workspaceclone"]')
.waitForElementVisible('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') .click('[data-id="workspaceclone"]')
.click('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]')
.useCss()
.waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') .waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]')
.click('[data-id="fileSystemModalDialogModalBody-react"]') .click('[data-id="fileSystemModalDialogModalBody-react"]')
.waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') .waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]')
@ -117,10 +109,8 @@ module.exports = {
.click('[data-id="fileSystem-modal-footer-ok-react"]') .click('[data-id="fileSystem-modal-footer-ok-react"]')
.pause(5000) .pause(5000)
.waitForElementContainsText('[data-id="workspacesSelect"]', 'awesome-remix2') .waitForElementContainsText('[data-id="workspacesSelect"]', 'awesome-remix2')
.useXpath() .click('[data-id="workspaceMenuDropdown"]')
.click('//*[@id="workspacesMenuDropdown"]/span/i') .waitForElementVisible('[data-id="workspaceDropdownMenuIcon]"')
.waitForElementVisible('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]')
.useCss()
.waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') .waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]')
.click('[data-id="fileSystemModalDialogModalBody-react"]') .click('[data-id="fileSystemModalDialogModalBody-react"]')
.waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') .waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]')
@ -136,15 +126,10 @@ module.exports = {
'Should display error message in modal for failed clone #group2': function (browser: NightwatchBrowser) { 'Should display error message in modal for failed clone #group2': function (browser: NightwatchBrowser) {
browser browser
.useXpath() .waitForElementVisible('[data-id="workspaceDropdownMenuIcon"]')
.waitForElementPresent({ .click('[data-id="workspaceMenuDropdown"]')
selector: '//i[@data-icon="workspaceDropdownMenuIcon"]', .waitForElementVisible('[data-id="workspaceclone"]')
locateStrategy: 'xpath', .click('[data-id="workspaceclone"]')
})
.click('//*[@id="workspacesMenuDropdown"]/span/i')
.waitForElementVisible('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]')
.click('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]')
.useCss()
.waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') .waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]')
.click('[data-id="fileSystemModalDialogModalBody-react"]') .click('[data-id="fileSystemModalDialogModalBody-react"]')
.waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') .waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]')
@ -163,11 +148,9 @@ module.exports = {
browser browser
.clickLaunchIcon('filePanel') .clickLaunchIcon('filePanel')
.waitForElementNotVisible('[data-id="workspaceGitPanel"]') .waitForElementNotVisible('[data-id="workspaceGitPanel"]')
.useXpath() .click('[data-id="workspaceMenuDropdown"]')
.click('//*[@id="workspacesMenuDropdown"]/span/i') .waitForElementVisible('[data-id="workspaceclone"]')
.waitForElementVisible('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') .click('[data-id="workspaceclone"]')
.click('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]')
.useCss()
.waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') .waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]')
.click('[data-id="fileSystemModalDialogModalBody-react"]') .click('[data-id="fileSystemModalDialogModalBody-react"]')
.waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') .waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]')

@ -6,131 +6,130 @@ import { PermissionHandlerDialog, PermissionHandlerValue } from '@remix-ui/permi
import { Profile } from '@remixproject/plugin-utils' import { Profile } from '@remixproject/plugin-utils'
const profile = { const profile = {
name: 'permissionhandler', name: 'permissionhandler',
displayName: 'permissionhandler', displayName: 'permissionhandler',
description: 'Plugin to handle permissions', description: 'Plugin to handle permissions',
methods: ['askPermission'] methods: ['askPermission']
} }
export class PermissionHandlerPlugin extends Plugin { export class PermissionHandlerPlugin extends Plugin {
permissions: any permissions: any
currentVersion: number currentVersion: number
fallbackMemory: boolean fallbackMemory: boolean
constructor() { constructor() {
super(profile) super(profile)
this.fallbackMemory = false this.fallbackMemory = false
this.permissions = this._getFromLocal() this.permissions = this._getFromLocal()
this.currentVersion = 1 this.currentVersion = 1
// here we remove the old permissions saved before adding 'permissionVersion' // here we remove the old permissions saved before adding 'permissionVersion'
// since with v1 the structure has been changed because of new engine ^0.2.0-alpha.6 changes // since with v1 the structure has been changed because of new engine ^0.2.0-alpha.6 changes
if (!localStorage.getItem('permissionVersion')) { if (!localStorage.getItem('permissionVersion')) {
localStorage.setItem('plugins/permissions', '') localStorage.setItem('plugins/permissions', '')
localStorage.setItem('permissionVersion', this.currentVersion.toString()) localStorage.setItem('permissionVersion', this.currentVersion.toString())
}
} }
}
_getFromLocal() { _getFromLocal() {
if (this.fallbackMemory) return this.permissions if (this.fallbackMemory) return this.permissions
const permission = localStorage.getItem('plugins/permissions') const permission = localStorage.getItem('plugins/permissions')
return permission ? JSON.parse(permission) : {} return permission ? JSON.parse(permission) : {}
} }
persistPermissions() { persistPermissions() {
const permissions = JSON.stringify(this.permissions) const permissions = JSON.stringify(this.permissions)
try { try {
localStorage.setItem('plugins/permissions', permissions) localStorage.setItem('plugins/permissions', permissions)
} catch (e) { } catch (e) {
this.fallbackMemory = true this.fallbackMemory = true
console.log(e) console.log(e)
}
} }
}
switchMode (from: Profile, to: Profile, method: string, set: boolean) { switchMode (from: Profile, to: Profile, method: string, set: boolean) {
set set
? this.permissions[to.name][method][from.name] = {} ? this.permissions[to.name][method][from.name] = {}
: delete this.permissions[to.name][method][from.name] : delete this.permissions[to.name][method][from.name]
} }
clear() { clear() {
localStorage.removeItem('plugins/permissions') localStorage.removeItem('plugins/permissions')
} }
notAllowWarning(from: Profile, to: Profile, method: string) { notAllowWarning(from: Profile, to: Profile, method: string) {
return `${from.displayName || from.name} is not allowed to call ${method} method of ${to.displayName || to.name}.` return `${from.displayName || from.name} is not allowed to call ${method} method of ${to.displayName || to.name}.`
} }
async getTheme() { async getTheme() {
return (await this.call('theme', 'currentTheme')).quality return (await this.call('theme', 'currentTheme')).quality
} }
/** /**
* Check if a plugin has the permission to call another plugin and askPermission if needed * Check if a plugin has the permission to call another plugin and askPermission if needed
* @param {PluginProfile} from the profile of the plugin that make the call * @param {PluginProfile} from the profile of the plugin that make the call
* @param {ModuleProfile} to The profile of the module that receive the call * @param {ModuleProfile} to The profile of the module that receive the call
* @param {string} method The name of the function to be called * @param {string} method The name of the function to be called
* @param {string} message from the caller plugin to add more details if needed * @param {string} message from the caller plugin to add more details if needed
* @returns {Promise<boolean>} * @returns {Promise<boolean>}
*/ */
async askPermission(from: Profile, to: Profile, method: string, message: string, sensitiveCall: boolean) { async askPermission(from: Profile, to: Profile, method: string, message: string, sensitiveCall: boolean) {
try { try {
this.permissions = this._getFromLocal() this.permissions = this._getFromLocal()
if (!this.permissions[to.name]) this.permissions[to.name] = {} if (!this.permissions[to.name]) this.permissions[to.name] = {}
if (!this.permissions[to.name][method]) this.permissions[to.name][method] = {} if (!this.permissions[to.name][method]) this.permissions[to.name][method] = {}
if (!this.permissions[to.name][method][from.name]) return this.openPermission(from, to, method, message, sensitiveCall) if (!this.permissions[to.name][method][from.name]) return this.openPermission(from, to, method, message, sensitiveCall)
const { allow, hash } = this.permissions[to.name][method][from.name] const { allow, hash } = this.permissions[to.name][method][from.name]
if (!allow) { if (!allow) {
const warning = this.notAllowWarning(from, to, method) const warning = this.notAllowWarning(from, to, method)
this.call('notification', 'toast', warning) this.call('notification', 'toast', warning)
return false return false
} }
return hash === from.hash return hash === from.hash
? true // Allow ? true // Allow
: await this.openPermission(from, to, method, message, sensitiveCall) : await this.openPermission(from, to, method, message, sensitiveCall)
} catch (err) { } catch (err) {
throw new Error(err) throw new Error(err)
}
} }
}
async openPermission(from: Profile, to: Profile, method: string, message: string, sensitiveCall: boolean) { async openPermission(from: Profile, to: Profile, method: string, message: string, sensitiveCall: boolean) {
const remember = this.permissions[to.name][method][from.name] const remember = this.permissions[to.name][method][from.name]
const value: PermissionHandlerValue = { const value: PermissionHandlerValue = {
from, from,
to, to,
method, method,
message, message,
remember, remember,
sensitiveCall sensitiveCall
}
const modal: AppModal = {
id: 'PermissionHandler',
title: <FormattedMessage id='permissionHandler.permissionNeededFor' values={{ to: to.displayName || to.name }} />,
message: <PermissionHandlerDialog plugin={this} theme={await this.getTheme()} value={value}></PermissionHandlerDialog>,
okLabel: <FormattedMessage id='permissionHandler.accept' />,
cancelLabel: <FormattedMessage id='permissionHandler.decline' />
}
const result = await this.call('notification', 'modal', modal)
return new Promise((resolve, reject) => {
if (result) {
if (this.permissions[to.name][method][from.name]) {
this.permissions[to.name][method][from.name] = {
allow: true,
hash: from.hash
}
this.persistPermissions()
} }
const modal: AppModal = { resolve(true)
id: 'PermissionHandler', } else {
title: <FormattedMessage id='permissionHandler.permissionNeededFor' values={{ to: to.displayName || to.name }} />, if (this.permissions[to.name][method][from.name]) {
message: <PermissionHandlerDialog plugin={this} theme={await this.getTheme()} value={value}></PermissionHandlerDialog>, this.permissions[to.name][method][from.name] = {
okLabel: <FormattedMessage id='permissionHandler.accept' />, allow: false,
cancelLabel: <FormattedMessage id='permissionHandler.decline' /> hash: from.hash
}
this.persistPermissions()
} }
reject(this.notAllowWarning(from, to, method))
const result = await this.call('notification', 'modal', modal) }
return new Promise((resolve, reject) => { })
if (result) { }
if (this.permissions[to.name][method][from.name]) {
this.permissions[to.name][method][from.name] = {
allow: true,
hash: from.hash
}
this.persistPermissions()
}
resolve(true)
} else {
if (this.permissions[to.name][method][from.name]) {
this.permissions[to.name][method][from.name] = {
allow: false,
hash: from.hash
}
this.persistPermissions()
}
reject(this.notAllowWarning(from, to, method))
}
})
}
} }

@ -39,7 +39,7 @@ export class CustomForkVMProvider extends BasicVMProvider {
</div> </div>
<div> <div>
<label className="mt-3 mb-1">EVM</label> <label className="mt-3 mb-1">EVM</label>
<select data-id="CustomForkEvmType" name="evmType" className="border form-control border-right-0"> <select data-id="CustomForkEvmType" name="evmType" defaultValue="merge" className="border form-control border-right-0">
{Object.keys(Hardfork).map((value, index) => { {Object.keys(Hardfork).map((value, index) => {
return <option value={Hardfork[value]} key={index}>{value}</option> return <option value={Hardfork[value]} key={index}>{value}</option>
})} })}

@ -35,10 +35,11 @@ export class InjectedProvider extends Plugin implements IProvider {
async init () { async init () {
const injectedProvider = (window as any).ethereum const injectedProvider = (window as any).ethereum
if (injectedProvider === undefined) { if (injectedProvider === undefined) {
this.call('notification', 'toast', noInjectedProviderMsg)
throw new Error(noInjectedProviderMsg) throw new Error(noInjectedProviderMsg)
} else { } else {
if (injectedProvider && injectedProvider._metamask && injectedProvider._metamask.isUnlocked) { if (injectedProvider && injectedProvider._metamask && injectedProvider._metamask.isUnlocked) {
if (!await injectedProvider._metamask.isUnlocked()) throw new Error('Please make sure the injected provider is unlocked (e.g Metamask).') if (!await injectedProvider._metamask.isUnlocked()) this.call('notification', 'toast', 'Please make sure the injected provider is unlocked (e.g Metamask).')
} }
this.askPermission(true) this.askPermission(true)
} }
@ -60,7 +61,6 @@ export class InjectedProvider extends Plugin implements IProvider {
} }
try { try {
if ((window as any) && typeof (window as any).ethereum.request === "function") (window as any).ethereum.request({ method: "eth_requestAccounts" }); if ((window as any) && typeof (window as any).ethereum.request === "function") (window as any).ethereum.request({ method: "eth_requestAccounts" });
if (!await (window as any).ethereum._metamask.isUnlocked()) this.call('notification', 'toast', 'Please make sure the injected provider is unlocked (e.g Metamask).')
const resultData = await this.provider.currentProvider.send(data.method, data.params) const resultData = await this.provider.currentProvider.send(data.method, data.params)
resolve({ jsonrpc: '2.0', result: resultData.result, id: data.id }) resolve({ jsonrpc: '2.0', result: resultData.result, id: data.id })
} catch (error) { } catch (error) {

@ -45,10 +45,6 @@ export class DebuggerTab extends DebuggerApiMixin(ViewPlugin) {
this.call('notification', 'toast', notFoundToastMsg(contractAddress)) this.call('notification', 'toast', notFoundToastMsg(contractAddress))
}) })
this.on('fetchAndCompile', 'usingLocalCompilation', (contractAddress) => {
this.call('notification', 'toast', localCompilationToastMsg())
})
this.on('fetchAndCompile', 'sourceVerificationNotAvailable', () => { this.on('fetchAndCompile', 'sourceVerificationNotAvailable', () => {
this.call('notification', 'toast', sourceVerificationNotAvailableToastMsg()) this.call('notification', 'toast', sourceVerificationNotAvailableToastMsg())
}) })

@ -3,16 +3,19 @@
"filePanel.workspace": "WORKSPACES", "filePanel.workspace": "WORKSPACES",
"filePanel.create": "Create", "filePanel.create": "Create",
"filePanel.clone": "Clone", "filePanel.clone": "Clone",
"filePanel.download": "Download", "filePanel.download": "Backup",
"filePanel.restore": "Restore", "filePanel.restore": "Restore",
"filePanel.workspace.create": "Create Workspace", "filePanel.workspace.create": "Create Workspace",
"filePanel.workspace.rename": "Rename Workspace", "filePanel.workspace.rename": "Rename Workspace",
"filePanel.workspace.delete": "Delete Workspace", "filePanel.workspace.delete": "Delete Workspace",
"filePanel.workspace.deleteConfirm": "Are you sure to delete the current workspace?", "filePanel.workspace.deleteConfirm": "Are you sure to delete the current workspace?",
"filePanel.workspace.deleteAll": "Delete All Workspaces",
"filePanel.workspace.deleteAllConfirm1": "Are you absolutely sure you want to delete all your workspaces?",
"filePanel.workspace.deleteAllConfirm2": "Deleted workspaces can not be restored in any manner.",
"filePanel.workspace.name": "Workspace name", "filePanel.workspace.name": "Workspace name",
"filePanel.workspace.chooseTemplate": "Choose a template", "filePanel.workspace.chooseTemplate": "Choose a template",
"filePanel.workspace.download": "Download Workspace", "filePanel.workspace.download": "Backup Workspaces",
"filePanel.workspace.restore": "Restore Workspace Backup", "filePanel.workspace.restore": "Restore Workspaces from the Backup",
"filePanel.workspace.clone": "Clone Git Repository", "filePanel.workspace.clone": "Clone Git Repository",
"filePanel.workspace.cloneMessage": "Please provide a valid git repository url.", "filePanel.workspace.cloneMessage": "Please provide a valid git repository url.",
"filePanel.workspace.enterGitUrl": "Enter git repository url", "filePanel.workspace.enterGitUrl": "Enter git repository url",
@ -52,5 +55,6 @@
"filePanel.customizeTemplate": "Customize template", "filePanel.customizeTemplate": "Customize template",
"filePanel.features": "Features", "filePanel.features": "Features",
"filePanel.upgradeability": "Upgradeability", "filePanel.upgradeability": "Upgradeability",
"filePanel.ok": "OK" "filePanel.ok": "OK",
"filePanel.cancel": "Cancel"
} }

@ -3,7 +3,7 @@
"settings.reset": "Reset to Default settings", "settings.reset": "Reset to Default settings",
"settings.general": "General settings", "settings.general": "General settings",
"settings.generateContractMetadataText": "Generate contract metadata. Generate a JSON file in the contract folder. Allows to specify library addresses the contract depends on. If nothing is specified, Remix deploys libraries automatically.", "settings.generateContractMetadataText": "Generate contract metadata. Generate a JSON file in the contract folder. Allows to specify library addresses the contract depends on. If nothing is specified, Remix deploys libraries automatically.",
"settings.ethereunVMText": "Always use Javascript VM at load", "settings.ethereunVMText": "Always use the Remix VM at load",
"settings.wordWrapText": "Word wrap in editor", "settings.wordWrapText": "Word wrap in editor",
"settings.useAutoCompleteText": "Enable code completion in editor.", "settings.useAutoCompleteText": "Enable code completion in editor.",
"settings.useShowGasInEditorText": "Display gas estimates in editor.", "settings.useShowGasInEditorText": "Display gas estimates in editor.",

@ -9,7 +9,9 @@
"filePanel.workspace.rename": "重命名工作空间", "filePanel.workspace.rename": "重命名工作空间",
"filePanel.workspace.delete": "删除工作空间", "filePanel.workspace.delete": "删除工作空间",
"filePanel.workspace.deleteConfirm": "确定要删除当前工作空间?", "filePanel.workspace.deleteConfirm": "确定要删除当前工作空间?",
"filePanel.workspace.name": "工作空间名称", "filePanel.workspace.deleteAll": "Delete All Workspaces",
"filePanel.workspace.deleteAllConfirm1": "Are you absolutely sure you want to delete all your workspaces?",
"filePanel.workspace.deleteAllConfirm2": "Deleted workspaces can not be restored in any manner.", "filePanel.workspace.name": "工作空间名称",
"filePanel.workspace.chooseTemplate": "选择一个工作空间模板", "filePanel.workspace.chooseTemplate": "选择一个工作空间模板",
"filePanel.workspace.download": "下载工作空间", "filePanel.workspace.download": "下载工作空间",
"filePanel.workspace.restore": "恢复工作空间", "filePanel.workspace.restore": "恢复工作空间",
@ -52,5 +54,6 @@
"filePanel.customizeTemplate": "自定义模板", "filePanel.customizeTemplate": "自定义模板",
"filePanel.features": "特点", "filePanel.features": "特点",
"filePanel.upgradeability": "可升级性", "filePanel.upgradeability": "可升级性",
"filePanel.ok": "确认" "filePanel.ok": "确认",
"filePanel.cancel": "Cancel"
} }

@ -131,6 +131,12 @@ export class RunTab extends ViewPlugin {
}) })
} }
// basic injected
const displayNameInjected = `Injected Provider${(window && window.ethereum && !(window.ethereum.providers && !window.ethereum.selectedProvider)) ?
window.ethereum.isCoinbaseWallet || window.ethereum.selectedProvider?.isCoinbaseWallet ? ' - Coinbase' :
window.ethereum.isBraveWallet || window.ethereum.selectedProvider?.isBraveWallet ? ' - Brave' :
window.ethereum.isMetaMask || window.ethereum.selectedProvider?.isMetaMask ? ' - MetaMask' : '' : ''}`
await addProvider('injected', displayNameInjected, true, false)
// VM // VM
const titleVM = 'Execution environment is local to Remix. Data is only saved to browser memory and will vanish upon reload.' const titleVM = 'Execution environment is local to Remix. Data is only saved to browser memory and will vanish upon reload.'
await addProvider('vm-merge', 'Remix VM (Merge)', false, true, 'merge', 'settingsVMMergeMode', titleVM) await addProvider('vm-merge', 'Remix VM (Merge)', false, true, 'merge', 'settingsVMMergeMode', titleVM)
@ -142,20 +148,17 @@ export class RunTab extends ViewPlugin {
await addProvider('vm-custom-fork', 'Remix VM - Custom fork', false, true, '', 'settingsVMCustomMode', titleVM) await addProvider('vm-custom-fork', 'Remix VM - Custom fork', false, true, '', 'settingsVMCustomMode', titleVM)
// external provider // external provider
await addProvider('hardhat-provider', 'Hardhat Provider', false, false) await addProvider('basic-http-provider', 'Custom - External Http Provider', false, false)
await addProvider('ganache-provider', 'Ganache Provider', false, false) await addProvider('hardhat-provider', 'Dev - Hardhat Provider', false, false)
await addProvider('foundry-provider', 'Foundry Provider', false, false) await addProvider('ganache-provider', 'Dev - Ganache Provider', false, false)
await addProvider('foundry-provider', 'Dev - Foundry Provider', false, false)
// injected provider
await addProvider('injected-optimism-provider', 'L2 - Optimism Provider', true, false)
await addProvider('injected-arbitrum-one-provider', 'L2 - Arbitrum One Provider', true, false)
await addProvider('walletconnect', 'Wallet Connect', false, false) await addProvider('walletconnect', 'Wallet Connect', false, false)
await addProvider('basic-http-provider', 'External Http Provider', false, false)
// injected provider
const displayNameInjected = `Injected Provider${(window && window.ethereum && !(window.ethereum.providers && !window.ethereum.selectedProvider)) ?
window.ethereum.isCoinbaseWallet || window.ethereum.selectedProvider?.isCoinbaseWallet ? ' - Coinbase' :
window.ethereum.isBraveWallet || window.ethereum.selectedProvider?.isBraveWallet ? ' - Brave' :
window.ethereum.isMetaMask || window.ethereum.selectedProvider?.isMetaMask ? ' - MetaMask' : '' : ''}`
await addProvider('injected', displayNameInjected, true, false)
await addProvider('injected-optimism-provider', 'Optimism Provider', true, false)
await addProvider('injected-arbitrum-one-provider', 'Arbitrum One Provider', true, false)
} }
writeFile (fileName, content) { writeFile (fileName, content) {

@ -8611,28 +8611,28 @@ legend {
text-decoration:underline text-decoration:underline
} }
.alert-primary { .alert-primary {
background-color:#2a9fd6 background-color:#2a9fd685
} }
.alert-secondary { .alert-secondary {
background-color:#555 background-color:#555
} }
.alert-success { .alert-success {
background-color:#77b300 background-color:#77b3007a
} }
.alert-info { .alert-info {
background-color:#93c background-color:#9933cc91
} }
.alert-warning { .alert-warning {
background-color:#f80 background-color:#ff8800a1
} }
.alert-danger { .alert-danger {
background-color:#c00 background-color:#cc00009c
} }
.alert-light { .alert-light {
background-color:#222 background-color:#222
} }
.alert-dark { .alert-dark {
background-color:#adafae background-color:#adafae99
} }
.badge-warning { .badge-warning {
color:#fff color:#fff

@ -4481,7 +4481,7 @@ a.badge-dark:focus {
} }
.alert-primary { .alert-primary {
color: #fff; color: #fff;
background-color: #5CBDEE; background-color: #5cbdee94;
border-color: #5CBDEE; border-color: #5CBDEE;
} }
.alert-primary hr { .alert-primary hr {

@ -13,7 +13,7 @@
--cyan: #355f7d; --cyan: #355f7d;
--white: #fff; --white: #fff;
--gray: #8B99A6; --gray: #8B99A6;
--gray-dark: #343a40; --gray-dark: #8694a1;
--primary: #007aa6; --primary: #007aa6;
--secondary: #0E2A3E; --secondary: #0E2A3E;
--success: #C4E07F; --success: #C4E07F;
@ -4491,7 +4491,7 @@ a.badge-dark:focus {
} }
.alert-primary { .alert-primary {
color: #fff; color: #fff;
background-color: #5CBDEE; background-color: #5cbdee94;
border-color: #5CBDEE; border-color: #5CBDEE;
} }
.alert-primary hr { .alert-primary hr {

@ -24,27 +24,30 @@ class VMProvider {
this.worker = new Worker(new URL('./worker-vm', import.meta.url)) this.worker = new Worker(new URL('./worker-vm', import.meta.url))
const provider = this.executionContext.getProviderObject() const provider = this.executionContext.getProviderObject()
this.worker.postMessage({ cmd: 'init', fork: this.executionContext.getCurrentFork(), nodeUrl: provider?.options['nodeUrl'], blockNumber: provider?.options['blockNumber']})
let incr = 0 let incr = 0
const stamps = {} const stamps = {}
this.worker.addEventListener('message', (msg) => { this.worker.addEventListener('message', (msg) => {
if (stamps[msg.data.stamp]) { if (msg.data.cmd === 'sendAsyncResult' && stamps[msg.data.stamp]) {
stamps[msg.data.stamp](msg.data.error, msg.data.result) stamps[msg.data.stamp](msg.data.error, msg.data.result)
} else if (msg.data.cmd === 'initiateResult') {
if (!msg.data.error) {
this.provider = {
sendAsync: (query, callback) => {
const stamp = Date.now() + incr
incr++
stamps[stamp] = callback
this.worker.postMessage({ cmd: 'sendAsync', query, stamp })
}
}
this.web3 = new Web3(this.provider)
extend(this.web3)
this.accounts = {}
this.executionContext.setWeb3(this.executionContext.getProvider(), this.web3)
}
} }
}) })
this.provider = {
sendAsync: (query, callback) => { this.worker.postMessage({ cmd: 'init', fork: this.executionContext.getCurrentFork(), nodeUrl: provider?.options['nodeUrl'], blockNumber: provider?.options['blockNumber']})
const stamp = Date.now() + incr
incr++
stamps[stamp] = callback
this.worker.postMessage({ cmd: 'sendAsync', query, stamp })
}
}
this.web3 = new Web3(this.provider)
extend(this.web3)
this.accounts = {}
this.executionContext.setWeb3(this.executionContext.getProvider(), this.web3)
} }
// TODO: is still here because of the plugin API // TODO: is still here because of the plugin API

@ -7,7 +7,18 @@ self.onmessage = (e: MessageEvent) => {
case 'init': 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 })
if (provider) provider.init() provider.init().then(() => {
self.postMessage({
cmd: 'initiateResult',
stamp: data.stamp
})
}).catch((error) => {
self.postMessage({
cmd: 'initiateResult',
error,
stamp: data.stamp
})
})
break break
} }
case 'sendAsync': case 'sendAsync':

@ -10,7 +10,7 @@ const requiredModules = [ // services + layout views + system views
'fileManager', 'contentImport', 'blockchain', 'web3Provider', 'scriptRunner', 'fetchAndCompile', 'mainPanel', 'hiddenPanel', 'sidePanel', 'menuicons', 'fileManager', 'contentImport', 'blockchain', 'web3Provider', 'scriptRunner', 'fetchAndCompile', 'mainPanel', 'hiddenPanel', 'sidePanel', 'menuicons',
'filePanel', 'terminal', 'settings', 'pluginManager', 'tabs', 'udapp', 'dGitProvider', 'solidity', 'solidity-logic', 'gistHandler', 'layout', 'filePanel', 'terminal', 'settings', 'pluginManager', 'tabs', 'udapp', 'dGitProvider', 'solidity', 'solidity-logic', 'gistHandler', 'layout',
'notification', 'permissionhandler', 'walkthrough', 'storage', 'restorebackupzip', 'link-libraries', 'deploy-libraries', 'openzeppelin-proxy', 'notification', 'permissionhandler', 'walkthrough', 'storage', 'restorebackupzip', 'link-libraries', 'deploy-libraries', 'openzeppelin-proxy',
'hardhat-provider', 'ganache-provider', 'foundry-provider', 'basic-http-provider', 'injected', 'injected-optimism-provider', 'injected-arbitrum-one-provider', 'hardhat-provider', 'ganache-provider', 'foundry-provider', 'basic-http-provider', 'injected', 'injected-optimism-provider', 'injected-arbitrum-one-provider', 'vm-custom-fork', 'vm-goerli-fork', 'vm-mainnet-fork', 'vm-sepolia-fork', 'vm-merge', 'vm-london', 'vm-berlin',
'compileAndRun', 'search', 'recorder', 'fileDecorator', 'codeParser', 'codeFormatter', 'solidityumlgen', 'contractflattener'] 'compileAndRun', 'search', 'recorder', 'fileDecorator', 'codeParser', 'codeFormatter', 'solidityumlgen', 'contractflattener']
// dependentModules shouldn't be manually activated (e.g hardhat is activated by remixd) // dependentModules shouldn't be manually activated (e.g hardhat is activated by remixd)

@ -1,6 +1,6 @@
{ {
"name": "@remix-project/ghaction-helper", "name": "@remix-project/ghaction-helper",
"version": "0.1.5", "version": "0.1.6",
"description": "Solidity Tests GitHub Action Helper", "description": "Solidity Tests GitHub Action Helper",
"main": "src/index.js", "main": "src/index.js",
"scripts": { "scripts": {
@ -19,14 +19,16 @@
}, },
"homepage": "https://github.com/ethereum/remix-project#readme", "homepage": "https://github.com/ethereum/remix-project#readme",
"devDependencies": { "devDependencies": {
"@remix-project/remix-solidity": "^0.5.9", "@remix-project/remix-solidity": "^0.5.10",
"@types/chai": "^4.3.4", "@types/chai": "^4.3.4",
"typescript": "^4.9.3" "typescript": "^4.9.3"
}, },
"dependencies": { "dependencies": {
"@ethereum-waffle/chai": "^3.4.4", "@ethereum-waffle/chai": "^3.4.4",
"@remix-project/remix-simulator": "^0.2.24",
"chai": "^4.3.7", "chai": "^4.3.7",
"ethers": "^5.7.2", "ethers": "^5.7.2"
"@remix-project/remix-simulator": "^0.2.21" },
} "types": "./src/index.d.ts",
"gitHead": "c1415e0c3751af8bf53292d67fedf68e15bf3b23"
} }

@ -1,6 +1,6 @@
{ {
"name": "@remix-project/remix-analyzer", "name": "@remix-project/remix-analyzer",
"version": "0.5.32", "version": "0.5.33",
"description": "Tool to perform static analysis on Solidity smart contracts", "description": "Tool to perform static analysis on Solidity smart contracts",
"scripts": { "scripts": {
"test": "./../../node_modules/.bin/ts-node --project ../../tsconfig.base.json --require tsconfig-paths/register ./../../node_modules/.bin/tape ./test/tests.ts" "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.0.2", "@ethereumjs/tx": "^4.0.2",
"@ethereumjs/util": "^8.0.3", "@ethereumjs/util": "^8.0.3",
"@ethereumjs/vm": "^6.3.0", "@ethereumjs/vm": "^6.3.0",
"@remix-project/remix-astwalker": "^0.0.53", "@remix-project/remix-astwalker": "^0.0.54",
"@remix-project/remix-lib": "^0.5.23", "@remix-project/remix-lib": "^0.5.24",
"async": "^2.6.2", "async": "^2.6.2",
"ethers": "^5.4.2", "ethers": "^5.4.2",
"ethjs-util": "^0.1.6", "ethjs-util": "^0.1.6",
@ -50,6 +50,6 @@
"typescript": "^3.7.5" "typescript": "^3.7.5"
}, },
"typings": "src/index.d.ts", "typings": "src/index.d.ts",
"gitHead": "69af4eddd1ba47f76a71c78077d453deb572b1a0", "gitHead": "c1415e0c3751af8bf53292d67fedf68e15bf3b23",
"main": "./src/index.js" "main": "./src/index.js"
} }

@ -1,6 +1,6 @@
{ {
"name": "@remix-project/remix-astwalker", "name": "@remix-project/remix-astwalker",
"version": "0.0.53", "version": "0.0.54",
"description": "Tool to walk through Solidity AST", "description": "Tool to walk through Solidity AST",
"main": "src/index.js", "main": "src/index.js",
"scripts": { "scripts": {
@ -37,7 +37,7 @@
"@ethereumjs/tx": "^4.0.2", "@ethereumjs/tx": "^4.0.2",
"@ethereumjs/util": "^8.0.3", "@ethereumjs/util": "^8.0.3",
"@ethereumjs/vm": "^6.3.0", "@ethereumjs/vm": "^6.3.0",
"@remix-project/remix-lib": "^0.5.23", "@remix-project/remix-lib": "^0.5.24",
"@types/tape": "^4.2.33", "@types/tape": "^4.2.33",
"async": "^2.6.2", "async": "^2.6.2",
"ethers": "^5.4.2", "ethers": "^5.4.2",
@ -53,6 +53,6 @@
"tap-spec": "^5.0.0" "tap-spec": "^5.0.0"
}, },
"typings": "src/index.d.ts", "typings": "src/index.d.ts",
"gitHead": "69af4eddd1ba47f76a71c78077d453deb572b1a0", "gitHead": "c1415e0c3751af8bf53292d67fedf68e15bf3b23",
"types": "./src/index.d.ts" "types": "./src/index.d.ts"
} }

@ -5,7 +5,7 @@ import { CompilerAbstract } from '@remix-project/remix-solidity'
const profile = { const profile = {
name: 'compilerArtefacts', name: 'compilerArtefacts',
methods: ['get', 'addResolvedContract', 'getCompilerAbstract', 'getAllContractDatas', 'getLastCompilationResult', 'getArtefactsByContractName', 'getContractDataFromAddress'], methods: ['get', 'addResolvedContract', 'getCompilerAbstract', 'getAllContractDatas', 'getLastCompilationResult', 'getArtefactsByContractName', 'getContractDataFromAddress', 'getContractDataFromByteCode'],
events: [], events: [],
version: '0.0.1' version: '0.0.1'
} }
@ -98,16 +98,17 @@ export class CompilerArtefacts extends Plugin {
filterAllContractDatas (filter) { filterAllContractDatas (filter) {
const contractsData = {} const contractsData = {}
Object.keys(this.compilersArtefactsPerFile).map((targetFile) => { Object.keys(this.compilersArtefactsPerFile).map((targetFile) => {
const contracts = this.compilersArtefactsPerFile[targetFile].getContracts() const artefact = this.compilersArtefactsPerFile[targetFile]
const contracts = artefact.getContracts()
Object.keys(contracts).map((file) => { Object.keys(contracts).map((file) => {
if (filter(file, contracts[file])) contractsData[file] = contracts[file] if (filter(file, contracts[file], artefact)) contractsData[file] = contracts[file]
}) })
}) })
// making sure we save last compilation result in there // making sure we save last compilation result in there
if (this.compilersArtefacts.__last) { if (this.compilersArtefacts.__last) {
const contracts = this.compilersArtefacts.__last.getContracts() const contracts = this.compilersArtefacts.__last.getContracts()
Object.keys(contracts).map((file) => { Object.keys(contracts).map((file) => {
if (filter(file, contracts[file])) contractsData[file] = contracts[file] if (filter(file, contracts[file], this.compilersArtefacts.__last)) contractsData[file] = contracts[file]
}) })
} }
return contractsData return contractsData
@ -194,8 +195,20 @@ export class CompilerArtefacts extends Plugin {
} }
} }
getCompilerAbstract (file) { async getCompilerAbstract (file) {
return this.compilersArtefactsPerFile[file] if (!file) return null
if (this.compilersArtefactsPerFile[file]) return this.compilersArtefactsPerFile[file]
const path = await this.call('fileManager', 'getPathFromUrl', file)
if (path && path.file && this.compilersArtefactsPerFile[path.file]) return this.compilersArtefactsPerFile[path.file]
let artefact = null
this.filterAllContractDatas((localFile, data, parentArtefact) => {
if (localFile === file || (path && path.file && localFile === path.file)) {
artefact = parentArtefact
}
})
return artefact
} }
addResolvedContract (address: string, compilerData: CompilerAbstract) { addResolvedContract (address: string, compilerData: CompilerAbstract) {
@ -212,12 +225,16 @@ export class CompilerArtefacts extends Plugin {
async getContractDataFromAddress (address) { async getContractDataFromAddress (address) {
const code = await this.call('blockchain', 'getCode', address) const code = await this.call('blockchain', 'getCode', address)
return this.getContractDataFromByteCode(code)
}
async getContractDataFromByteCode (code) {
let found let found
this.filterAllContractDatas((file, contractsData) => { this.filterAllContractDatas((file, contractsData) => {
for (const name of Object.keys(contractsData)) { for (const name of Object.keys(contractsData)) {
const contract = contractsData[name] const contract = contractsData[name]
if (util.compareByteCode(code, '0x' + contract.evm.deployedBytecode.object)) { if (util.compareByteCode(code, '0x' + contract.evm.deployedBytecode.object)) {
found = { name, contract } found = { name, contract, file }
return true return true
} }
} }

@ -43,7 +43,14 @@ export class FetchAndCompile extends Plugin {
async resolve (contractAddress, codeAtAddress, targetPath) { async resolve (contractAddress, codeAtAddress, targetPath) {
contractAddress = toChecksumAddress(contractAddress) contractAddress = toChecksumAddress(contractAddress)
const localCompilation = async () => await this.call('compilerArtefacts', 'get', contractAddress) ? await this.call('compilerArtefacts', 'get', contractAddress) : await this.call('compilerArtefacts', 'get', '__last') ? await this.call('compilerArtefacts', 'get', '__last') : null const localCompilation = async () => {
const contractData = await this.call('compilerArtefacts', 'getContractDataFromByteCode', codeAtAddress)
if (contractData) {
return await this.call('compilerArtefacts', 'getCompilerAbstract', contractData.file)
}
else
return await this.call('compilerArtefacts', 'get', '__last')
}
const resolved = await this.call('compilerArtefacts', 'get', contractAddress) const resolved = await this.call('compilerArtefacts', 'get', contractAddress)
if (resolved) return resolved if (resolved) return resolved
@ -87,20 +94,19 @@ export class FetchAndCompile extends Plugin {
return localCompilation() return localCompilation()
} }
if (!network) return localCompilation() if (!network) return localCompilation()
if (!this.sourceVerifierNetWork.includes(network.name)) return localCompilation() if (!this.sourceVerifierNetWork.includes(network.name)) {
// check if the contract if part of the local compilation result
// check if the contract if part of the local compilation result const compilation = await localCompilation()
const compilation = await localCompilation() if (compilation) {
if (compilation) { let found = false
let found = false compilation.visitContracts((contract) => {
compilation.visitContracts((contract) => { found = util.compareByteCode(codeAtAddress, '0x' + contract.object.evm.deployedBytecode.object)
found = util.compareByteCode('0x' + contract.object.evm.deployedBytecode.object, codeAtAddress) return found
return found })
}) if (found) {
if (found) { await this.call('compilerArtefacts', 'addResolvedContract', contractAddress, compilation)
await this.call('compilerArtefacts', 'addResolvedContract', contractAddress, compilation) return compilation
setTimeout(_ => this.emit('usingLocalCompilation', contractAddress), 0) }
return compilation
} }
} }

@ -20,7 +20,8 @@ export interface ContractData {
getConstructorInterface: () => any, getConstructorInterface: () => any,
getConstructorInputs: () => any, getConstructorInputs: () => any,
isOverSizeLimit: () => boolean, isOverSizeLimit: () => boolean,
metadata: any metadata: any,
contractName?: string
} }
export interface ContractAST { export interface ContractAST {

@ -1,6 +1,6 @@
{ {
"name": "@remix-project/remix-debug", "name": "@remix-project/remix-debug",
"version": "0.5.23", "version": "0.5.24",
"description": "Tool to debug Ethereum transactions", "description": "Tool to debug Ethereum transactions",
"contributors": [ "contributors": [
{ {
@ -26,10 +26,10 @@
"@ethereumjs/tx": "^4.0.2", "@ethereumjs/tx": "^4.0.2",
"@ethereumjs/util": "^8.0.3", "@ethereumjs/util": "^8.0.3",
"@ethereumjs/vm": "^6.3.0", "@ethereumjs/vm": "^6.3.0",
"@remix-project/remix-astwalker": "^0.0.53", "@remix-project/remix-astwalker": "^0.0.54",
"@remix-project/remix-lib": "^0.5.23", "@remix-project/remix-lib": "^0.5.24",
"@remix-project/remix-simulator": "^0.2.23", "@remix-project/remix-simulator": "^0.2.24",
"@remix-project/remix-solidity": "^0.5.9", "@remix-project/remix-solidity": "^0.5.10",
"ansi-gray": "^0.1.1", "ansi-gray": "^0.1.1",
"async": "^2.6.2", "async": "^2.6.2",
"color-support": "^1.1.3", "color-support": "^1.1.3",
@ -69,6 +69,6 @@
}, },
"homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-debug#readme", "homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-debug#readme",
"typings": "src/index.d.ts", "typings": "src/index.d.ts",
"gitHead": "69af4eddd1ba47f76a71c78077d453deb572b1a0", "gitHead": "c1415e0c3751af8bf53292d67fedf68e15bf3b23",
"types": "./src/index.d.ts" "types": "./src/index.d.ts"
} }

@ -1,6 +1,6 @@
{ {
"name": "@remix-project/remix-lib", "name": "@remix-project/remix-lib",
"version": "0.5.23", "version": "0.5.24",
"description": "Library to various Remix tools", "description": "Library to various Remix tools",
"contributors": [ "contributors": [
{ {
@ -17,8 +17,8 @@
"test": "./../../node_modules/.bin/ts-node --require tsconfig-paths/register ./../../node_modules/.bin/tape ./test/tests.ts" "test": "./../../node_modules/.bin/ts-node --require tsconfig-paths/register ./../../node_modules/.bin/tape ./test/tests.ts"
}, },
"dependencies": { "dependencies": {
"async": "^2.1.2",
"@ethereumjs/util": "^8.0.3", "@ethereumjs/util": "^8.0.3",
"async": "^2.1.2",
"ethers": "^4.0.40", "ethers": "^4.0.40",
"ethjs-util": "^0.1.6", "ethjs-util": "^0.1.6",
"events": "^3.0.0", "events": "^3.0.0",
@ -51,6 +51,6 @@
}, },
"homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-lib#readme", "homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-lib#readme",
"typings": "src/index.d.ts", "typings": "src/index.d.ts",
"gitHead": "69af4eddd1ba47f76a71c78077d453deb572b1a0", "gitHead": "c1415e0c3751af8bf53292d67fedf68e15bf3b23",
"types": "./src/index.d.ts" "types": "./src/index.d.ts"
} }

@ -258,6 +258,12 @@ export function compareByteCode (code1, code2) {
code2 = extractcborMetadata(code2) code2 = extractcborMetadata(code2)
if (code1 && code2) { if (code1 && code2) {
if (code1.length !== code2.length) {
// if the length isn't the same, we have an issue with extracting the metadata hash.
const minLength = code1.length > code2.length ? code2.length: code1.length
code1 = code1.substr(0, minLength - 10)
code2 = code2.substr(0, minLength - 10)
}
const compare = stringSimilarity.compareTwoStrings(code1, code2) const compare = stringSimilarity.compareTwoStrings(code1, code2)
return compare == 1 return compare == 1
} }
@ -294,12 +300,12 @@ function removeByIndex (code, index, length, emptyRef) {
function removeImmutableReference (code1, code2) { function removeImmutableReference (code1, code2) {
try { try {
const refOccurence = code2.match(/7f000000000000000000000000000000000000000000000000000000000000000073/g) const refOccurence = code2.match(/7f0000000000000000000000000000000000000000000000000000000000000000/g)
if (!refOccurence) return code1 if (!refOccurence) return code1
let offset = 0 let offset = 0
refOccurence.map((value) => { refOccurence.map((value) => {
offset = code2.indexOf(value, offset) offset = code2.indexOf(value, offset)
code1 = removeByIndex(code1, offset, value.length, '7f000000000000000000000000000000000000000000000000000000000000000073') code1 = removeByIndex(code1, offset, value.length, '7f0000000000000000000000000000000000000000000000000000000000000000')
offset = offset + 1 offset = offset + 1
}) })
} catch (e) { } catch (e) {

File diff suppressed because one or more lines are too long

@ -1,6 +1,6 @@
{ {
"name": "@remix-project/remix-simulator", "name": "@remix-project/remix-simulator",
"version": "0.2.23", "version": "0.2.24",
"description": "Ethereum IDE and tools for the web", "description": "Ethereum IDE and tools for the web",
"contributors": [ "contributors": [
{ {
@ -22,7 +22,7 @@
"@ethereumjs/tx": "^4.0.2", "@ethereumjs/tx": "^4.0.2",
"@ethereumjs/util": "^8.0.3", "@ethereumjs/util": "^8.0.3",
"@ethereumjs/vm": "^6.3.0", "@ethereumjs/vm": "^6.3.0",
"@remix-project/remix-lib": "^0.5.23", "@remix-project/remix-lib": "^0.5.24",
"ansi-gray": "^0.1.1", "ansi-gray": "^0.1.1",
"async": "^3.1.0", "async": "^3.1.0",
"body-parser": "^1.18.2", "body-parser": "^1.18.2",
@ -67,6 +67,6 @@
}, },
"homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-simulator#readme", "homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-simulator#readme",
"typings": "src/index.d.ts", "typings": "src/index.d.ts",
"gitHead": "69af4eddd1ba47f76a71c78077d453deb572b1a0", "gitHead": "c1415e0c3751af8bf53292d67fedf68e15bf3b23",
"types": "./src/index.d.ts" "types": "./src/index.d.ts"
} }

@ -23,7 +23,7 @@ export class Provider {
constructor (options: Record<string, string | number> = {}) { constructor (options: Record<string, string | number> = {}) {
this.options = options this.options = options
this.connected = true this.connected = true
this.vmContext = new VMContext(options['fork'] as string, options['nodeUrl'] as string, options['blockNumber'] as number) this.vmContext = new VMContext(options['fork'] as string, options['nodeUrl'] as string, options['blockNumber'] as (number | 'latest'))
this.Accounts = new Web3Accounts(this.vmContext) this.Accounts = new Web3Accounts(this.vmContext)
this.Transactions = new Transactions(this.vmContext) this.Transactions = new Transactions(this.vmContext)

@ -168,7 +168,6 @@ export class VMContext {
async createVm (hardfork) { async createVm (hardfork) {
let stateManager: StateManager let stateManager: StateManager
console.log('creating a new VM', hardfork, this.nodeUrl, this.blockNumber)
if (this.nodeUrl) { if (this.nodeUrl) {
let block = this.blockNumber let block = this.blockNumber
if (this.blockNumber === 'latest') { if (this.blockNumber === 'latest') {

@ -1,6 +1,6 @@
{ {
"name": "@remix-project/remix-solidity", "name": "@remix-project/remix-solidity",
"version": "0.5.9", "version": "0.5.10",
"description": "Tool to load and run Solidity compiler", "description": "Tool to load and run Solidity compiler",
"main": "src/index.js", "main": "src/index.js",
"types": "src/index.d.ts", "types": "src/index.d.ts",
@ -19,7 +19,7 @@
"@ethereumjs/tx": "^4.0.2", "@ethereumjs/tx": "^4.0.2",
"@ethereumjs/util": "^8.0.3", "@ethereumjs/util": "^8.0.3",
"@ethereumjs/vm": "^6.3.0", "@ethereumjs/vm": "^6.3.0",
"@remix-project/remix-lib": "^0.5.23", "@remix-project/remix-lib": "^0.5.24",
"async": "^2.6.2", "async": "^2.6.2",
"eslint-scope": "^5.0.0", "eslint-scope": "^5.0.0",
"ethers": "^5.4.2", "ethers": "^5.4.2",
@ -57,5 +57,5 @@
}, },
"homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-solidity#readme", "homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-solidity#readme",
"typings": "src/index.d.ts", "typings": "src/index.d.ts",
"gitHead": "69af4eddd1ba47f76a71c78077d453deb572b1a0" "gitHead": "c1415e0c3751af8bf53292d67fedf68e15bf3b23"
} }

@ -1,6 +1,6 @@
{ {
"name": "@remix-project/remix-tests", "name": "@remix-project/remix-tests",
"version": "0.2.23", "version": "0.2.24",
"description": "Tool to test Solidity smart contracts", "description": "Tool to test Solidity smart contracts",
"main": "src/index.js", "main": "src/index.js",
"types": "./src/index.d.ts", "types": "./src/index.d.ts",
@ -41,9 +41,9 @@
"@ethereumjs/tx": "^4.0.2", "@ethereumjs/tx": "^4.0.2",
"@ethereumjs/util": "^8.0.3", "@ethereumjs/util": "^8.0.3",
"@ethereumjs/vm": "^6.3.0", "@ethereumjs/vm": "^6.3.0",
"@remix-project/remix-lib": "^0.5.23", "@remix-project/remix-lib": "^0.5.24",
"@remix-project/remix-simulator": "^0.2.23", "@remix-project/remix-simulator": "^0.2.24",
"@remix-project/remix-solidity": "^0.5.9", "@remix-project/remix-solidity": "^0.5.10",
"@remix-project/remix-url-resolver": "^0.0.42", "@remix-project/remix-url-resolver": "^0.0.42",
"ansi-gray": "^0.1.1", "ansi-gray": "^0.1.1",
"async": "^2.6.0", "async": "^2.6.0",
@ -78,5 +78,5 @@
"typescript": "^3.3.1" "typescript": "^3.3.1"
}, },
"typings": "src/index.d.ts", "typings": "src/index.d.ts",
"gitHead": "69af4eddd1ba47f76a71c78077d453deb572b1a0" "gitHead": "c1415e0c3751af8bf53292d67fedf68e15bf3b23"
} }

@ -30,7 +30,7 @@ export const CustomIconsToggle = React.forwardRef(({ onClick, icon, className =
className={`${className.replace('dropdown-toggle', '')} mb-0 pb-0 d-flex justify-content-end align-items-end remixuimenuicon_shadow fs-3`} className={`${className.replace('dropdown-toggle', '')} mb-0 pb-0 d-flex justify-content-end align-items-end remixuimenuicon_shadow fs-3`}
data-id="workspaceMenuDropdown" data-id="workspaceMenuDropdown"
> >
{ icon && <i style={{ fontSize: 'large' }} className={`${icon}`} data-icon="workspaceDropdownMenuIcon"></i> } { icon && <i style={{ fontSize: 'large' }} className={`${icon}`} data-id="workspaceDropdownMenuIcon"></i> }
</span> </span>
)) ))
@ -48,7 +48,7 @@ export const CustomMenu = React.forwardRef(
> >
<ul className="overflow-auto list-unstyled mb-0" style={{ maxHeight: height+'px' }}> <ul className="overflow-auto list-unstyled mb-0" style={{ maxHeight: height+'px' }}>
{ {
children children
} }
</ul> </ul>
</div> </div>

@ -119,7 +119,7 @@ export const upgradeWithProxyMsg = () => (
export const unavailableProxyLayoutMsg = () => ( export const unavailableProxyLayoutMsg = () => (
<div> <div>
<p>Previous contract implementation is NOT available for upgrade comparison. <br /> A new storage layout will be saved for future upgrades.</p> <p>The previous contract implementation is NOT available for an upgrade comparison<br /> A new storage layout will be saved for future upgrades.</p>
</div> </div>
) )

@ -68,7 +68,7 @@ function HomeTabTitle() {
></audio> ></audio>
</div> </div>
</div> </div>
<span> <span className="d-flex flex-nowrap">
<CustomTooltip <CustomTooltip
placement={'top'} placement={'top'}
tooltipId="overlay-tooltip" tooltipId="overlay-tooltip"
@ -81,7 +81,7 @@ function HomeTabTitle() {
openLink("https://www.youtube.com/channel/UCjTUPyFEr2xDGN6Cg8nKDaA") openLink("https://www.youtube.com/channel/UCjTUPyFEr2xDGN6Cg8nKDaA")
_paq.push(['trackEvent', 'hometab', 'socialMedia', 'youtube']) _paq.push(['trackEvent', 'hometab', 'socialMedia', 'youtube'])
}} }}
className="border-0 h-100 btn fab fa-youtube"> className="border-0 h-100 btn fab fa-youtube p-1 pl-2">
</button> </button>
</CustomTooltip> </CustomTooltip>
@ -97,7 +97,7 @@ function HomeTabTitle() {
openLink("https://twitter.com/EthereumRemix") openLink("https://twitter.com/EthereumRemix")
_paq.push(['trackEvent', 'hometab', 'socialMedia', 'twitter']) _paq.push(['trackEvent', 'hometab', 'socialMedia', 'twitter'])
}} }}
className="border-0 p-2 h-100 pl-2 btn fab fa-twitter"> className="border-0 p-1 h-100 pl-2 btn fab fa-twitter">
</button> </button>
</CustomTooltip> </CustomTooltip>
@ -113,7 +113,7 @@ function HomeTabTitle() {
openLink("https://www.linkedin.com/company/ethereum-remix/") openLink("https://www.linkedin.com/company/ethereum-remix/")
_paq.push(['trackEvent', 'hometab', 'socialmedia', 'linkedin']) _paq.push(['trackEvent', 'hometab', 'socialmedia', 'linkedin'])
}} }}
className="border-0 p-2 h-100 pl-2 btn fa fa-linkedin"> className="border-0 p-1 h-100 pl-2 btn fa fa-linkedin">
</button> </button>
</CustomTooltip> </CustomTooltip>
@ -129,7 +129,7 @@ function HomeTabTitle() {
openLink("https://medium.com/remix-ide") openLink("https://medium.com/remix-ide")
_paq.push(['trackEvent', 'hometab', 'socialmedia', 'medium']) _paq.push(['trackEvent', 'hometab', 'socialmedia', 'medium'])
}} }}
className="border-0 p-2 h-100 pl-2 btn fab fa-medium"> className="border-0 p-1 h-100 pl-2 btn fab fa-medium">
</button> </button>
</CustomTooltip> </CustomTooltip>
@ -145,7 +145,7 @@ function HomeTabTitle() {
openLink("https://discord.gg/mh9hFCKkEq") openLink("https://discord.gg/mh9hFCKkEq")
_paq.push(['trackEvent', 'hometab', 'socialmedia', 'discord']) _paq.push(['trackEvent', 'hometab', 'socialmedia', 'discord'])
}} }}
className="border-0 h-100 p-2 btn fab fa-discord"> className="border-0 h-100 p-1 pr-2 btn fab fa-discord">
</button> </button>
</CustomTooltip> </CustomTooltip>
</span> </span>

@ -98,7 +98,7 @@ export const ModalDialog = (props: ModalDialogProps) => {
{/* todo add autofocus ^^ */} {/* todo add autofocus ^^ */}
{ props.okLabel && <button { props.okLabel && <button
data-id={`${props.id}-modal-footer-ok-react`} data-id={`${props.id}-modal-footer-ok-react`}
className={'modal-ok btn btn-sm ' + (props.okBtnClass ? props.okBtnClass : state.toggleBtn ? 'btn-dark' : 'btn-light')} className={'modal-ok btn btn-sm ' + (props.okBtnClass ? props.okBtnClass : state.toggleBtn ? 'border-primary' : 'border-secondary')}
disabled={props.validation && !props.validation.valid} disabled={props.validation && !props.validation.valid}
onClick={() => { onClick={() => {
if (props.validation && !props.validation.valid) return if (props.validation && !props.validation.valid) return
@ -111,7 +111,7 @@ export const ModalDialog = (props: ModalDialogProps) => {
} }
{ props.cancelLabel && <button { props.cancelLabel && <button
data-id={`${props.id}-modal-footer-cancel-react`} data-id={`${props.id}-modal-footer-cancel-react`}
className={'modal-cancel btn btn-sm ' + (props.cancelBtnClass ? props.cancelBtnClass : state.toggleBtn ? 'btn-light' : 'btn-dark')} className={'modal-cancel btn btn-sm ' + (props.cancelBtnClass ? props.cancelBtnClass : state.toggleBtn ? 'border-secondary' : 'border-primary')}
data-dismiss="modal" data-dismiss="modal"
onClick={() => { onClick={() => {
if (props.cancelFn) props.cancelFn() if (props.cancelFn) props.cancelFn()

@ -62,7 +62,7 @@ const PermissionHandlerDialog = (props: PermissionHandlerProps) => {
<label htmlFor='remember' className="form-check-label" data-id="permissionHandlerRememberChoice"><FormattedMessage id='permissionHandler.rememberThisChoice' /></label> <label htmlFor='remember' className="form-check-label" data-id="permissionHandlerRememberChoice"><FormattedMessage id='permissionHandler.rememberThisChoice' /></label>
</div> </div>
} }
<button className="btn btn-sm" onClick={reset}><FormattedMessage id='permissionHandler.resetAllPermissions' /></button> <button className="btn-secondary btn-sm" onClick={reset}><FormattedMessage id='permissionHandler.resetAllPermissions' /></button>
</article> </article>
<div>{feedback}</div> <div>{feedback}</div>
</section>) </section>)

@ -235,7 +235,7 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
} }
const isValidProxyUpgrade = (proxyAddress: string) => { const isValidProxyUpgrade = (proxyAddress: string) => {
return props.isValidProxyUpgrade(proxyAddress, loadedContractData.name, loadedContractData.compiler.source, loadedContractData.compiler.data) return props.isValidProxyUpgrade(proxyAddress, loadedContractData.contractName || loadedContractData.name, loadedContractData.compiler.source, loadedContractData.compiler.data)
} }
const checkSumWarning = () => { const checkSumWarning = () => {
@ -319,6 +319,7 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
isValidProxyAddress={props.isValidProxyAddress} isValidProxyAddress={props.isValidProxyAddress}
isValidProxyUpgrade={isValidProxyUpgrade} isValidProxyUpgrade={isValidProxyUpgrade}
modal={props.modal} modal={props.modal}
disabled={props.selectedAccount === ''}
/> />
<div className="d-flex py-1 align-items-center custom-control custom-checkbox"> <div className="d-flex py-1 align-items-center custom-control custom-checkbox">
<input <input

@ -280,7 +280,7 @@ export function ContractGUI (props: ContractGUIProps) {
className={`udapp_instanceButton ${props.widthClass} btn btn-sm ${buttonOptions.classList}`} className={`udapp_instanceButton ${props.widthClass} btn btn-sm ${buttonOptions.classList}`}
data-id={buttonOptions.dataId} data-id={buttonOptions.dataId}
data-title={buttonOptions.title} data-title={buttonOptions.title}
disabled={toggleUpgradeImp && !proxyAddress} disabled={(toggleUpgradeImp && !proxyAddress) || props.disabled}
> >
<CustomTooltip <CustomTooltip
placement={"right-start"} placement={"right-start"}
@ -415,6 +415,7 @@ export function ContractGUI (props: ContractGUIProps) {
onClick={handleExpandMultiClick} onClick={handleExpandMultiClick}
data-id={buttonOptions.dataId} data-id={buttonOptions.dataId}
className={`udapp_instanceButton ${buttonOptions.classList}`} className={`udapp_instanceButton ${buttonOptions.classList}`}
disabled={props.disabled}
> >
{buttonOptions.content} {buttonOptions.content}
</button> </button>
@ -537,6 +538,7 @@ export function ContractGUI (props: ContractGUIProps) {
tooltipClasses="text-nowrap" tooltipClasses="text-nowrap"
tooltipId={`proxyAddressTooltip${index}`} tooltipId={`proxyAddressTooltip${index}`}
tooltipText={'Deployed ' + shortenDate(deployment.date)} tooltipText={'Deployed ' + shortenDate(deployment.date)}
key={index}
> >
<Dropdown.Item <Dropdown.Item
key={index} key={index}

@ -44,7 +44,7 @@ export function InstanceContainerUI (props: InstanceContainerProps) {
{ instanceList.length > 0 { instanceList.length > 0
? <div> { props.instances.instanceList.map((instance, index) => { ? <div> { props.instances.instanceList.map((instance, index) => {
return <UniversalDappUI return <UniversalDappUI
key={instance.address} key={index}
instance={instance} instance={instance}
context={props.getContext()} context={props.getContext()}
removeInstance={props.removeInstance} removeInstance={props.removeInstance}

@ -223,6 +223,7 @@ export function RunTabUI (props: RunTabProps) {
passphrase={runTab.passphrase} passphrase={runTab.passphrase}
/> />
<ContractDropdownUI <ContractDropdownUI
selectedAccount={runTab.accounts.selectedAccount}
syncContracts={syncContracts} syncContracts={syncContracts}
exEnvironment={runTab.selectExEnv} exEnvironment={runTab.selectExEnv}
contracts={runTab.contracts} contracts={runTab.contracts}

@ -224,6 +224,7 @@ export type MainnetPrompt = (
) => JSX.Element ) => JSX.Element
export interface ContractDropdownProps { export interface ContractDropdownProps {
selectedAccount: string,
exEnvironment: string, exEnvironment: string,
contracts: { contracts: {
contractList: ContractList, contractList: ContractList,

@ -188,7 +188,8 @@ export const ContractSelection = (props: ContractSelectionProps) => {
} }
const copyBytecode = () => { const copyBytecode = () => {
return copyContractProperty('bytecode') const bytecodeObj = JSON.parse(copyContractProperty('bytecode'))
return bytecodeObj.object
} }
return ( return (
@ -244,14 +245,14 @@ export const ContractSelection = (props: ContractSelectionProps) => {
<div className="remixui_contractHelperButtons"> <div className="remixui_contractHelperButtons">
<div className="input-group"> <div className="input-group">
<div className="btn-group" role="group" aria-label="Copy to Clipboard"> <div className="btn-group" role="group" aria-label="Copy to Clipboard">
<CopyToClipboard title="Copy ABI to clipboard" getContent={copyABI} direction='top'> <CopyToClipboard tip="Copy ABI to clipboard" getContent={copyABI} direction='top'>
<button className="btn remixui_copyButton" title="Copy ABI to clipboard"> <button className="btn remixui_copyButton" >
<i className="remixui_copyIcon far fa-copy" aria-hidden="true"></i> <i className="remixui_copyIcon far fa-copy" aria-hidden="true"></i>
<span>ABI</span> <span>ABI</span>
</button> </button>
</CopyToClipboard> </CopyToClipboard>
<CopyToClipboard title="Copy ABI to clipboard" getContent={copyBytecode} direction='top'> <CopyToClipboard tip="Copy Bytecode to clipboard" getContent={copyBytecode} direction='top'>
<button className="btn remixui_copyButton" title="Copy Bytecode to clipboard"> <button className="btn remixui_copyButton">
<i className="remixui_copyIcon far fa-copy" aria-hidden="true"></i> <i className="remixui_copyIcon far fa-copy" aria-hidden="true"></i>
<span>Bytecode</span> <span>Bytecode</span>
</button> </button>

@ -311,6 +311,14 @@ export const deleteWorkspace = async (workspaceName: string, cb?: (err: Error, r
cb && cb(null, workspaceName) cb && cb(null, workspaceName)
} }
export const deleteAllWorkspaces = async () => {
await (await getWorkspaces()).map(async workspace => {
await deleteWorkspaceFromProvider(workspace.name)
await dispatch(setDeleteWorkspace(workspace.name))
plugin.workspaceDeleted(workspace.name)
})
}
const deleteWorkspaceFromProvider = async (workspaceName: string) => { const deleteWorkspaceFromProvider = async (workspaceName: string) => {
const workspacesPath = plugin.fileProviders.workspace.workspacesPath const workspacesPath = plugin.fileProviders.workspace.workspacesPath
@ -450,7 +458,6 @@ export const cloneRepository = async (url: string) => {
if (!isActive) await plugin.call('manager', 'activatePlugin', 'dgit') if (!isActive) await plugin.call('manager', 'activatePlugin', 'dgit')
await fetchWorkspaceDirectory(ROOT_PATH) await fetchWorkspaceDirectory(ROOT_PATH)
const workspacesPath = plugin.fileProviders.workspace.workspacesPath const workspacesPath = plugin.fileProviders.workspace.workspacesPath
console.log('go in to promise')
const branches = await getGitRepoBranches(workspacesPath + '/' + repoName) const branches = await getGitRepoBranches(workspacesPath + '/' + repoName)
dispatch(setCurrentWorkspaceBranches(branches)) dispatch(setCurrentWorkspaceBranches(branches))
@ -481,7 +488,6 @@ export const cloneRepository = async (url: string) => {
} }
} }
export const checkGit = async () => { export const checkGit = async () => {
const isGitRepo = await plugin.fileManager.isGitRepo() const isGitRepo = await plugin.fileManager.isGitRepo()
dispatch(setCurrentWorkspaceIsGitRepo(isGitRepo)) dispatch(setCurrentWorkspaceIsGitRepo(isGitRepo))

@ -0,0 +1,51 @@
import React from 'react'
import { CustomTooltip } from '@remix-ui/helper'
import { Dropdown } from 'react-bootstrap'
import { FormattedMessage } from 'react-intl'
const _paq = window._paq = window._paq || []
export interface HamburgerMenuItemProps {
hideOption: boolean
kind: string
actionOnClick: () => void
fa: string
}
export function HamburgerMenuItem (props: HamburgerMenuItemProps) {
const { hideOption } = props
const uid = 'workspace' + props.kind
return (
<>
<Dropdown.Item>
<CustomTooltip
placement="right-start"
tooltipId={uid + "Tooltip"}
tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id={'filePanel.workspace.' + props.kind} />}
>
<div
data-id={uid}
key={uid + '-fe-ws'}
onClick={() => {
props.actionOnClick()
_paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', {uid}])
}}
>
<span
hidden={hideOption}
id={uid}
data-id={uid}
className={props.fa + ' pl-2'}
style={{width: '1.4rem'}}
>
</span>
<span className="px-2">
<FormattedMessage id={'filePanel.' + props.kind } />
</span>
</div>
</CustomTooltip>
</Dropdown.Item>
</>
)
}

@ -1,13 +1,11 @@
import React from 'react' import React from 'react'
import { CustomTooltip } from '@remix-ui/helper'
import { Dropdown } from 'react-bootstrap' import { Dropdown } from 'react-bootstrap'
import { FormattedMessage } from 'react-intl' import { HamburgerMenuItem } from './workspace-hamburger-item'
const _paq = window._paq = window._paq || []
export interface HamburgerMenuProps { export interface HamburgerMenuProps {
createWorkspace: () => void, createWorkspace: () => void,
deleteCurrentWorkspace: () => void, deleteCurrentWorkspace: () => void,
deleteAllWorkspaces: () => void,
renameCurrentWorkspace: () => void, renameCurrentWorkspace: () => void,
cloneGitRepository: () => void, cloneGitRepository: () => void,
downloadWorkspaces: () => void, downloadWorkspaces: () => void,
@ -23,294 +21,51 @@ export interface HamburgerMenuProps {
export function HamburgerMenu (props: HamburgerMenuProps) { export function HamburgerMenu (props: HamburgerMenuProps) {
const { showIconsMenu, hideWorkspaceOptions, hideLocalhostOptions } = props const { showIconsMenu, hideWorkspaceOptions, hideLocalhostOptions } = props
return ( return (
<> <>
<Dropdown.Item> <HamburgerMenuItem kind='create' fa='far fa-plus' hideOption={hideWorkspaceOptions} actionOnClick={() => {
<CustomTooltip props.createWorkspace()
placement="right" props.hideIconsMenu(!showIconsMenu)
tooltipId="createWorkspaceTooltip" }}></HamburgerMenuItem>
tooltipClasses="text-nowrap" <HamburgerMenuItem kind='delete' fa='far fa-trash' hideOption={hideWorkspaceOptions || hideLocalhostOptions} actionOnClick={() => {
tooltipText={<FormattedMessage id='filePanel.workspace.create' />} props.deleteCurrentWorkspace()
> props.hideIconsMenu(!showIconsMenu)
<div }}></HamburgerMenuItem>
data-id='workspaceCreate' <HamburgerMenuItem kind='deleteAll' fa='far fa-trash-alt' hideOption={hideWorkspaceOptions || hideLocalhostOptions} actionOnClick={() => {
onClick={() => { props.deleteAllWorkspaces()
props.createWorkspace() props.hideIconsMenu(!showIconsMenu)
_paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'workspaceCreate']) }}></HamburgerMenuItem>
props.hideIconsMenu(!showIconsMenu) <HamburgerMenuItem kind='rename' fa='far fa-edit' hideOption={hideWorkspaceOptions || hideLocalhostOptions} actionOnClick={() => {
}} props.renameCurrentWorkspace()
key={`workspacesCreate-fe-ws`} props.hideIconsMenu(!showIconsMenu)
> }}></HamburgerMenuItem>
<span <Dropdown.Divider className="border mb-0 mt-0 remixui_menuhr" style={{ pointerEvents: 'none' }} />
hidden={hideWorkspaceOptions} <HamburgerMenuItem kind='clone' fa='fab fa-github' hideOption={hideWorkspaceOptions} actionOnClick={() => {
id='workspaceCreate' props.cloneGitRepository()
data-id='workspaceCreate' props.hideIconsMenu(!showIconsMenu)
onClick={() => { }}></HamburgerMenuItem>
props.createWorkspace() <Dropdown.Divider className="border mt-0 mb-0 remixui_menuhr" style={{ pointerEvents: 'none' }}/>
_paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'workspaceCreate']) <HamburgerMenuItem kind='download' fa='far fa-download' hideOption={hideWorkspaceOptions || hideLocalhostOptions} actionOnClick={() => {
props.hideIconsMenu(!showIconsMenu) props.downloadWorkspaces()
}} props.hideIconsMenu(!showIconsMenu)
className='far fa-plus pl-2' }}></HamburgerMenuItem>
> <HamburgerMenuItem kind='restore' fa='far fa-upload' hideOption={hideWorkspaceOptions} actionOnClick={() => {
</span> props.restoreBackup()
<span className="pl-3"><FormattedMessage id='filePanel.create' /></span> props.hideIconsMenu(!showIconsMenu)
</div> }}></HamburgerMenuItem>
</CustomTooltip> <Dropdown.Divider className="border mt-0 mb-0 remixui_menuhr" style={{ pointerEvents: 'none' }}/>
</Dropdown.Item> <HamburgerMenuItem kind='solghaction' fa='fak fa-solidity-mono' hideOption={hideWorkspaceOptions} actionOnClick={() => {
<Dropdown.Item> props.addGithubAction()
<CustomTooltip props.hideIconsMenu(!showIconsMenu)
placement="right-start" }}></HamburgerMenuItem>
tooltipId="createWorkspaceTooltip" <HamburgerMenuItem kind='tssoltestghaction' fa='fab fa-js' hideOption={hideWorkspaceOptions} actionOnClick={() => {
tooltipClasses="text-nowrap" props.addTsSolTestGithubAction()
tooltipText={<FormattedMessage id='filePanel.workspace.delete' />} props.hideIconsMenu(!showIconsMenu)
> }}></HamburgerMenuItem>
<div <HamburgerMenuItem kind='slitherghaction' fa='far fa-shield' hideOption={hideWorkspaceOptions} actionOnClick={() => {
data-id='workspaceDelete' props.addSlitherGithubAction()
onClick={() => { props.hideIconsMenu(!showIconsMenu)
props.deleteCurrentWorkspace() }}></HamburgerMenuItem>
_paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'workspaceDelete']) </>
props.hideIconsMenu(!showIconsMenu) )
}} }
key={`workspacesDelete-fe-ws`}
>
<span
hidden={ hideWorkspaceOptions || hideLocalhostOptions }
id='workspaceDelete'
data-id='workspaceDelete'
onClick={() => {
props.deleteCurrentWorkspace()
_paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'workspaceDelete'])
props.hideIconsMenu(!showIconsMenu)
}}
className='far fa-trash pl-2'
>
</span>
<span className="pl-3"><FormattedMessage id='filePanel.delete' /></span>
</div>
</CustomTooltip>
</Dropdown.Item>
<Dropdown.Item>
<CustomTooltip
placement='right-start'
tooltipClasses="text-nowrap"
tooltipId="workspaceRenametooltip"
tooltipText={<FormattedMessage id='filePanel.workspace.rename' />}
>
<div onClick={() => {
props.renameCurrentWorkspace()
_paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'workspaceRename'])
props.hideIconsMenu(!showIconsMenu)
}}
data-id='workspaceRename'
key={`workspacesRename-fe-ws`}
>
<span
hidden={ hideWorkspaceOptions || hideLocalhostOptions }
id='workspaceRename'
data-id='workspaceRename'
onClick={() => {
props.renameCurrentWorkspace()
_paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'workspaceRename'])
props.hideIconsMenu(!showIconsMenu)
}}
className='far fa-edit pl-2'>
</span>
<span className="pl-3"><FormattedMessage id='filePanel.rename' /></span>
</div>
</CustomTooltip>
</Dropdown.Item>
<Dropdown.Item><Dropdown.Divider className="border mb-0 mt-0" /></Dropdown.Item>
<Dropdown.Item>
<CustomTooltip
placement="right-start"
tooltipId="cloneWorkspaceTooltip"
tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id='filePanel.workspace.clone' />}
>
<div
data-id='cloneGitRepository'
onClick={() => {
props.cloneGitRepository()
_paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'cloneGitRepository'])
props.hideIconsMenu(!showIconsMenu)
}}
key={`cloneGitRepository-fe-ws`}
>
<span
hidden={ hideWorkspaceOptions }
id='cloneGitRepository'
data-id='cloneGitRepository'
onClick={() => {
props.cloneGitRepository()
_paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'cloneGitRepository'])
props.hideIconsMenu(!showIconsMenu)
}}
className='fab fa-github pl-2'
>
</span>
<span className="pl-3"><FormattedMessage id='filePanel.clone' /></span>
</div>
</CustomTooltip>
</Dropdown.Item>
<Dropdown.Item><Dropdown.Divider className="border mt-0 mb-0 remixui_menuhr" style={{ pointerEvents: 'none' }}/></Dropdown.Item>
<Dropdown.Item>
<CustomTooltip
placement="right-start"
tooltipId="createWorkspaceTooltip"
tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id='filePanel.workspace.download' />}
>
<div
data-id='workspacesDownload'
onClick={() => {
props.downloadWorkspaces()
_paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'workspacesDownload'])
props.hideIconsMenu(!showIconsMenu)
}}
key={`workspacesDownload-fe-ws`}
>
<span
hidden={ hideWorkspaceOptions || hideLocalhostOptions }
id='workspacesDownload'
data-id='workspacesDownload'
onClick={() => {
props.downloadWorkspaces()
_paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'workspacesDownload'])
props.hideIconsMenu(!showIconsMenu)
}}
className='far fa-download pl-2 '
>
</span>
<span className="pl-3"><FormattedMessage id='filePanel.download' /></span>
</div>
</CustomTooltip>
</Dropdown.Item>
<Dropdown.Item>
<CustomTooltip
placement="right-start"
tooltipId="createWorkspaceTooltip"
tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id='filePanel.workspace.restore' />}
>
<div
data-id='workspacesRestore'
onClick={() => {
props.restoreBackup()
_paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'workspacesRestore'])
props.hideIconsMenu(!showIconsMenu)
}}
key={`workspacesRestore-fe-ws`}
>
<span
hidden={ hideWorkspaceOptions }
id='workspacesRestore'
data-id='workspacesRestore'
onClick={() => {
props.restoreBackup()
_paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'workspacesRestore'])
props.hideIconsMenu(!showIconsMenu)
}}
className='far fa-upload pl-2'
>
</span>
<span className="pl-3"><FormattedMessage id='filePanel.restore' /></span>
</div>
</CustomTooltip>
</Dropdown.Item>
<Dropdown.Item><Dropdown.Divider className="border mt-0 mb-0 remixui_menuhr" style={{ pointerEvents: 'none' }}/></Dropdown.Item>
<Dropdown.Item>
<CustomTooltip
placement="right-start"
tooltipId="createSolGHActionTooltip"
tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id='filePanel.workspace.solghaction' />}
>
<div
data-id='soliditygithubaction'
onClick={() => {
props.addGithubAction()
_paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'addSolidityTesting'])
props.hideIconsMenu(!showIconsMenu)
}}
>
<span
hidden={ hideWorkspaceOptions }
id='soliditygithubaction'
data-id='soliditygithubaction'
onClick={() => {
props.addGithubAction()
_paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'addSolidityTesting'])
props.hideIconsMenu(!showIconsMenu)
}}
className='fak fa-solidity-mono pl-2'
>
</span>
<span className="pl-3">{<FormattedMessage id='filePanel.solghaction' />}</span>
</div>
</CustomTooltip>
</Dropdown.Item>
<Dropdown.Item>
<CustomTooltip
placement="right-start"
tooltipId="createTsSolTestGHActionTooltip"
tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id='filePanel.workspace.tssoltestghaction' />}
>
<div
data-id='typescriptsoliditygithubtestaction'
onClick={() => {
props.addTsSolTestGithubAction()
_paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'addTsSolTestingAction'])
props.hideIconsMenu(!showIconsMenu)
}}
>
<span
hidden={ hideWorkspaceOptions }
id='tssoliditygithubaction'
data-id='tssoliditygithubaction'
onClick={() => {
props.addTsSolTestGithubAction()
_paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'addTsSolTestingAction'])
props.hideIconsMenu(!showIconsMenu)
}}
className='fab fa-js pl-2'
>
</span>
<span className="pl-3">{<FormattedMessage id='filePanel.tssoltestghaction' />}</span>
</div>
</CustomTooltip>
</Dropdown.Item>
<Dropdown.Item>
<CustomTooltip
placement="right-start"
tooltipId="createSlitherGHActionTooltip"
tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id='filePanel.workspace.slitherghaction' />}
>
<div
data-id='slithergithubtestaction'
onClick={() => {
props.addSlitherGithubAction()
_paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'addSlitherAction'])
props.hideIconsMenu(!showIconsMenu)
}}
>
<span
hidden={ hideWorkspaceOptions }
id='slithergithubaction'
data-id='slithergithubaction'
onClick={() => {
props.addSlitherGithubAction()
_paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'addSlitherAction'])
props.hideIconsMenu(!showIconsMenu)
}}
className='far fa-shield pl-2'
>
</span>
<span className="pl-3">{<FormattedMessage id='filePanel.slitherghaction' />}</span>
</div>
</CustomTooltip>
</Dropdown.Item>
</>
)
}

@ -15,6 +15,7 @@ export const FileSystemContext = createContext<{
dispatchSwitchToWorkspace: (name: string) => Promise<void>, dispatchSwitchToWorkspace: (name: string) => Promise<void>,
dispatchRenameWorkspace: (oldName: string, workspaceName: string) => Promise<void>, dispatchRenameWorkspace: (oldName: string, workspaceName: string) => Promise<void>,
dispatchDeleteWorkspace: (workspaceName: string) => Promise<void>, dispatchDeleteWorkspace: (workspaceName: string) => Promise<void>,
dispatchDeleteAllWorkspaces: () => Promise<void>,
dispatchPublishToGist: (path?: string, type?: string) => Promise<void>, dispatchPublishToGist: (path?: string, type?: string) => Promise<void>,
dispatchUploadFile: (target?: SyntheticEvent, targetFolder?: string) => Promise<void>, dispatchUploadFile: (target?: SyntheticEvent, targetFolder?: string) => Promise<void>,
dispatchCreateNewFile: (path: string, rootDir: string) => Promise<void>, dispatchCreateNewFile: (path: string, rootDir: string) => Promise<void>,

@ -115,21 +115,8 @@
min-width: 8rem; min-width: 8rem;
} }
.remixui_menuhr{
}
#workspacesMenuDropdown>div>ul>a:nth-child(6):hover {
background-color: rgba(0,0,0,0)
}
#workspacesMenuDropdown>div>ul>a:nth-child(4):hover {
background-color: rgba(0, 0, 0, 0)
}
#workspacesMenuDropdown > div > ul > a:hover { #workspacesMenuDropdown > div > ul > a:hover {
background-color: var(--secondary); background-color: var(--secondary);
/* border: 1px solid var(--secondary); */
border-radius: 2px; border-radius: 2px;
color: var(--text) color: var(--text)
} }

@ -5,7 +5,7 @@ import { Toaster } from '@remix-ui/toaster' // eslint-disable-line
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
import { FileSystemContext } from '../contexts' import { FileSystemContext } from '../contexts'
import { browserReducer, browserInitialState } from '../reducers/workspace' import { browserReducer, browserInitialState } from '../reducers/workspace'
import { initWorkspace, fetchDirectory, removeInputField, deleteWorkspace, clearPopUp, publishToGist, createNewFile, setFocusElement, createNewFolder, import { initWorkspace, fetchDirectory, removeInputField, deleteWorkspace, deleteAllWorkspaces, clearPopUp, publishToGist, createNewFile, setFocusElement, createNewFolder,
deletePath, renamePath, downloadPath, copyFile, copyFolder, runScript, emitContextMenuEvent, handleClickFile, handleExpandPath, addInputField, createWorkspace, deletePath, renamePath, downloadPath, copyFile, copyFolder, runScript, emitContextMenuEvent, handleClickFile, handleExpandPath, addInputField, createWorkspace,
fetchWorkspaceDirectory, renameWorkspace, switchToWorkspace, uploadFile, handleDownloadFiles, restoreBackupZip, cloneRepository, moveFile, moveFolder, fetchWorkspaceDirectory, renameWorkspace, switchToWorkspace, uploadFile, handleDownloadFiles, restoreBackupZip, cloneRepository, moveFile, moveFolder,
showAllBranches, switchBranch, createNewBranch, checkoutRemoteBranch, createSolidityGithubAction, createTsSolGithubAction, createSlitherGithubAction showAllBranches, switchBranch, createNewBranch, checkoutRemoteBranch, createSolidityGithubAction, createTsSolGithubAction, createSlitherGithubAction
@ -67,6 +67,10 @@ export const FileSystemProvider = (props: WorkspaceProps) => {
await deleteWorkspace(workspaceName) await deleteWorkspace(workspaceName)
} }
const dispatchDeleteAllWorkspaces = async () => {
await deleteAllWorkspaces()
}
const dispatchPublishToGist = async (path?: string, type?: string) => { const dispatchPublishToGist = async (path?: string, type?: string) => {
await publishToGist(path, type) await publishToGist(path, type)
} }
@ -258,6 +262,7 @@ export const FileSystemProvider = (props: WorkspaceProps) => {
dispatchSwitchToWorkspace, dispatchSwitchToWorkspace,
dispatchRenameWorkspace, dispatchRenameWorkspace,
dispatchDeleteWorkspace, dispatchDeleteWorkspace,
dispatchDeleteAllWorkspaces,
dispatchPublishToGist, dispatchPublishToGist,
dispatchUploadFile, dispatchUploadFile,
dispatchCreateNewFile, dispatchCreateNewFile,

@ -73,11 +73,11 @@ export function Workspace () {
}, [currentWorkspace]) }, [currentWorkspace])
const renameCurrentWorkspace = () => { const renameCurrentWorkspace = () => {
global.modal(intl.formatMessage({ id: 'filePanel.workspace.rename' }), renameModalMessage(), intl.formatMessage({ id: 'filePanel.ok' }), onFinishRenameWorkspace, '') global.modal(intl.formatMessage({ id: 'filePanel.workspace.rename' }), renameModalMessage(), intl.formatMessage({ id: 'filePanel.ok' }), onFinishRenameWorkspace, intl.formatMessage({ id: 'filePanel.cancel' }))
} }
const createWorkspace = () => { const createWorkspace = () => {
global.modal(intl.formatMessage({ id: 'filePanel.workspace.create' }), createModalMessage(), intl.formatMessage({ id: 'filePanel.ok' }), onFinishCreateWorkspace, '') global.modal(intl.formatMessage({ id: 'filePanel.workspace.create' }), createModalMessage(), intl.formatMessage({ id: 'filePanel.ok' }), onFinishCreateWorkspace, intl.formatMessage({ id: 'filePanel.cancel' }))
} }
const deleteCurrentWorkspace = () => { const deleteCurrentWorkspace = () => {
@ -86,7 +86,22 @@ export function Workspace () {
intl.formatMessage({ id: 'filePanel.workspace.deleteConfirm' }), intl.formatMessage({ id: 'filePanel.workspace.deleteConfirm' }),
intl.formatMessage({ id: 'filePanel.ok' }), intl.formatMessage({ id: 'filePanel.ok' }),
onFinishDeleteWorkspace, onFinishDeleteWorkspace,
'' intl.formatMessage({ id: 'filePanel.cancel' })
)
}
const deleteAllWorkspaces = () => {
global.modal(
intl.formatMessage({ id: 'filePanel.workspace.deleteAll' }),
<>
<div className="d-flex flex-column">
<span className='pb-1'>{intl.formatMessage({ id: 'filePanel.workspace.deleteAllConfirm1' })}</span>
<span>{intl.formatMessage({ id: 'filePanel.workspace.deleteAllConfirm2' })}</span>
</div>
</>,
intl.formatMessage({ id: 'filePanel.ok' }),
onFinishDeleteAllWorkspaces,
intl.formatMessage({ id: 'filePanel.cancel' })
) )
} }
@ -96,7 +111,7 @@ export function Workspace () {
cloneModalMessage(), cloneModalMessage(),
intl.formatMessage({ id: 'filePanel.ok' }), intl.formatMessage({ id: 'filePanel.ok' }),
handleTypingUrl, handleTypingUrl,
'' intl.formatMessage({ id: 'filePanel.cancel' })
) )
} }
@ -136,7 +151,7 @@ export function Workspace () {
try { try {
await global.dispatchRenameWorkspace(currentWorkspace, workspaceName) await global.dispatchRenameWorkspace(currentWorkspace, workspaceName)
} catch (e) { } catch (e) {
global.modal(intl.formatMessage({ id: 'filePanel.workspace.rename' }), e.message, intl.formatMessage({ id: 'filePanel.ok' }), () => {}, '') global.modal(intl.formatMessage({ id: 'filePanel.workspace.rename' }), e.message, intl.formatMessage({ id: 'filePanel.ok' }), () => {}, intl.formatMessage({ id: 'filePanel.cancel' }))
console.error(e) console.error(e)
} }
} }
@ -162,7 +177,7 @@ export function Workspace () {
try { try {
await global.dispatchCreateWorkspace(workspaceName, workspaceTemplateName, opts, initGitRepo) await global.dispatchCreateWorkspace(workspaceName, workspaceTemplateName, opts, initGitRepo)
} catch (e) { } catch (e) {
global.modal(intl.formatMessage({ id: 'filePanel.workspace.create' }), e.message, intl.formatMessage({ id: 'filePanel.ok' }), () => {}, '') global.modal(intl.formatMessage({ id: 'filePanel.workspace.create' }), e.message, intl.formatMessage({ id: 'filePanel.ok' }), () => {}, intl.formatMessage({ id: 'filePanel.cancel' }))
console.error(e) console.error(e)
} }
} }
@ -171,11 +186,19 @@ export function Workspace () {
try { try {
await global.dispatchDeleteWorkspace(global.fs.browser.currentWorkspace) await global.dispatchDeleteWorkspace(global.fs.browser.currentWorkspace)
} catch (e) { } catch (e) {
global.modal(intl.formatMessage({ id: 'filePanel.workspace.delete' }), e.message, intl.formatMessage({ id: 'filePanel.ok' }), () => {}, '') global.modal(intl.formatMessage({ id: 'filePanel.workspace.delete' }), e.message, intl.formatMessage({ id: 'filePanel.ok' }), () => {}, intl.formatMessage({ id: 'filePanel.cancel' }))
console.error(e)
}
}
const onFinishDeleteAllWorkspaces = async () => {
try {
await global.dispatchDeleteAllWorkspaces()
} catch (e) {
global.modal(intl.formatMessage({ id: 'filePanel.workspace.deleteAll' }), e.message, intl.formatMessage({ id: 'filePanel.ok' }), () => {}, intl.formatMessage({ id: 'filePanel.cancel' }))
console.error(e) console.error(e)
} }
} }
/** ** ****/
const resetFocus = () => { const resetFocus = () => {
global.dispatchSetFocusElement([{ key: '', type: 'folder' }]) global.dispatchSetFocusElement([{ key: '', type: 'folder' }])
@ -186,7 +209,7 @@ export function Workspace () {
await global.dispatchSwitchToWorkspace(name) await global.dispatchSwitchToWorkspace(name)
global.dispatchHandleExpandPath([]) global.dispatchHandleExpandPath([])
} catch (e) { } catch (e) {
global.modal(intl.formatMessage({ id: 'filePanel.workspace.switch' }), e.message, intl.formatMessage({ id: 'filePanel.ok' }), () => {}, '') global.modal(intl.formatMessage({ id: 'filePanel.workspace.switch' }), e.message, intl.formatMessage({ id: 'filePanel.ok' }), () => {}, intl.formatMessage({ id: 'filePanel.cancel' }))
console.error(e) console.error(e)
} }
} }
@ -222,7 +245,7 @@ export function Workspace () {
intl.formatMessage({ id: 'filePanel.workspace.cloneMessage' }), intl.formatMessage({ id: 'filePanel.workspace.cloneMessage' }),
intl.formatMessage({ id: 'filePanel.ok' }), intl.formatMessage({ id: 'filePanel.ok' }),
() => {}, () => {},
'' intl.formatMessage({ id: 'filePanel.cancel' })
) )
} }
} }
@ -261,7 +284,7 @@ export function Workspace () {
} }
} catch (e) { } catch (e) {
console.error(e) console.error(e)
global.modal(intl.formatMessage({ id: 'filePanel.checkoutGitBranch' }), e.message, intl.formatMessage({ id: 'filePanel.ok' }), () => {}) global.modal(intl.formatMessage({ id: 'filePanel.checkoutGitBranch' }), e.message, intl.formatMessage({ id: 'filePanel.ok' }), () => {}, intl.formatMessage({ id: 'filePanel.cancel' }))
} }
} }
@ -270,7 +293,7 @@ export function Workspace () {
await global.dispatchCreateNewBranch(branchFilter) await global.dispatchCreateNewBranch(branchFilter)
_paq.push(['trackEvent', 'Workspace', 'GIT', 'switch_to_new_branch']) _paq.push(['trackEvent', 'Workspace', 'GIT', 'switch_to_new_branch'])
} catch (e) { } catch (e) {
global.modal(intl.formatMessage({ id: 'filePanel.checkoutGitBranch' }), e.message, intl.formatMessage({ id: 'filePanel.ok' }), () => {}) global.modal(intl.formatMessage({ id: 'filePanel.checkoutGitBranch' }), e.message, intl.formatMessage({ id: 'filePanel.ok' }), () => {}, intl.formatMessage({ id: 'filePanel.cancel' }))
} }
} }
@ -433,6 +456,7 @@ export function Workspace () {
<HamburgerMenu <HamburgerMenu
createWorkspace={createWorkspace} createWorkspace={createWorkspace}
deleteCurrentWorkspace={deleteCurrentWorkspace} deleteCurrentWorkspace={deleteCurrentWorkspace}
deleteAllWorkspaces={deleteAllWorkspaces}
renameCurrentWorkspace={renameCurrentWorkspace} renameCurrentWorkspace={renameCurrentWorkspace}
cloneGitRepository={cloneGitRepository} cloneGitRepository={cloneGitRepository}
downloadWorkspaces={downloadWorkspaces} downloadWorkspaces={downloadWorkspaces}

@ -1,6 +1,6 @@
{ {
"name": "@remix-project/remix-url-resolver", "name": "@remix-project/remix-url-resolver",
"version": "0.0.45", "version": "0.0.46",
"description": "Solidity import url resolver engine", "description": "Solidity import url resolver engine",
"main": "src/index.js", "main": "src/index.js",
"types": "src/index.d.ts", "types": "src/index.d.ts",
@ -40,5 +40,5 @@
"typescript": "^3.1.6" "typescript": "^3.1.6"
}, },
"typings": "src/index.d.ts", "typings": "src/index.d.ts",
"gitHead": "69af4eddd1ba47f76a71c78077d453deb572b1a0" "gitHead": "c1415e0c3751af8bf53292d67fedf68e15bf3b23"
} }

@ -1,6 +1,6 @@
{ {
"name": "@remix-project/remix-ws-templates", "name": "@remix-project/remix-ws-templates",
"version": "1.0.10", "version": "1.0.11",
"description": "Create a Remix IDE workspace using different templates", "description": "Create a Remix IDE workspace using different templates",
"main": "src/index.js", "main": "src/index.js",
"types": "src/index.d.ts", "types": "src/index.d.ts",
@ -25,5 +25,5 @@
"ethers": "^5.4.2", "ethers": "^5.4.2",
"web3": "^1.5.1" "web3": "^1.5.1"
}, },
"gitHead": "69af4eddd1ba47f76a71c78077d453deb572b1a0" "gitHead": "c1415e0c3751af8bf53292d67fedf68e15bf3b23"
} }

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

@ -5,12 +5,14 @@ import * as utils from '../utils'
import * as chokidar from 'chokidar' import * as chokidar from 'chokidar'
import * as fs from 'fs-extra' import * as fs from 'fs-extra'
import * as isbinaryfile from 'isbinaryfile' import * as isbinaryfile from 'isbinaryfile'
import * as pathModule from 'path'
export class RemixdClient extends PluginClient { export class RemixdClient extends PluginClient {
methods: Array<string> methods: Array<string>
websocket: WS websocket: WS
currentSharedFolder: string currentSharedFolder: string
watcher: chokidar.FSWatcher watcher: chokidar.FSWatcher
trackDownStreamUpdate: Record<string, string> = {}
constructor (private readOnly = false) { constructor (private readOnly = false) {
super() super()
@ -105,6 +107,7 @@ export class RemixdClient extends PluginClient {
this.createDir({ path: args.path.substr(0, args.path.lastIndexOf('/')) }) this.createDir({ path: args.path.substr(0, args.path.lastIndexOf('/')) })
} }
try { try {
this.trackDownStreamUpdate[path] = args.content
fs.writeFile(path, args.content, 'utf8', (error: Error) => { fs.writeFile(path, args.content, 'utf8', (error: Error) => {
if (error) { if (error) {
console.log(error) console.log(error)
@ -263,8 +266,9 @@ export class RemixdClient extends PluginClient {
}) })
*/ */
this.watcher.on('change', async (f: string) => { this.watcher.on('change', async (f: string) => {
const currentContent = await this.call('editor', 'getText' as any, f) const path = pathModule.resolve(f)
const newContent = fs.readFileSync(f) const currentContent = this.trackDownStreamUpdate[path]
const newContent = fs.readFileSync(f, 'utf-8')
if (currentContent !== newContent && this.isLoaded) { if (currentContent !== newContent && this.isLoaded) {
this.emit('changed', utils.relativePath(f, this.currentSharedFolder)) this.emit('changed', utils.relativePath(f, this.currentSharedFolder))
} }

@ -1,6 +1,6 @@
{ {
"name": "remix-project", "name": "remix-project",
"version": "0.31.0-dev", "version": "0.32.0-dev",
"license": "MIT", "license": "MIT",
"description": "Ethereum Remix Monorepo", "description": "Ethereum Remix Monorepo",
"keywords": [ "keywords": [

Loading…
Cancel
Save