Merge pull request #2584 from ethereum/l2_providers

add L2 networks
pull/5370/head
yann300 2 years ago committed by GitHub
commit 97f6c133b2
  1. 18
      apps/remix-ide-e2e/src/commands/switchEnvironment.ts
  2. 10
      apps/remix-ide-e2e/src/tests/ballot.test.ts
  3. 2
      apps/remix-ide-e2e/src/tests/ballot_0_4_11.test.ts
  4. 8
      apps/remix-ide-e2e/src/tests/debugger.test.ts
  5. 4
      apps/remix-ide-e2e/src/tests/plugin_api.ts
  6. 8
      apps/remix-ide-e2e/src/tests/providers.test.ts
  7. 2
      apps/remix-ide-e2e/src/tests/runAndDeploy.test.ts
  8. 4
      apps/remix-ide-e2e/src/tests/terminal.test.ts
  9. 2
      apps/remix-ide-e2e/src/tests/transactionExecution.test.ts
  10. 1
      apps/remix-ide-e2e/src/types/index.d.ts
  11. 6
      apps/remix-ide/src/app.js
  12. 8
      apps/remix-ide/src/app/tabs/abstract-provider.tsx
  13. 21
      apps/remix-ide/src/app/tabs/injected-arbitrum-one-provider.tsx
  14. 21
      apps/remix-ide/src/app/tabs/injected-optimism-provider.tsx
  15. 75
      apps/remix-ide/src/app/tabs/injected-provider.tsx
  16. 34
      apps/remix-ide/src/app/udapp/run-tab.js
  17. 13
      apps/remix-ide/src/blockchain/execution-context.js
  18. 2
      apps/remix-ide/src/remixAppManager.js
  19. 1
      libs/remix-ui/helper/src/index.ts
  20. 0
      libs/remix-ui/helper/src/lib/components/custom-dropdown.tsx
  21. 56
      libs/remix-ui/run-tab/src/lib/components/environment.tsx
  22. 3
      libs/remix-ui/run-tab/src/lib/css/run-tab.css
  23. 2
      libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx

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

