diff --git a/README.md b/README.md
index aea4299352..06fd3fbe16 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,24 @@
-[![CircleCI](https://circleci.com/gh/ethereum/remix-project.svg?style=svg)](https://circleci.com/gh/ethereum/remix-project)
-[![Documentation Status](https://readthedocs.org/projects/docs/badge/?version=latest)](https://remix-ide.readthedocs.io/en/latest/index.html)
-[![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/ethereum/remix-project/blob/master/CONTRIBUTING.md)
-![GitHub contributors](https://img.shields.io/github/contributors/ethereum/remix-project)
-[![Awesome Remix](https://img.shields.io/badge/Awesome--Remix-resources-green)](https://github.com/ethereum/awesome-remix)
+
+
+
+Remix Project
+
+
+
+
+[![CircleCI](https://img.shields.io/circleci/build/github/ethereum/remix-project?logo=circleci)](https://circleci.com/gh/ethereum/remix-project)
+[![Documentation Status](https://readthedocs.org/projects/remix-ide/badge/?version=latest)](https://remix-ide.readthedocs.io/en/latest/index.html)
+[![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat&logo=github)](https://github.com/ethereum/remix-project/blob/master/CONTRIBUTING.md)
+[![GitHub contributors](https://img.shields.io/github/contributors/ethereum/remix-project?style=flat&logo=github)](https://github.com/ethereum/remix-project/blob/master/CONTRIBUTING.md)
+[![Awesome Remix](https://img.shields.io/badge/Awesome--Remix-resources-green?logo=awesomelists)](https://github.com/ethereum/awesome-remix)
![GitHub](https://img.shields.io/github/license/ethereum/remix-project)
-[![Join the chat at https://gitter.im/ethereum/remix](https://badges.gitter.im/ethereum/remix.svg)](https://gitter.im/ethereum/remix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
-[![Twitter Follow](https://img.shields.io/twitter/follow/ethereumremix?style=social)](https://twitter.com/ethereumremix)
+[![Gitter Chat](https://img.shields.io/badge/Gitter%20-chat-brightgreen?style=plastic&logo=gitter)](https://gitter.im/ethereum/remix)
+[![Twitter Follow](https://img.shields.io/twitter/follow/ethereumremix?style=flat&logo=twitter&color=green)](https://twitter.com/ethereumremix)
+
+
+
+## Remix Project
-# Remix Project
**Remix Project** is a rich toolset including Remix IDE, a comprehensive smart contract development tool. The Remix Project also includes Remix Plugin Engine and Remix Libraries which are low-level tools for wider use.
## Remix IDE
diff --git a/apps/remix-ide-e2e/src/commands/switchEnvironment.ts b/apps/remix-ide-e2e/src/commands/switchEnvironment.ts
new file mode 100644
index 0000000000..03564ced41
--- /dev/null
+++ b/apps/remix-ide-e2e/src/commands/switchEnvironment.ts
@@ -0,0 +1,18 @@
+import { NightwatchBrowser } from 'nightwatch'
+import EventEmitter from 'events'
+
+class switchEnvironment extends EventEmitter {
+ command (this: NightwatchBrowser, provider: string): NightwatchBrowser {
+ this.api.waitForElementVisible('[data-id="settingsSelectEnvOptions"]')
+ .click('[data-id="settingsSelectEnvOptions"] button')
+ .waitForElementVisible(`[data-id="dropdown-item-${provider}"]`)
+ .click(`[data-id="dropdown-item-${provider}"]`)
+ .perform((done) => {
+ done()
+ this.emit('complete')
+ })
+ return this
+ }
+}
+
+module.exports = switchEnvironment
diff --git a/apps/remix-ide-e2e/src/tests/ballot.test.ts b/apps/remix-ide-e2e/src/tests/ballot.test.ts
index 9d2e9c5b07..1c8383f047 100644
--- a/apps/remix-ide-e2e/src/tests/ballot.test.ts
+++ b/apps/remix-ide-e2e/src/tests/ballot.test.ts
@@ -83,7 +83,7 @@ module.exports = {
browser
.openFile('Untitled.sol')
.clickLaunchIcon('udapp')
- .click('*[data-id="settingsSelectEnvOptions"] *[data-id="External Http Provider"]')
+ .switchEnvironment('External Http Provider')
.waitForElementPresent('[data-id="basic-http-provider-modal-footer-ok-react"]')
.execute(function () {
const modal = document.querySelector('[data-id="basic-http-provider-modal-footer-ok-react"]') as any
@@ -91,13 +91,7 @@ module.exports = {
modal.click()
})
.pause(5000)
- .execute(function () {
- const env: any = document.getElementById('selectExEnvOptions')
-
- return env.value
- }, [], function (result) {
- browser.assert.ok(result.value === 'External Http Provider', 'Web3 Provider not selected')
- })
+ .waitForElementContainsText('#selectExEnvOptions button', 'External Http Provider')
.clickLaunchIcon('solidity')
.clickLaunchIcon('udapp')
.pause(2000)
diff --git a/apps/remix-ide-e2e/src/tests/ballot_0_4_11.test.ts b/apps/remix-ide-e2e/src/tests/ballot_0_4_11.test.ts
index 95696e3882..9a36ad67f4 100644
--- a/apps/remix-ide-e2e/src/tests/ballot_0_4_11.test.ts
+++ b/apps/remix-ide-e2e/src/tests/ballot_0_4_11.test.ts
@@ -78,7 +78,7 @@ module.exports = {
browser
.openFile('Untitled.sol')
.clickLaunchIcon('udapp')
- .click('*[data-id="settingsSelectEnvOptions"] *[data-id="External Http Provider"]')
+ .switchEnvironment('External Http Provider')
.waitForElementPresent('[data-id="basic-http-provider-modal-footer-ok-react"]')
.execute(function () {
const modal = document.querySelector('[data-id="basic-http-provider-modal-footer-ok-react"]') as any
diff --git a/apps/remix-ide-e2e/src/tests/debugger.test.ts b/apps/remix-ide-e2e/src/tests/debugger.test.ts
index 9b8da7e7df..19aa196b60 100644
--- a/apps/remix-ide-e2e/src/tests/debugger.test.ts
+++ b/apps/remix-ide-e2e/src/tests/debugger.test.ts
@@ -214,10 +214,10 @@ module.exports = {
.setSolidityCompilerVersion('soljson-v0.8.7+commit.e28d00a7.js')
.addFile('useDebugNodes.sol', sources[5]['useDebugNodes.sol']) // compile contract
.clickLaunchIcon('udapp')
- .click('*[data-id="settingsSelectEnvOptions"] *[data-id="External Http Provider"]') // select web3 provider with debug nodes URL
- .clearValue('*[data-id="modalDialogCustomPromp"]')
- .setValue('*[data-id="modalDialogCustomPromp"]', 'https://remix-rinkeby.ethdevops.io')
- .modalFooterOKClick('basic-http-provider')
+ .switchEnvironment('External Http Provider') // select web3 provider with debug nodes URL
+ .clearValue('*[data-id="modalDialogCustomPromptText"]')
+ .setValue('*[data-id="modalDialogCustomPromptText"]', 'https://remix-rinkeby.ethdevops.io')
+ .modalFooterOKClick()
.waitForElementPresent('*[title="Deploy - transact (not payable)"]', 65000) // wait for the compilation to succeed
.clickLaunchIcon('debugger')
.clearValue('*[data-id="debuggerTransactionInput"]')
diff --git a/apps/remix-ide-e2e/src/tests/plugin_api.ts b/apps/remix-ide-e2e/src/tests/plugin_api.ts
index f05a2fe130..c7ef41a231 100644
--- a/apps/remix-ide-e2e/src/tests/plugin_api.ts
+++ b/apps/remix-ide-e2e/src/tests/plugin_api.ts
@@ -188,7 +188,7 @@ module.exports = {
.frameParent()
.useCss()
.clickLaunchIcon('udapp')
- .waitForElementContainsText('#selectExEnvOptions option:checked', 'Remix VM (Berlin)')
+ .waitForElementContainsText('#selectExEnvOptions button', 'Remix VM (Berlin)')
.clickLaunchIcon('localPlugin')
.useXpath()
// @ts-ignore
@@ -391,7 +391,7 @@ module.exports = {
.useCss()
.clickLaunchIcon('pluginManager')
.clickLaunchIcon('udapp')
- .click('*[data-id="Hardhat Provider"]')
+ .switchEnvironment('Hardhat Provider')
.modalFooterOKClick('hardhat-provider')
.waitForElementContainsText('*[data-id="settingsNetworkEnv"]', 'Custom') // e.g Custom (1337) network
.clickLaunchIcon('localPlugin')
diff --git a/apps/remix-ide-e2e/src/tests/providers.test.ts b/apps/remix-ide-e2e/src/tests/providers.test.ts
index fe6dcde8bf..5cb1048d91 100644
--- a/apps/remix-ide-e2e/src/tests/providers.test.ts
+++ b/apps/remix-ide-e2e/src/tests/providers.test.ts
@@ -10,7 +10,7 @@ module.exports = {
'Should switch to ganache provider, set a custom URL and fail to connect': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('div[data-id="remixIdeIconPanel"]', 10000)
.clickLaunchIcon('udapp')
- .click('*[data-id="Ganache Provider"]')
+ .switchEnvironment('Ganache Provider')
.waitForElementVisible('*[data-id="ganache-providerModalDialogModalBody-react"]')
.execute(() => {
(document.querySelector('*[data-id="ganache-providerModalDialogModalBody-react"] input') as any).focus()
@@ -25,7 +25,7 @@ module.exports = {
},
'Should switch to ganache provider, use the default ganache URL and succeed to connect': function (browser: NightwatchBrowser) {
- browser.click('*[data-id="Ganache Provider"]')
+ browser.switchEnvironment('Ganache Provider')
.waitForElementVisible('*[data-id="ganache-providerModalDialogModalBody-react"]')
.modalFooterOKClick('ganache-provider')
.waitForElementContainsText('*[data-id="settingsNetworkEnv"]', 'Custom (')
@@ -33,7 +33,7 @@ module.exports = {
'Should switch to foundry provider, set a custom URL and fail to connect': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('div[data-id="remixIdeIconPanel"]', 10000)
- .click('*[data-id="Foundry Provider"]')
+ .switchEnvironment('Foundry Provider')
.waitForElementVisible('*[data-id="foundry-providerModalDialogModalBody-react"]')
.execute(() => {
(document.querySelector('*[data-id="foundry-providerModalDialogModalBody-react"] input') as any).focus()
@@ -48,7 +48,7 @@ module.exports = {
},
'Should switch to foundry provider, use the default foundry URL and succeed to connect': function (browser: NightwatchBrowser) {
- browser.click('*[data-id="Foundry Provider"]')
+ browser.switchEnvironment('Foundry Provider')
.waitForElementVisible('*[data-id="foundry-providerModalDialogModalBody-react"]')
.modalFooterOKClick('foundry-provider')
.waitForElementContainsText('*[data-id="settingsNetworkEnv"]', 'Custom (')
diff --git a/apps/remix-ide-e2e/src/tests/runAndDeploy.test.ts b/apps/remix-ide-e2e/src/tests/runAndDeploy.test.ts
index dc00530bdd..6d87899d79 100644
--- a/apps/remix-ide-e2e/src/tests/runAndDeploy.test.ts
+++ b/apps/remix-ide-e2e/src/tests/runAndDeploy.test.ts
@@ -32,7 +32,7 @@ module.exports = {
'Should sign message using account key #group2': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="settingsRemixRunSignMsg"]')
- .click('select[id="selectExEnvOptions"] option[value="vm-berlin"]')
+ .switchEnvironment('vm-berlin')
.pause(2000)
.click('*[data-id="settingsRemixRunSignMsg"]')
.pause(2000)
diff --git a/apps/remix-ide-e2e/src/tests/terminal.test.ts b/apps/remix-ide-e2e/src/tests/terminal.test.ts
index 59af766031..b958b7d42d 100644
--- a/apps/remix-ide-e2e/src/tests/terminal.test.ts
+++ b/apps/remix-ide-e2e/src/tests/terminal.test.ts
@@ -46,11 +46,11 @@ module.exports = {
.waitForElementContainsText('*[data-id="terminalJournal"]', '["0x5B38Da6a701c568545dCfcB03FcB875f56beddC4","0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2","0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db","0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB","0x617F2E2fD72FD9D5503197092aC168c91465E7f2","0x17F6AD8Ef982297579C203069C1DbfFE4348c372","0x5c6B0f7Bf3E7ce046039Bd8FABdfD3f9F5021678","0x03C6FcED478cBbC9a4FAB34eF9f40767739D1Ff7","0x1aE0EA34a72D944a8C7603FfB3eC30a6669E454C","0x0A098Eda01Ce92ff4A4CCb7A4fFFb5A43EBC70DC","0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c","0x14723A09ACff6D2A60DcdF7aA4AFf308FDDC160C","0x4B0897b0513fdC7C541B6d9D7E929C4e5364D2dB","0x583031D1113aD414F02576BD6afaBfb302140225","0xdD870fA1b7C4700F2BD7f44238821C26f7392148"]')
},
- 'Call web3.eth.getAccounts() using Web3 Provider #group5': function (browser: NightwatchBrowser) {
+ 'Call web3.eth.getAccounts() using External Http Provider #group5': function (browser: NightwatchBrowser) {
browser
.click('*[data-id="terminalClearConsole"]') // clear the terminal
.clickLaunchIcon('udapp')
- .click('*[data-id="settingsSelectEnvOptions"] *[data-id="External Http Provider"]')
+ .switchEnvironment('External Http Provider')
.modalFooterOKClick('basic-http-provider')
.executeScript('web3.eth.getAccounts()')
.waitForElementContainsText('*[data-id="terminalJournal"]', '["', 60000) // we check if an array is present, don't need to check for the content
@@ -95,7 +95,7 @@ module.exports = {
browser
.clickLaunchIcon('settings')
.clickLaunchIcon('udapp')
- .click('*[data-id="settingsVMLondonMode"]')
+ .switchEnvironment('vm-london')
.click('*[data-id="terminalClearConsole"]') // clear the terminal
.clickLaunchIcon('filePanel')
.click('*[data-id="treeViewDivtreeViewItem"]') // make sure we create the file at the root folder
diff --git a/apps/remix-ide-e2e/src/tests/transactionExecution.test.ts b/apps/remix-ide-e2e/src/tests/transactionExecution.test.ts
index 3eac406fcd..842da3b3c7 100644
--- a/apps/remix-ide-e2e/src/tests/transactionExecution.test.ts
+++ b/apps/remix-ide-e2e/src/tests/transactionExecution.test.ts
@@ -161,7 +161,7 @@ module.exports = {
browser
.clickLaunchIcon('udapp')
.clearTransactions()
- .click('*[data-id="settingsVMLondonMode"]') // switch to London fork
+ .switchEnvironment('vm-london') // switch to London fork
.selectAccount('0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c') // this account will be used for this test suite
.click('.udapp_contractActionsContainerSingle > button')
.clickInstance(0)
diff --git a/apps/remix-ide-e2e/src/types/index.d.ts b/apps/remix-ide-e2e/src/types/index.d.ts
index eccd50bad5..9ba94f470b 100644
--- a/apps/remix-ide-e2e/src/types/index.d.ts
+++ b/apps/remix-ide-e2e/src/types/index.d.ts
@@ -64,6 +64,7 @@ declare module 'nightwatch' {
getBrowserLogs (this: NightwatchBrowser): NightwatchBrowser
currentSelectedFileIs (name: string): NightwatchBrowser,
switchWorkspace: (workspaceName: string) => NightwatchBrowser
+ switchEnvironment: (provider: string) => NightwatchBrowser
}
export interface NightwatchBrowser {
diff --git a/apps/remix-ide/src/app.js b/apps/remix-ide/src/app.js
index 6037f1bfbb..6adce5ec67 100644
--- a/apps/remix-ide/src/app.js
+++ b/apps/remix-ide/src/app.js
@@ -30,6 +30,8 @@ import { HardhatProvider } from './app/tabs/hardhat-provider'
import { GanacheProvider } from './app/tabs/ganache-provider'
import { FoundryProvider } from './app/tabs/foundry-provider'
import { ExternalHttpProvider } from './app/tabs/external-http-provider'
+import { Injected0ptimismProvider } from './app/tabs/injected-optimism-provider'
+import { InjectedArbitrumOneProvider } from './app/tabs/injected-arbitrum-one-provider'
const isElectron = require('is-electron')
@@ -185,6 +187,8 @@ class AppComponent {
const ganacheProvider = new GanacheProvider(blockchain)
const foundryProvider = new FoundryProvider(blockchain)
const externalHttpProvider = new ExternalHttpProvider(blockchain)
+ const injected0ptimismProvider = new Injected0ptimismProvider(blockchain)
+ const injectedArbitrumOneProvider = new InjectedArbitrumOneProvider(blockchain)
// ----------------- convert offset to line/column service -----------
const offsetToLineColumnConverter = new OffsetToLineColumnConverter()
Registry.getInstance().put({
@@ -244,6 +248,8 @@ class AppComponent {
ganacheProvider,
foundryProvider,
externalHttpProvider,
+ injected0ptimismProvider,
+ injectedArbitrumOneProvider,
this.walkthroughService,
search
])
diff --git a/apps/remix-ide/src/app/panels/file-panel.js b/apps/remix-ide/src/app/panels/file-panel.js
index 2f41aa2b8d..7b46d54504 100644
--- a/apps/remix-ide/src/app/panels/file-panel.js
+++ b/apps/remix-ide/src/app/panels/file-panel.js
@@ -153,6 +153,9 @@ module.exports = class Filepanel extends ViewPlugin {
const workspaceProvider = this.fileProviders.workspace
this.currentWorkspaceMetadata = { name: workspace.name, isLocalhost: workspace.isLocalhost, absolutePath: `${workspaceProvider.workspacesPath}/${workspace.name}` }
+ if (workspace.name !== " - connect to localhost - ") {
+ localStorage.setItem('currentWorkspace', workspace.name)
+ }
this.emit('setWorkspace', workspace)
}
diff --git a/apps/remix-ide/src/app/tabs/abstract-provider.tsx b/apps/remix-ide/src/app/tabs/abstract-provider.tsx
index ff924a618a..31b1a4e317 100644
--- a/apps/remix-ide/src/app/tabs/abstract-provider.tsx
+++ b/apps/remix-ide/src/app/tabs/abstract-provider.tsx
@@ -3,21 +3,21 @@ import { AppModal, AlertModal, ModalTypes } from '@remix-ui/app'
import { Blockchain } from '../../blockchain/blockchain'
import { ethers } from 'ethers'
-type JsonDataRequest = {
+export type JsonDataRequest = {
id: number,
jsonrpc: string // version
method: string,
params: Array,
}
-type JsonDataResult = {
+export type JsonDataResult = {
id: number,
jsonrpc: string // version
result: any
}
-type RejectRequest = (error: Error) => void
-type SuccessRequest = (data: JsonDataResult) => void
+export type RejectRequest = (error: Error) => void
+export type SuccessRequest = (data: JsonDataResult) => void
export abstract class AbstractProvider extends Plugin {
provider: ethers.providers.JsonRpcProvider
diff --git a/apps/remix-ide/src/app/tabs/external-http-provider.tsx b/apps/remix-ide/src/app/tabs/external-http-provider.tsx
index 5a22b48ba4..71c7c249c0 100644
--- a/apps/remix-ide/src/app/tabs/external-http-provider.tsx
+++ b/apps/remix-ide/src/app/tabs/external-http-provider.tsx
@@ -30,7 +30,7 @@ export class ExternalHttpProvider extends AbstractProvider {
WARNING: It is not safe to use the --http.corsdomain flag with a wildcard: --http.corsdomain *
-
For more info: Remix Docs on Web3 Provider
+
For more info: Remix Docs on External HTTP Provider
External HTTP Provider Endpoint
diff --git a/apps/remix-ide/src/app/tabs/injected-arbitrum-one-provider.tsx b/apps/remix-ide/src/app/tabs/injected-arbitrum-one-provider.tsx
new file mode 100644
index 0000000000..283dacdf24
--- /dev/null
+++ b/apps/remix-ide/src/app/tabs/injected-arbitrum-one-provider.tsx
@@ -0,0 +1,21 @@
+import * as packageJson from '../../../../../package.json'
+import { InjectedProvider } from './injected-provider'
+
+const profile = {
+ name: 'injected-arbitrum-one-provider',
+ displayName: 'Injected Arbitrum One Provider',
+ kind: 'provider',
+ description: 'injected Arbitrum One Provider',
+ methods: ['sendAsync'],
+ version: packageJson.version
+}
+
+export class InjectedArbitrumOneProvider extends InjectedProvider {
+
+ constructor () {
+ super(profile)
+ this.chainName = 'Arbitrum One'
+ this.chainId = '0xa4b1'
+ this.rpcUrls = ['https://arb1.arbitrum.io/rpc']
+ }
+}
\ No newline at end of file
diff --git a/apps/remix-ide/src/app/tabs/injected-optimism-provider.tsx b/apps/remix-ide/src/app/tabs/injected-optimism-provider.tsx
new file mode 100644
index 0000000000..64c8c4e91a
--- /dev/null
+++ b/apps/remix-ide/src/app/tabs/injected-optimism-provider.tsx
@@ -0,0 +1,21 @@
+import * as packageJson from '../../../../../package.json'
+import { InjectedProvider } from './injected-provider'
+
+const profile = {
+ name: 'injected-optimism-provider',
+ displayName: 'Injected Optimism Provider',
+ kind: 'provider',
+ description: 'injected Optimism Provider',
+ methods: ['sendAsync'],
+ version: packageJson.version
+}
+
+export class Injected0ptimismProvider extends InjectedProvider {
+
+ constructor () {
+ super(profile)
+ this.chainName = 'Optimism'
+ this.chainId = '0xa'
+ this.rpcUrls = ['https://mainnet.optimism.io']
+ }
+}
\ No newline at end of file
diff --git a/apps/remix-ide/src/app/tabs/injected-provider.tsx b/apps/remix-ide/src/app/tabs/injected-provider.tsx
new file mode 100644
index 0000000000..038919206c
--- /dev/null
+++ b/apps/remix-ide/src/app/tabs/injected-provider.tsx
@@ -0,0 +1,75 @@
+import { Plugin } from '@remixproject/engine'
+import { JsonDataRequest, RejectRequest, SuccessRequest } from './abstract-provider'
+import { ethers } from 'ethers'
+import Web3 from 'web3'
+
+export class InjectedProvider extends Plugin {
+ provider: any
+ chainName: string
+ chainId: string
+ rpcUrls: Array
+
+ constructor (profile) {
+ super(profile)
+ if ((window as any).ethereum) {
+ this.provider = new Web3((window as any).ethereum)
+ }
+ }
+
+ sendAsync (data: JsonDataRequest): Promise {
+ return new Promise((resolve, reject) => {
+ this.sendAsyncInternal(data, resolve, reject)
+ })
+ }
+
+ private async sendAsyncInternal (data: JsonDataRequest, resolve: SuccessRequest, reject: RejectRequest): Promise {
+ // Check the case where current environment is VM on UI and it still sends RPC requests
+ // This will be displayed on UI tooltip as 'cannot get account list: Environment Updated !!'
+ if (!this.provider) {
+ this.call('notification', 'toast', 'No injected provider (e.g Metamask) has been found.')
+ return reject(new Error('no injected provider found.'))
+ }
+ try {
+ if ((window as any) && typeof (window as any).ethereum.enable === 'function') (window as any).ethereum.enable()
+ if (!await (window as any).ethereum._metamask.isUnlocked()) this.call('notification', 'toast', 'Please make sure the injected provider is unlocked (e.g Metamask).')
+ await addL2Network(this.chainName, this.chainId, this.rpcUrls)
+ const resultData = await this.provider.currentProvider.send(data.method, data.params)
+ resolve({ jsonrpc: '2.0', result: resultData.result, id: data.id })
+ } catch (error) {
+ reject(error)
+ }
+ }
+}
+
+export const addL2Network = async (chainName: string, chainId: string, rpcUrls: Array) => {
+ try {
+ await (window as any).ethereum.request({
+ method: 'wallet_switchEthereumChain',
+ params: [{ chainId: chainId }],
+ });
+ } catch (switchError) {
+ // This error code indicates that the chain has not been added to MetaMask.
+ if (switchError.code === 4902) {
+ try {
+ await (window as any).ethereum.request({
+ method: 'wallet_addEthereumChain',
+ params: [
+ {
+ chainId: chainId,
+ chainName: chainName,
+ rpcUrls: rpcUrls,
+ },
+ ],
+ });
+
+ await (window as any).ethereum.request({
+ method: 'wallet_switchEthereumChain',
+ params: [{ chainId: chainId }],
+ });
+ } catch (addError) {
+ // handle "add" error
+ }
+ }
+ // handle other "switch" errors
+ }
+}
\ No newline at end of file
diff --git a/apps/remix-ide/src/app/udapp/run-tab.js b/apps/remix-ide/src/app/udapp/run-tab.js
index f465b88bcc..8475a92848 100644
--- a/apps/remix-ide/src/app/udapp/run-tab.js
+++ b/apps/remix-ide/src/app/udapp/run-tab.js
@@ -103,6 +103,7 @@ export class RunTab extends ViewPlugin {
await this.call('blockchain', 'addProvider', {
name: 'Hardhat Provider',
+ isInjected: false,
provider: {
async sendAsync (payload, callback) {
try {
@@ -117,6 +118,7 @@ export class RunTab extends ViewPlugin {
await this.call('blockchain', 'addProvider', {
name: 'Ganache Provider',
+ isInjected: false,
provider: {
async sendAsync (payload, callback) {
try {
@@ -131,6 +133,7 @@ export class RunTab extends ViewPlugin {
await this.call('blockchain', 'addProvider', {
name: 'Foundry Provider',
+ isInjected: false,
provider: {
async sendAsync (payload, callback) {
try {
@@ -145,6 +148,7 @@ export class RunTab extends ViewPlugin {
await this.call('blockchain', 'addProvider', {
name: 'Wallet Connect',
+ isInjected: false,
provider: {
async sendAsync (payload, callback) {
try {
@@ -170,6 +174,36 @@ export class RunTab extends ViewPlugin {
}
}
})
+
+ await this.call('blockchain', 'addProvider', {
+ name: 'Optimism Provider',
+ isInjected: true,
+ provider: {
+ async sendAsync (payload, callback) {
+ try {
+ const result = await udapp.call('injected-optimism-provider', 'sendAsync', payload)
+ callback(null, result)
+ } catch (e) {
+ callback(e)
+ }
+ }
+ }
+ })
+
+ await this.call('blockchain', 'addProvider', {
+ name: 'Arbitrum One Provider',
+ isInjected: true,
+ provider: {
+ async sendAsync (payload, callback) {
+ try {
+ const result = await udapp.call('injected-arbitrum-one-provider', 'sendAsync', payload)
+ callback(null, result)
+ } catch (e) {
+ callback(e)
+ }
+ }
+ }
+ })
}
writeFile (fileName, content) {
diff --git a/apps/remix-ide/src/blockchain/execution-context.js b/apps/remix-ide/src/blockchain/execution-context.js
index 95b4abe45d..1fe181acfb 100644
--- a/apps/remix-ide/src/blockchain/execution-context.js
+++ b/apps/remix-ide/src/blockchain/execution-context.js
@@ -152,9 +152,12 @@ export class ExecutionContext {
if (context === 'injected') {
if (injectedProvider === undefined) {
- infoCb('No injected Web3 provider found. Make sure your provider (e.g. MetaMask) is active and running (when recently activated you may have to reload the page).')
+ infoCb('No injected provider found. Make sure your provider (e.g. MetaMask) is active and running (when recently activated you may have to reload the page).')
return cb()
} else {
+ if (injectedProvider && injectedProvider._metamask && injectedProvider._metamask.isUnlocked) {
+ if (!await injectedProvider._metamask.isUnlocked()) this.call('notification', 'toast', 'Please make sure the injected provider is unlocked (e.g Metamask).')
+ }
this.askPermission()
this.executionContext = context
web3.setProvider(injectedProvider)
@@ -166,11 +169,21 @@ export class ExecutionContext {
if (this.customNetWorks[context]) {
var network = this.customNetWorks[context]
- this.setProviderFromEndpoint(network.provider, { context: network.name }, (error) => {
- if (error) infoCb(error)
- cb()
- })
- }
+ if (!this.customNetWorks[context].isInjected) {
+ this.setProviderFromEndpoint(network.provider, { context: network.name }, (error) => {
+ if (error) infoCb(error)
+ cb()
+ })
+ } else {
+ // injected
+ this.askPermission()
+ this.executionContext = context
+ web3.setProvider(network.provider)
+ await this._updateChainContext()
+ this.event.trigger('contextChanged', [context])
+ return cb()
+ }
+ }
}
currentblockGasLimit () {
diff --git a/apps/remix-ide/src/remixAppManager.js b/apps/remix-ide/src/remixAppManager.js
index e1dcd80f07..b90f37a022 100644
--- a/apps/remix-ide/src/remixAppManager.js
+++ b/apps/remix-ide/src/remixAppManager.js
@@ -19,7 +19,7 @@ const sensitiveCalls = {
}
export function isNative(name) {
- const nativePlugins = ['vyper', 'workshops', 'debugger', 'remixd', 'menuicons', 'solidity', 'solidity-logic', 'solidityStaticAnalysis', 'solidityUnitTesting', 'layout', 'notification', 'hardhat-provider', 'ganache-provider', 'foundry-provider', 'basic-http-provider']
+ const nativePlugins = ['vyper', 'workshops', 'debugger', 'remixd', 'menuicons', 'solidity', 'solidity-logic', 'solidityStaticAnalysis', 'solidityUnitTesting', 'layout', 'notification', 'hardhat-provider', 'ganache-provider', 'foundry-provider', 'basic-http-provider', 'injected-optimism-provider', 'injected-arbitrum-one-provider']
return nativePlugins.includes(name) || requiredModules.includes(name)
}
diff --git a/libs/remix-ui/helper/src/index.ts b/libs/remix-ui/helper/src/index.ts
index 36d73ef523..9f050ea8b8 100644
--- a/libs/remix-ui/helper/src/index.ts
+++ b/libs/remix-ui/helper/src/index.ts
@@ -1,3 +1,4 @@
export * from './lib/remix-ui-helper'
export * from './lib/helper-components'
-export * from './lib/components/PluginViewWrapper'
\ No newline at end of file
+export * from './lib/components/PluginViewWrapper'
+export * from './lib/components/custom-dropdown'
\ No newline at end of file
diff --git a/libs/remix-ui/workspace/src/lib/components/custom-dropdown.tsx b/libs/remix-ui/helper/src/lib/components/custom-dropdown.tsx
similarity index 100%
rename from libs/remix-ui/workspace/src/lib/components/custom-dropdown.tsx
rename to libs/remix-ui/helper/src/lib/components/custom-dropdown.tsx
diff --git a/libs/remix-ui/plugin-manager/src/lib/components/LocalPluginForm.tsx b/libs/remix-ui/plugin-manager/src/lib/components/LocalPluginForm.tsx
index 36c72b1410..0329189fdc 100644
--- a/libs/remix-ui/plugin-manager/src/lib/components/LocalPluginForm.tsx
+++ b/libs/remix-ui/plugin-manager/src/lib/components/LocalPluginForm.tsx
@@ -222,7 +222,7 @@ function LocalPluginForm ({ closeModal, visible, pluginManager }: LocalPluginFor
type="radio"
name="location"
value="sidePanel"
- id="none"
+ id="localPluginRadioButtonsidePanelSidePanel"
data-id='localPluginRadioButtonsidePanel'
checked={location === 'sidePanel'}
onChange={(e) => setLocation(e.target.value as 'sidePanel' | 'mainPanel' | 'none')} />
@@ -234,7 +234,7 @@ function LocalPluginForm ({ closeModal, visible, pluginManager }: LocalPluginFor
type="radio"
name="location"
value="mainPanel"
- id="none"
+ id="localPluginRadioButtonsidePanelMainPanel"
data-id='localPluginRadioButtonmainPanel'
checked={location === 'mainPanel'}
onChange={(e) => setLocation(e.target.value as 'sidePanel' | 'mainPanel' | 'none')} />
@@ -246,7 +246,7 @@ function LocalPluginForm ({ closeModal, visible, pluginManager }: LocalPluginFor
type="radio"
name="location"
value="none"
- id="none"
+ id="localPluginRadioButtonsidePanelNone"
data-id='localPluginRadioButtonnone'
checked={location === 'none'}
onChange={(e) => setLocation(e.target.value as 'sidePanel' | 'mainPanel' | 'none')} />
diff --git a/libs/remix-ui/run-tab/src/lib/components/account.tsx b/libs/remix-ui/run-tab/src/lib/components/account.tsx
index 151f165632..f8d5619725 100644
--- a/libs/remix-ui/run-tab/src/lib/components/account.tsx
+++ b/libs/remix-ui/run-tab/src/lib/components/account.tsx
@@ -25,7 +25,7 @@ export function AccountUI (props: AccountProps) {
case 'injected':
setPlusOpt({
classList: 'udapp_disableMouseEvents',
- title: "Unfortunately it's not possible to create an account using injected web3. Please create the account directly from your provider (i.e metamask or other of the same type)."
+ title: "Unfortunately it's not possible to create an account using injected provider. Please create the account directly from your provider (i.e metamask or other of the same type)."
})
break
diff --git a/libs/remix-ui/run-tab/src/lib/components/environment.tsx b/libs/remix-ui/run-tab/src/lib/components/environment.tsx
index 8fbd17cfa9..04315d6d8b 100644
--- a/libs/remix-ui/run-tab/src/lib/components/environment.tsx
+++ b/libs/remix-ui/run-tab/src/lib/components/environment.tsx
@@ -2,11 +2,14 @@
import React from 'react'
import { FormattedMessage, useIntl } from 'react-intl'
import { EnvironmentProps } from '../types'
+import { Dropdown } from 'react-bootstrap'
+import { CustomMenu, CustomToggle } from '@remix-ui/helper'
+import { OverlayTrigger, Tooltip } from 'react-bootstrap' // eslint-disable-line
export function EnvironmentUI (props: EnvironmentProps) {
const handleChangeExEnv = (env: string) => {
const provider = props.providers.providerList.find(exEnv => exEnv.value === env)
- const fork = provider.fork // can be undefined if connected to an external source (web3 provider / injected)
+ const fork = provider.fork // can be undefined if connected to an external source (External Http Provider / injected)
let context = provider.value
context = context.startsWith('vm') ? 'vm' : context
@@ -16,22 +19,55 @@ export function EnvironmentUI (props: EnvironmentProps) {
const intl = useIntl()
+ const currentProvider = props.providers.providerList.find(exEnv => exEnv.value === props.selectedEnv)
+ const bridges = {
+ 'Optimism Provider': 'https://www.optimism.io/apps/bridges',
+ 'Arbitrum One Provider': 'https://bridge.arbitrum.io/'
+ }
+
+ const isL2 = (provider) => provider && (provider.value === 'Optimism Provider' || provider.value === 'Arbitrum One Provider')
return (
-
+
+
+
+ { isL2(currentProvider) && 'L2 - '}
+ { currentProvider && currentProvider.content }
+ { currentProvider && bridges[currentProvider.value] &&
+ Click to open a bridge for converting L1 mainnet ETH to the selected network currency.
+
+ }>
+ { window.open(bridges[currentProvider.value], '_blank') }}>
+ }
+
+
+ {
+ props.providers.providerList.map(({ content, value }, index) => (
+ {
+ handleChangeExEnv(value)
+ }}
+ data-id={`dropdown-item-${value}`}
+ >
+ { isL2({ value }) && 'L2 - ' }{ content }
+
+ ))
+ }
+
+
{props.count}
- Save transactions (deployed contracts and function executions) and replay them in another environment.
e.g Transactions created in Javascript VM can be replayed in the Injected Web3.
+ Save transactions (deployed contracts and function executions) and replay them in another environment.
e.g Transactions created in Remix VM can be replayed in the Injected Provider.
}>
diff --git a/libs/remix-ui/run-tab/src/lib/css/run-tab.css b/libs/remix-ui/run-tab/src/lib/css/run-tab.css
index c4c65e4e01..2921381007 100644
--- a/libs/remix-ui/run-tab/src/lib/css/run-tab.css
+++ b/libs/remix-ui/run-tab/src/lib/css/run-tab.css
@@ -529,4 +529,7 @@
text-decoration: none;
background-color: #007aa6;
}
+.udapp_selectExEnvOptions {
+ width: 100%;
+}
diff --git a/libs/remix-ui/workspace/src/lib/actions/index.ts b/libs/remix-ui/workspace/src/lib/actions/index.ts
index 7a5387474b..b684fdc05a 100644
--- a/libs/remix-ui/workspace/src/lib/actions/index.ts
+++ b/libs/remix-ui/workspace/src/lib/actions/index.ts
@@ -127,6 +127,14 @@ export const initWorkspace = (filePanelPlugin) => async (reducerDispatch: React.
plugin.on('editor', 'editorMounted', async () => await plugin.fileManager.openFile(route))
} else await basicWorkspaceInit(workspaces, workspaceProvider)
} else await basicWorkspaceInit(workspaces, workspaceProvider)
+ } else if (localStorage.getItem("currentWorkspace")) {
+ const index = workspaces.findIndex(element => element.name == localStorage.getItem("currentWorkspace"))
+ if (index !== -1) {
+ const name = localStorage.getItem("currentWorkspace")
+ workspaceProvider.setWorkspace(name)
+ plugin.setWorkspace({ name: name, isLocalhost: false })
+ dispatch(setCurrentWorkspace({ name: name, isGitRepo: false }))
+ }
} else {
await basicWorkspaceInit(workspaces, workspaceProvider)
}
diff --git a/libs/remix-ui/workspace/src/lib/providers/FileSystemProvider.tsx b/libs/remix-ui/workspace/src/lib/providers/FileSystemProvider.tsx
index 86f4cccaf2..f754869931 100644
--- a/libs/remix-ui/workspace/src/lib/providers/FileSystemProvider.tsx
+++ b/libs/remix-ui/workspace/src/lib/providers/FileSystemProvider.tsx
@@ -5,7 +5,9 @@ import { Toaster } from '@remix-ui/toaster' // eslint-disable-line
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import { FileSystemContext } from '../contexts'
import { browserReducer, browserInitialState } from '../reducers/workspace'
-import { initWorkspace, fetchDirectory, removeInputField, deleteWorkspace, clearPopUp, publishToGist, createNewFile, setFocusElement, createNewFolder, deletePath, renamePath, copyFile, copyFolder, runScript, emitContextMenuEvent, handleClickFile, handleExpandPath, addInputField, createWorkspace, fetchWorkspaceDirectory, renameWorkspace, switchToWorkspace, uploadFile, handleDownloadFiles, restoreBackupZip, cloneRepository } from '../actions'
+import { initWorkspace, fetchDirectory, removeInputField, deleteWorkspace, clearPopUp, publishToGist, createNewFile, setFocusElement, createNewFolder,
+ deletePath, renamePath, copyFile, copyFolder, runScript, emitContextMenuEvent, handleClickFile, handleExpandPath, addInputField, createWorkspace,
+ fetchWorkspaceDirectory, renameWorkspace, switchToWorkspace, uploadFile, handleDownloadFiles, restoreBackupZip, cloneRepository } from '../actions'
import { Modal, WorkspaceProps, WorkspaceTemplate } from '../types'
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import { Workspace } from '../remix-ui-workspace'
diff --git a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx
index 32e82f3499..aee6534b22 100644
--- a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx
+++ b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx
@@ -1,7 +1,7 @@
import React, { useState, useEffect, useRef, useContext } from 'react' // eslint-disable-line
import { FormattedMessage, useIntl } from 'react-intl'
import { Dropdown } from 'react-bootstrap'
-import { CustomMenu, CustomToggle } from './components/custom-dropdown'
+import { CustomMenu, CustomToggle } from '@remix-ui/helper'
import { FileExplorer } from './components/file-explorer' // eslint-disable-line
import { FileSystemContext } from './contexts'
import './css/remix-ui-workspace.css'
@@ -21,6 +21,7 @@ export function Workspace () {
const cloneUrlRef = useRef()
useEffect(() => {
+ setCurrentWorkspace(localStorage.getItem('currentWorkspace') ? localStorage.getItem('currentWorkspace') : '')
resetFocus()
}, [])
@@ -280,13 +281,13 @@ export function Workspace () {
}}
data-id={`dropdown-item-${name}`}
>
- { isGitRepo ?
-
- { currentWorkspace === name ? ✓ { name } : { name } }
-
-
:
+ { isGitRepo ?
+
{ currentWorkspace === name ? ✓ { name } : { name } }
- }
+
+
:
+ { currentWorkspace === name ? ✓ { name } : { name } }
+ }
))
}