@ -83,7 +83,7 @@ module.exports = {
browser browser
.openFile('Untitled.sol') .openFile('Untitled.sol')
.clickLaunchIcon('udapp') .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"]') .waitForElementPresent('[data-id="basic-http-provider-modal-footer-ok-react"]')
.execute(function () { .execute(function () {
const modal = document.querySelector('[data-id="basic-http-provider-modal-footer-ok-react"]') as any const modal = document.querySelector('[data-id="basic-http-provider-modal-footer-ok-react"]') as any
@ -91,13 +91,7 @@ module.exports = {
modal.click() modal.click()
}) })
.pause(5000) .pause(5000)
.execute(function () { .waitForElementContainsText('#selectExEnvOptions button', 'External Http Provider')
const env: any = document.getElementById('selectExEnvOptions')
return env.value
}, [], function (result) {
browser.assert.ok(result.value === 'External Http Provider', 'Web3 Provider not selected')
})
.clickLaunchIcon('solidity') .clickLaunchIcon('solidity')
.clickLaunchIcon('udapp') .clickLaunchIcon('udapp')
.pause(2000) .pause(2000)

@ -78,7 +78,7 @@ module.exports = {
browser browser
.openFile('Untitled.sol') .openFile('Untitled.sol')
.clickLaunchIcon('udapp') .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"]') .waitForElementPresent('[data-id="basic-http-provider-modal-footer-ok-react"]')
.execute(function () { .execute(function () {
const modal = document.querySelector('[data-id="basic-http-provider-modal-footer-ok-react"]') as any const modal = document.querySelector('[data-id="basic-http-provider-modal-footer-ok-react"]') as any

@ -214,10 +214,10 @@ module.exports = {
.setSolidityCompilerVersion('soljson-v0.8.7+commit.e28d00a7.js') .setSolidityCompilerVersion('soljson-v0.8.7+commit.e28d00a7.js')
.addFile('useDebugNodes.sol', sources[5]['useDebugNodes.sol']) // compile contract .addFile('useDebugNodes.sol', sources[5]['useDebugNodes.sol']) // compile contract
.clickLaunchIcon('udapp') .clickLaunchIcon('udapp')
.click('*[data-id="settingsSelectEnvOptions"] *[data-id="External Http Provider"]') // select web3 provider with debug nodes URL .switchEnvironment('External Http Provider') // select web3 provider with debug nodes URL
.clearValue('*[data-id="modalDialogCustomPromp"]') .clearValue('*[data-id="modalDialogCustomPromptText"]')
.setValue('*[data-id="modalDialogCustomPromp"]', 'https://remix-rinkeby.ethdevops.io') .setValue('*[data-id="modalDialogCustomPromptText"]', 'https://remix-rinkeby.ethdevops.io')
.modalFooterOKClick('basic-http-provider') .modalFooterOKClick()
.waitForElementPresent('*[title="Deploy - transact (not payable)"]', 65000) // wait for the compilation to succeed .waitForElementPresent('*[title="Deploy - transact (not payable)"]', 65000) // wait for the compilation to succeed
.clickLaunchIcon('debugger') .clickLaunchIcon('debugger')
.clearValue('*[data-id="debuggerTransactionInput"]') .clearValue('*[data-id="debuggerTransactionInput"]')

@ -188,7 +188,7 @@ module.exports = {
.frameParent() .frameParent()
.useCss() .useCss()
.clickLaunchIcon('udapp') .clickLaunchIcon('udapp')
.waitForElementContainsText('#selectExEnvOptions option:checked', 'Remix VM (Berlin)') .waitForElementContainsText('#selectExEnvOptions button', 'Remix VM (Berlin)')
.clickLaunchIcon('localPlugin') .clickLaunchIcon('localPlugin')
.useXpath() .useXpath()
// @ts-ignore // @ts-ignore
@ -391,7 +391,7 @@ module.exports = {
.useCss() .useCss()
.clickLaunchIcon('pluginManager') .clickLaunchIcon('pluginManager')
.clickLaunchIcon('udapp') .clickLaunchIcon('udapp')
.click('*[data-id="Hardhat Provider"]') .switchEnvironment('Hardhat Provider')
.modalFooterOKClick('hardhat-provider') .modalFooterOKClick('hardhat-provider')
.waitForElementContainsText('*[data-id="settingsNetworkEnv"]', 'Custom') // e.g Custom (1337) network .waitForElementContainsText('*[data-id="settingsNetworkEnv"]', 'Custom') // e.g Custom (1337) network
.clickLaunchIcon('localPlugin') .clickLaunchIcon('localPlugin')

@ -10,7 +10,7 @@ module.exports = {
'Should switch to ganache provider, set a custom URL and fail to connect': function (browser: NightwatchBrowser) { 'Should switch to ganache provider, set a custom URL and fail to connect': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('div[data-id="remixIdeIconPanel"]', 10000) browser.waitForElementVisible('div[data-id="remixIdeIconPanel"]', 10000)
.clickLaunchIcon('udapp') .clickLaunchIcon('udapp')
.click('*[data-id="Ganache Provider"]') .switchEnvironment('Ganache Provider')
.waitForElementVisible('*[data-id="ganache-providerModalDialogModalBody-react"]') .waitForElementVisible('*[data-id="ganache-providerModalDialogModalBody-react"]')
.execute(() => { .execute(() => {
(document.querySelector('*[data-id="ganache-providerModalDialogModalBody-react"] input') as any).focus() (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) { '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"]') .waitForElementVisible('*[data-id="ganache-providerModalDialogModalBody-react"]')
.modalFooterOKClick('ganache-provider') .modalFooterOKClick('ganache-provider')
.waitForElementContainsText('*[data-id="settingsNetworkEnv"]', 'Custom (') .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) { 'Should switch to foundry provider, set a custom URL and fail to connect': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('div[data-id="remixIdeIconPanel"]', 10000) browser.waitForElementVisible('div[data-id="remixIdeIconPanel"]', 10000)
.click('*[data-id="Foundry Provider"]') .switchEnvironment('Foundry Provider')
.waitForElementVisible('*[data-id="foundry-providerModalDialogModalBody-react"]') .waitForElementVisible('*[data-id="foundry-providerModalDialogModalBody-react"]')
.execute(() => { .execute(() => {
(document.querySelector('*[data-id="foundry-providerModalDialogModalBody-react"] input') as any).focus() (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) { '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"]') .waitForElementVisible('*[data-id="foundry-providerModalDialogModalBody-react"]')
.modalFooterOKClick('foundry-provider') .modalFooterOKClick('foundry-provider')
.waitForElementContainsText('*[data-id="settingsNetworkEnv"]', 'Custom (') .waitForElementContainsText('*[data-id="settingsNetworkEnv"]', 'Custom (')

@ -32,7 +32,7 @@ module.exports = {
'Should sign message using account key #group2': function (browser: NightwatchBrowser) { 'Should sign message using account key #group2': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="settingsRemixRunSignMsg"]') browser.waitForElementVisible('*[data-id="settingsRemixRunSignMsg"]')
.click('select[id="selectExEnvOptions"] option[value="vm-berlin"]') .switchEnvironment('vm-berlin')
.pause(2000) .pause(2000)
.click('*[data-id="settingsRemixRunSignMsg"]') .click('*[data-id="settingsRemixRunSignMsg"]')
.pause(2000) .pause(2000)

@ -50,7 +50,7 @@ module.exports = {
browser browser
.click('*[data-id="terminalClearConsole"]') // clear the terminal .click('*[data-id="terminalClearConsole"]') // clear the terminal
.clickLaunchIcon('udapp') .clickLaunchIcon('udapp')
.click('*[data-id="settingsSelectEnvOptions"] *[data-id="External Http Provider"]') .switchEnvironment('External Http Provider')
.modalFooterOKClick('basic-http-provider') .modalFooterOKClick('basic-http-provider')
.executeScript('web3.eth.getAccounts()') .executeScript('web3.eth.getAccounts()')
.waitForElementContainsText('*[data-id="terminalJournal"]', '["', 60000) // we check if an array is present, don't need to check for the content .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 browser
.clickLaunchIcon('settings') .clickLaunchIcon('settings')
.clickLaunchIcon('udapp') .clickLaunchIcon('udapp')
.click('*[data-id="settingsVMLondonMode"]') .switchEnvironment('vm-london')
.click('*[data-id="terminalClearConsole"]') // clear the terminal .click('*[data-id="terminalClearConsole"]') // clear the terminal
.clickLaunchIcon('filePanel') .clickLaunchIcon('filePanel')
.click('*[data-id="treeViewDivtreeViewItem"]') // make sure we create the file at the root folder .click('*[data-id="treeViewDivtreeViewItem"]') // make sure we create the file at the root folder

@ -161,7 +161,7 @@ module.exports = {
browser browser
.clickLaunchIcon('udapp') .clickLaunchIcon('udapp')
.clearTransactions() .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 .selectAccount('0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c') // this account will be used for this test suite
.click('.udapp_contractActionsContainerSingle > button') .click('.udapp_contractActionsContainerSingle > button')
.clickInstance(0) .clickInstance(0)

@ -64,6 +64,7 @@ declare module 'nightwatch' {
getBrowserLogs (this: NightwatchBrowser): NightwatchBrowser getBrowserLogs (this: NightwatchBrowser): NightwatchBrowser
currentSelectedFileIs (name: string): NightwatchBrowser, currentSelectedFileIs (name: string): NightwatchBrowser,
switchWorkspace: (workspaceName: string) => NightwatchBrowser switchWorkspace: (workspaceName: string) => NightwatchBrowser
switchEnvironment: (provider: string) => NightwatchBrowser
} }
export interface NightwatchBrowser { export interface NightwatchBrowser {

@ -29,6 +29,8 @@ import { HardhatProvider } from './app/tabs/hardhat-provider'
import { GanacheProvider } from './app/tabs/ganache-provider' import { GanacheProvider } from './app/tabs/ganache-provider'
import { FoundryProvider } from './app/tabs/foundry-provider' import { FoundryProvider } from './app/tabs/foundry-provider'
import { ExternalHttpProvider } from './app/tabs/external-http-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') const isElectron = require('is-electron')
@ -181,6 +183,8 @@ class AppComponent {
const ganacheProvider = new GanacheProvider(blockchain) const ganacheProvider = new GanacheProvider(blockchain)
const foundryProvider = new FoundryProvider(blockchain) const foundryProvider = new FoundryProvider(blockchain)
const externalHttpProvider = new ExternalHttpProvider(blockchain) const externalHttpProvider = new ExternalHttpProvider(blockchain)
const injected0ptimismProvider = new Injected0ptimismProvider(blockchain)
const injectedArbitrumOneProvider = new InjectedArbitrumOneProvider(blockchain)
// ----------------- convert offset to line/column service ----------- // ----------------- convert offset to line/column service -----------
const offsetToLineColumnConverter = new OffsetToLineColumnConverter() const offsetToLineColumnConverter = new OffsetToLineColumnConverter()
Registry.getInstance().put({ Registry.getInstance().put({
@ -239,6 +243,8 @@ class AppComponent {
ganacheProvider, ganacheProvider,
foundryProvider, foundryProvider,
externalHttpProvider, externalHttpProvider,
injected0ptimismProvider,
injectedArbitrumOneProvider,
this.walkthroughService, this.walkthroughService,
search search
]) ])

@ -3,21 +3,21 @@ import { AppModal, AlertModal, ModalTypes } from '@remix-ui/app'
import { Blockchain } from '../../blockchain/blockchain' import { Blockchain } from '../../blockchain/blockchain'
import { ethers } from 'ethers' import { ethers } from 'ethers'
type JsonDataRequest = { export type JsonDataRequest = {
id: number, id: number,
jsonrpc: string // version jsonrpc: string // version
method: string, method: string,
params: Array<any>, params: Array<any>,
} }
type JsonDataResult = { export type JsonDataResult = {
id: number, id: number,
jsonrpc: string // version jsonrpc: string // version
result: any result: any
} }
type RejectRequest = (error: Error) => void export type RejectRequest = (error: Error) => void
type SuccessRequest = (data: JsonDataResult) => void export type SuccessRequest = (data: JsonDataResult) => void
export abstract class AbstractProvider extends Plugin { export abstract class AbstractProvider extends Plugin {
provider: ethers.providers.JsonRpcProvider provider: ethers.providers.JsonRpcProvider

@ -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']
}
}

@ -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']
}
}

@ -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<string>
constructor (profile) {
super(profile)
if ((window as any).ethereum) {
this.provider = new Web3((window as any).ethereum)
}
}
sendAsync (data: JsonDataRequest): Promise<any> {
return new Promise((resolve, reject) => {
this.sendAsyncInternal(data, resolve, reject)
})
}
private async sendAsyncInternal (data: JsonDataRequest, resolve: SuccessRequest, reject: RejectRequest): Promise<void> {
// 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<string>) => {
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
}
}

@ -102,6 +102,7 @@ export class RunTab extends ViewPlugin {
await this.call('blockchain', 'addProvider', { await this.call('blockchain', 'addProvider', {
name: 'Hardhat Provider', name: 'Hardhat Provider',
isInjected: false,
provider: { provider: {
async sendAsync (payload, callback) { async sendAsync (payload, callback) {
try { try {
@ -116,6 +117,7 @@ export class RunTab extends ViewPlugin {
await this.call('blockchain', 'addProvider', { await this.call('blockchain', 'addProvider', {
name: 'Ganache Provider', name: 'Ganache Provider',
isInjected: false,
provider: { provider: {
async sendAsync (payload, callback) { async sendAsync (payload, callback) {
try { try {
@ -130,6 +132,7 @@ export class RunTab extends ViewPlugin {
await this.call('blockchain', 'addProvider', { await this.call('blockchain', 'addProvider', {
name: 'Foundry Provider', name: 'Foundry Provider',
isInjected: false,
provider: { provider: {
async sendAsync (payload, callback) { async sendAsync (payload, callback) {
try { try {
@ -144,6 +147,7 @@ export class RunTab extends ViewPlugin {
await this.call('blockchain', 'addProvider', { await this.call('blockchain', 'addProvider', {
name: 'Wallet Connect', name: 'Wallet Connect',
isInjected: false,
provider: { provider: {
async sendAsync (payload, callback) { async sendAsync (payload, callback) {
try { try {
@ -169,6 +173,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) { writeFile (fileName, content) {

@ -155,6 +155,9 @@ export class ExecutionContext {
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 Web3 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() return cb()
} else { } 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.askPermission()
this.executionContext = context this.executionContext = context
web3.setProvider(injectedProvider) web3.setProvider(injectedProvider)
@ -166,10 +169,20 @@ export class ExecutionContext {
if (this.customNetWorks[context]) { if (this.customNetWorks[context]) {
var network = this.customNetWorks[context] var network = this.customNetWorks[context]
if (!this.customNetWorks[context].isInjected) {
this.setProviderFromEndpoint(network.provider, { context: network.name }, (error) => { this.setProviderFromEndpoint(network.provider, { context: network.name }, (error) => {
if (error) infoCb(error) if (error) infoCb(error)
cb() cb()
}) })
} else {
// injected
this.askPermission()
this.executionContext = context
web3.setProvider(network.provider)
await this._updateChainContext()
this.event.trigger('contextChanged', [context])
return cb()
}
} }
} }

@ -19,7 +19,7 @@ const sensitiveCalls = {
} }
export function isNative(name) { 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) return nativePlugins.includes(name) || requiredModules.includes(name)
} }

@ -1,3 +1,4 @@
export * from './lib/remix-ui-helper' export * from './lib/remix-ui-helper'
export * from './lib/helper-components' export * from './lib/helper-components'
export * from './lib/components/PluginViewWrapper' export * from './lib/components/PluginViewWrapper'
export * from './lib/components/custom-dropdown'

@ -1,8 +1,13 @@
// eslint-disable-next-line no-use-before-define // eslint-disable-next-line no-use-before-define
import React from 'react' import React from 'react'
import { EnvironmentProps } from '../types' 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) { export function EnvironmentUI (props: EnvironmentProps) {
const handleChangeExEnv = (env: string) => { const handleChangeExEnv = (env: string) => {
const provider = props.providers.providerList.find(exEnv => exEnv.value === env) 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 (web3 provider / injected)
@ -13,22 +18,55 @@ export function EnvironmentUI (props: EnvironmentProps) {
props.setExecutionContext({ context, fork }) props.setExecutionContext({ context, fork })
} }
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 ( return (
<div className="udapp_crow"> <div className="udapp_crow">
<label id="selectExEnv" className="udapp_settingsLabel"> <label id="selectExEnv" className="udapp_settingsLabel">
Environment Environment <OverlayTrigger placement={'right'} overlay={
<Tooltip className="text-nowrap" id="info-recorder">
<span>Open chainlist and add a new provider for the chain you want to interact to.</span>
</Tooltip>
}>
<a href='https://chainlist.org/' target='_blank'><i style={{ fontSize: 'medium' }} className={'ml-2 fad fa-plug'} aria-hidden="true"></i></a>
</OverlayTrigger>
</label> </label>
<div className="udapp_environment"> <div className="udapp_environment">
<select id="selectExEnvOptions" data-id="settingsSelectEnvOptions" className="form-control udapp_select custom-select" value={props.selectedEnv || ''} onChange={(e) => { handleChangeExEnv(e.target.value) }}>
<Dropdown id="selectExEnvOptions" data-id="settingsSelectEnvOptions" className='udapp_selectExEnvOptions'>
<Dropdown.Toggle as={CustomToggle} id="dropdown-custom-components" className="btn btn-light btn-block w-100 d-inline-block border border-dark form-control" icon={null}>
{ isL2(currentProvider) && 'L2 - '}
{ currentProvider && currentProvider.content }
{ currentProvider && bridges[currentProvider.value] && <OverlayTrigger placement={'right'} overlay={
<Tooltip className="text-nowrap" id="info-recorder">
<span>Click to open a bridge for converting L1 mainnet ETH to the selected network currency.</span>
</Tooltip>
}>
<i style={{ fontSize: 'medium' }} className={'ml-2 fal fa-plug'} aria-hidden="true" onClick={() => { window.open(bridges[currentProvider.value], '_blank') }}></i>
</OverlayTrigger>}
</Dropdown.Toggle>
<Dropdown.Menu as={CustomMenu} className='w-100 custom-dropdown-items' data-id="custom-dropdown-items" >
{ {
props.providers.providerList.map((provider, index) => props.providers.providerList.map(({ content, value }, index) => (
<option id={provider.id} key={index} data-id={provider.dataId} <Dropdown.Item
title={provider.title} key={index}
value={provider.value}> { provider.content } onClick={() => {
</option> handleChangeExEnv(value)
) }}
data-id={`dropdown-item-${value}`}
>
<span className="pl-3">{ isL2({ value }) && 'L2 - ' }{ content }</span>
</Dropdown.Item>
))
} }
</select> </Dropdown.Menu>
</Dropdown>
<a href="https://remix-ide.readthedocs.io/en/latest/run.html#environment" target="_blank" rel="noreferrer"><i className="udapp_infoDeployAction ml-2 fas fa-info" title="Click for docs about Environment"></i></a> <a href="https://remix-ide.readthedocs.io/en/latest/run.html#environment" target="_blank" rel="noreferrer"><i className="udapp_infoDeployAction ml-2 fas fa-info" title="Click for docs about Environment"></i></a>
</div> </div>
</div> </div>

@ -529,4 +529,7 @@
text-decoration: none; text-decoration: none;
background-color: #007aa6; background-color: #007aa6;
} }
.udapp_selectExEnvOptions {
width: 100%;
}

@ -1,6 +1,6 @@
import React, { useState, useEffect, useRef, useContext } from 'react' // eslint-disable-line import React, { useState, useEffect, useRef, useContext } from 'react' // eslint-disable-line
import { Dropdown } from 'react-bootstrap' 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 { FileExplorer } from './components/file-explorer' // eslint-disable-line
import { FileSystemContext } from './contexts' import { FileSystemContext } from './contexts'
import './css/remix-ui-workspace.css' import './css/remix-ui-workspace.css'

Loading…
Cancel
Save