Merge branch 'master' into mg-compile-btn-text

pull/2257/head
Miladinho 3 years ago committed by GitHub
commit 61ad1ce23e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      apps/remix-ide-e2e/src/tests/plugin_api.ts
  2. 33
      apps/remix-ide-e2e/src/tests/providers.test.ts
  3. 3
      apps/remix-ide/src/app.js
  4. 114
      apps/remix-ide/src/app/tabs/abstract-provider.tsx
  5. 33
      apps/remix-ide/src/app/tabs/ganache-provider.tsx
  6. 106
      apps/remix-ide/src/app/tabs/hardhat-provider.tsx
  7. 48
      apps/remix-ide/src/app/udapp/run-tab.js
  8. 2
      apps/remix-ide/src/blockchain/blockchain.js
  9. 4
      apps/remix-ide/src/blockchain/execution-context.js
  10. 4
      apps/remix-ide/src/remixAppManager.js
  11. 1
      apps/remix-ide/src/remixEngine.js
  12. 2
      libs/remix-ui/helper/src/lib/helper-components.tsx
  13. 27
      libs/remix-ui/run-tab/src/lib/actions/index.ts
  14. 1
      libs/remix-ui/run-tab/src/lib/run-tab.tsx
  15. 1
      libs/remix-ui/run-tab/src/lib/types/run-tab.d.ts
  16. 1
      package.json

@ -355,10 +355,9 @@ module.exports = {
.frameParent()
.useCss()
.clickLaunchIcon('pluginManager')
.scrollAndClick('[data-id="pluginManagerComponentActivateButtonhardhat-provider"]')
.clickLaunchIcon('udapp')
.click('*[data-id="Hardhat Provider"]')
.modalFooterOKClick('hardhatprovider')
.modalFooterOKClick('hardhat-provider')
.waitForElementContainsText('*[data-id="settingsNetworkEnv"]', 'Custom') // e.g Custom (1337) network
.clickLaunchIcon('localPlugin')
.useXpath()

@ -0,0 +1,33 @@
'use strict'
import { NightwatchBrowser } from 'nightwatch'
import init from '../helpers/init'
module.exports = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done, 'http://127.0.0.1:8080', false)
},
'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"]')
.waitForElementVisible('*[data-id="ganache-providerModalDialogModalBody-react"]')
.execute(() => {
(document.querySelector('*[data-id="ganache-providerModalDialogModalBody-react"] input') as any).focus()
}, [], () => {})
.clearValue('*[data-id="ganache-providerModalDialogModalBody-react"] input')
.setValue('*[data-id="ganache-providerModalDialogModalBody-react"] input', 'http://127.0.0.1:8084')
.modalFooterOKClick('ganache-provider')
.waitForElementContainsText('*[data-id="ganache-providerModalDialogModalBody-react"]', 'Error while connecting to the provider')
.modalFooterOKClick('ganache-provider')
.waitForElementNotVisible('*[data-id="ganache-providerModalDialogModalBody-react"]')
.pause(1000)
},
'Should switch to ganache provider, use the default ganache URL and succeed to connect': function (browser: NightwatchBrowser) {
browser.click('*[data-id="Ganache Provider"]')
.waitForElementVisible('*[data-id="ganache-providerModalDialogModalBody-react"]')
.modalFooterOKClick('ganache-provider')
.waitForElementContainsText('*[data-id="settingsNetworkEnv"]', 'Custom (')
}
}

@ -26,6 +26,7 @@ import { Layout } from './app/panels/layout'
import { NotificationPlugin } from './app/plugins/notification'
import { Blockchain } from './blockchain/blockchain.js'
import { HardhatProvider } from './app/tabs/hardhat-provider'
import { GanacheProvider } from './app/tabs/ganache-provider'
const isElectron = require('is-electron')
@ -174,6 +175,7 @@ class AppComponent {
// ----------------- represent the current selected web3 provider ----
const web3Provider = new Web3ProviderModule(blockchain)
const hardhatProvider = new HardhatProvider(blockchain)
const ganacheProvider = new GanacheProvider(blockchain)
// ----------------- convert offset to line/column service -----------
const offsetToLineColumnConverter = new OffsetToLineColumnConverter()
Registry.getInstance().put({
@ -229,6 +231,7 @@ class AppComponent {
dGitProvider,
storagePlugin,
hardhatProvider,
ganacheProvider,
this.walkthroughService,
])

@ -0,0 +1,114 @@
import { Plugin } from '@remixproject/engine'
import { AppModal, AlertModal, ModalTypes } from '@remix-ui/app'
import { Blockchain } from '../../blockchain/blockchain'
import { ethers } from 'ethers'
type JsonDataRequest = {
id: number,
jsonrpc: string // version
method: string,
params: Array<any>,
}
type JsonDataResult = {
id: number,
jsonrpc: string // version
result: any
}
type RejectRequest = (error: Error) => void
type SuccessRequest = (data: JsonDataResult) => void
export abstract class AbstractProvider extends Plugin {
provider: ethers.providers.JsonRpcProvider
blocked: boolean
blockchain: Blockchain
defaultUrl: string
constructor (profile, blockchain, defaultUrl) {
super(profile)
this.defaultUrl = defaultUrl
this.provider = null
this.blocked = false // used to block any call when trying to recover after a failed connection.
this.blockchain = blockchain
}
abstract body(): JSX.Element
onDeactivation () {
this.provider = null
this.blocked = false
}
sendAsync (data: JsonDataRequest): Promise<any> {
return new Promise(async (resolve, reject) => {
if (this.blocked) return reject(new Error('provider unable to connect'))
// If provider is not set, allow to open modal only when provider is trying to connect
if (!this.provider) {
let value: string
try {
value = await ((): Promise<string> => {
return new Promise((resolve, reject) => {
const modalContent: AppModal = {
id: this.profile.name,
title: this.profile.displayName,
message: this.body(),
modalType: ModalTypes.prompt,
okLabel: 'OK',
cancelLabel: 'Cancel',
okFn: (value: string) => {
setTimeout(() => resolve(value), 0)
},
cancelFn: () => {
setTimeout(() => reject(new Error('Canceled')), 0)
},
hideFn: () => {
setTimeout(() => reject(new Error('Hide')), 0)
},
defaultValue: this.defaultUrl
}
this.call('notification', 'modal', modalContent)
})
})()
} catch (e) {
// the modal has been canceled/hide
const result = data.method === 'net_listening' ? 'canceled' : []
resolve({ jsonrpc: '2.0', result: result, id: data.id })
return
}
this.provider = new ethers.providers.JsonRpcProvider(value)
this.sendAsyncInternal(data, resolve, reject)
} else {
this.sendAsyncInternal(data, resolve, reject)
}
})
}
private async sendAsyncInternal (data: JsonDataRequest, resolve: SuccessRequest, reject: RejectRequest): Promise<void> {
if (this.provider) {
// 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.blockchain.getProvider() !== this.profile.displayName && data.method !== 'net_listening') return reject(new Error('Environment Updated !!'))
try {
const result = await this.provider.send(data.method, data.params)
resolve({ jsonrpc: '2.0', result, id: data.id })
} catch (error) {
this.blocked = true
const modalContent: AlertModal = {
id: this.profile.name,
title: this.profile.displayName,
message: `Error while connecting to the provider: ${error.message}`,
}
this.call('notification', 'alert', modalContent)
await this.call('udapp', 'setEnvironmentMode', { context: 'vm', fork: 'london' })
this.provider = null
setTimeout(_ => { this.blocked = false }, 1000) // we wait 1 second for letting remix to switch to vm
reject(error)
}
} else {
const result = data.method === 'net_listening' ? 'canceled' : []
resolve({ jsonrpc: '2.0', result: result, id: data.id })
}
}
}

@ -0,0 +1,33 @@
import * as packageJson from '../../../../../package.json'
import { Plugin } from '@remixproject/engine'
import { AppModal, AlertModal, ModalTypes } from '@remix-ui/app'
import React from 'react' // eslint-disable-line
import { Blockchain } from '../../blockchain/blockchain'
import { ethers } from 'ethers'
import { AbstractProvider } from './abstract-provider'
const profile = {
name: 'ganache-provider',
displayName: 'Ganache Provider',
kind: 'provider',
description: 'Ganache',
methods: ['sendAsync'],
version: packageJson.version
}
export class GanacheProvider extends AbstractProvider {
constructor (blockchain) {
super(profile, blockchain, 'http://127.0.0.1:8545')
}
body (): JSX.Element {
return (
<div> Note: To run Ganache on your system, run
<div className="border p-1">npm install -g ganache</div>
<div className="border p-1">ganache</div>
For more info, visit: <a href="https://github.com/trufflesuite/ganache" target="_blank">Ganache Documentation</a>
<div>Ganache JSON-RPC Endpoint:</div>
</div>
)
}
}

@ -4,6 +4,7 @@ import { AppModal, AlertModal, ModalTypes } from '@remix-ui/app'
import React from 'react' // eslint-disable-line
import { Blockchain } from '../../blockchain/blockchain'
import { ethers } from 'ethers'
import { AbstractProvider } from './abstract-provider'
const profile = {
name: 'hardhat-provider',
@ -14,41 +15,12 @@ const profile = {
version: packageJson.version
}
type JsonDataRequest = {
id: number,
jsonrpc: string // version
method: string,
params: Array<any>,
}
type JsonDataResult = {
id: number,
jsonrpc: string // version
result: any
}
type RejectRequest = (error: Error) => void
type SuccessRequest = (data: JsonDataResult) => void
export class HardhatProvider extends Plugin {
provider: ethers.providers.JsonRpcProvider
blocked: boolean
blockchain: Blockchain
target: String
export class HardhatProvider extends AbstractProvider {
constructor (blockchain) {
super(profile)
this.provider = null
this.blocked = false // used to block any call when trying to recover after a failed connection.
this.blockchain = blockchain
super(profile, blockchain, 'http://127.0.0.1:8545')
}
onDeactivation () {
this.provider = null
this.blocked = false
}
hardhatProviderDialogBody (): JSX.Element {
body (): JSX.Element {
return (
<div> Note: To run Hardhat network node on your system, go to hardhat project folder and run command:
<div className="border p-1">npx hardhat node</div>
@ -57,74 +29,4 @@ export class HardhatProvider extends Plugin {
</div>
)
}
sendAsync (data: JsonDataRequest): Promise<any> {
return new Promise(async (resolve, reject) => {
if (this.blocked) return reject(new Error('provider unable to connect'))
// If provider is not set, allow to open modal only when provider is trying to connect
if (!this.provider) {
let value: string
try {
value = await ((): Promise<string> => {
return new Promise((resolve, reject) => {
const modalContent: AppModal = {
id: 'hardhatprovider',
title: 'Hardhat node request',
message: this.hardhatProviderDialogBody(),
modalType: ModalTypes.prompt,
okLabel: 'OK',
cancelLabel: 'Cancel',
okFn: (value: string) => {
setTimeout(() => resolve(value), 0)
},
cancelFn: () => {
setTimeout(() => reject(new Error('Canceled')), 0)
},
hideFn: () => {
setTimeout(() => reject(new Error('Hide')), 0)
},
defaultValue: 'http://127.0.0.1:8545'
}
this.call('notification', 'modal', modalContent)
})
})()
} catch (e) {
// the modal has been canceled/hide
return
}
this.provider = new ethers.providers.JsonRpcProvider(value)
this.sendAsyncInternal(data, resolve, reject)
} else {
this.sendAsyncInternal(data, resolve, reject)
}
})
}
private async sendAsyncInternal (data: JsonDataRequest, resolve: SuccessRequest, reject: RejectRequest): Promise<void> {
if (this.provider) {
// 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.blockchain.getProvider() !== 'Hardhat Provider' && data.method !== 'net_listening') return reject(new Error('Environment Updated !!'))
try {
const result = await this.provider.send(data.method, data.params)
resolve({ jsonrpc: '2.0', result, id: data.id })
} catch (error) {
this.blocked = true
const modalContent: AlertModal = {
id: 'hardhatprovider',
title: 'Hardhat Provider',
message: `Error while connecting to the hardhat provider: ${error.message}`,
}
this.call('notification', 'alert', modalContent)
await this.call('udapp', 'setEnvironmentMode', { context: 'vm', fork: 'london' })
this.provider = null
setTimeout(_ => { this.blocked = false }, 1000) // we wait 1 second for letting remix to switch to vm
reject(error)
}
} else {
const result = data.method === 'net_listening' ? 'canceled' : []
resolve({ jsonrpc: '2.0', result: result, id: data.id })
}
}
}

@ -39,7 +39,6 @@ export class RunTab extends ViewPlugin {
this.el = document.createElement('div')
}
setupEvents () {
this.blockchain.events.on('newTransaction', (tx, receipt) => {
this.emit('newTransaction', tx, receipt)
@ -93,11 +92,56 @@ export class RunTab extends ViewPlugin {
return <div><RunTabUI plugin={this} /></div>
}
onReady (api) {
this.REACT_API = api
}
async onInitDone () {
const udapp = this // eslint-disable-line
await this.call('blockchain', 'addProvider', {
name: 'Hardhat Provider',
provider: {
async sendAsync (payload, callback) {
try {
const result = await udapp.call('hardhat-provider', 'sendAsync', payload)
callback(null, result)
} catch (e) {
callback(e)
}
}
}
})
await this.call('blockchain', 'addProvider', {
name: 'Ganache Provider',
provider: {
async sendAsync (payload, callback) {
try {
const result = await udapp.call('ganache-provider', 'sendAsync', payload)
callback(null, result)
} catch (e) {
callback(e)
}
}
}
})
await this.call('blockchain', 'addProvider', {
name: 'Wallet Connect',
provider: {
async sendAsync (payload, callback) {
try {
const result = await udapp.call('walletconnect', 'sendAsync', payload)
callback(null, result)
} catch (e) {
callback(e)
}
}
}
})
}
writeFile (fileName, content) {
return this.call('fileManager', 'writeFile', fileName, content)
}

@ -22,7 +22,7 @@ const profile = {
name: 'blockchain',
displayName: 'Blockchain',
description: 'Blockchain - Logic',
methods: ['getCode', 'getTransactionReceipt'],
methods: ['getCode', 'getTransactionReceipt', 'addProvider', 'removeProvider'],
version: packageJson.version
}

@ -3,6 +3,7 @@
import Web3 from 'web3'
import { execution } from '@remix-project/remix-lib'
import EventManager from '../lib/events'
const _paq = window._paq = window._paq || []
let web3
@ -128,6 +129,7 @@ export class ExecutionContext {
}
async executionContextChange (value, endPointUrl, confirmCb, infoCb, cb) {
_paq.push(['trackEvent', 'udapp', 'providerChanged', value.context])
const context = value.context
if (!cb) cb = () => { /* Do nothing. */ }
if (!confirmCb) confirmCb = () => { /* Do nothing. */ }
@ -220,7 +222,7 @@ export class ExecutionContext {
cb()
} else {
web3.setProvider(oldProvider)
cb('Not possible to connect to the Web3 provider. Make sure the provider is running, a connection is open (via IPC or RPC) or that the provider plugin is properly configured.')
cb(`Not possible to connect to ${context}. Make sure the provider is running, a connection is open (via IPC or RPC) or that the provider plugin is properly configured.`)
}
})
}

@ -8,12 +8,12 @@ const requiredModules = [ // services + layout views + system views
'manager', 'config', 'compilerArtefacts', 'compilerMetadata', 'contextualListener', 'editor', 'offsetToLineColumnConverter', 'network', 'theme',
'fileManager', 'contentImport', 'blockchain', 'web3Provider', 'scriptRunner', 'fetchAndCompile', 'mainPanel', 'hiddenPanel', 'sidePanel', 'menuicons',
'filePanel', 'terminal', 'settings', 'pluginManager', 'tabs', 'udapp', 'dGitProvider', 'solidity-logic', 'gistHandler', 'layout',
'notification', 'permissionhandler', 'walkthrough', 'storage', 'restorebackupzip', 'link-libraries', 'deploy-libraries', 'intelligentScriptExecutor']
'notification', 'permissionhandler', 'walkthrough', 'storage', 'restorebackupzip', 'link-libraries', 'deploy-libraries', 'hardhat-provider', 'intelligentScriptExecutor']
const dependentModules = ['git', 'hardhat', 'truffle', 'slither'] // module which shouldn't be manually activated (e.g git is activated by remixd)
export function isNative (name) {
const nativePlugins = ['vyper', 'workshops', 'debugger', 'remixd', 'menuicons', 'solidity', 'hardhat-provider', 'solidityStaticAnalysis', 'solidityUnitTesting', 'layout', 'notification']
const nativePlugins = ['vyper', 'workshops', 'debugger', 'remixd', 'menuicons', 'solidity', 'hardhat-provider', 'solidityStaticAnalysis', 'solidityUnitTesting', 'layout', 'notification', 'hardhat-provider', 'ganache-provider']
return nativePlugins.includes(name) || requiredModules.includes(name)
}

@ -18,6 +18,7 @@ export class RemixEngine extends Engine {
if (name === 'notification') return { queueTimeout: 60000 * 4 }
if (name === 'sourcify') return { queueTimeout: 60000 * 4 }
if (name === 'fetchAndCompile') return { queueTimeout: 60000 * 4 }
if (name === 'walletconnect') return { queueTimeout: 60000 * 4 }
return { queueTimeout: 10000 }
}

@ -64,7 +64,7 @@ export const envChangeNotification = (env: { context: string, fork: string }, fr
<span>
{ from + ' '}
<span className="font-weight-bold text-warning">
is changing your environment to
set your environment to
</span> {env && env.context}
</span>
</div>

@ -64,10 +64,6 @@ const setupEvents = () => {
plugin.blockchain.event.register('removeProvider', name => removeExternalProvider(name))
plugin.on('manager', 'pluginActivated', addPluginProvider.bind(plugin))
plugin.on('manager', 'pluginDeactivated', removePluginProvider.bind(plugin))
plugin.on('solidity', 'compilationFinished', (file, source, languageVersion, data, input, version) => broadcastCompilationResult(file, source, languageVersion, data, input))
plugin.on('vyper', 'compilationFinished', (file, source, languageVersion, data) => broadcastCompilationResult(file, source, languageVersion, data))
@ -186,28 +182,6 @@ export const setGasFee = (value: number) => {
dispatch(setGasLimit(value))
}
const addPluginProvider = (profile) => {
if (profile.kind === 'provider') {
((profile, app) => {
const web3Provider = {
async sendAsync (payload, callback) {
try {
const result = await app.call(profile.name, 'sendAsync', payload)
callback(null, result)
} catch (e) {
callback(e)
}
}
}
app.blockchain.addProvider({ name: profile.displayName, provider: web3Provider })
})(profile, plugin)
}
}
const removePluginProvider = (profile) => {
if (profile.kind === 'provider') plugin.blockchain.removeProvider(profile.displayName)
}
const setFinalContext = () => {
// set the final context. Cause it is possible that this is not the one we've originaly selected
const value = _getProviderDropdownValue()
@ -233,7 +207,6 @@ export const setNetworkNameFromProvider = (networkName: string) => {
const addExternalProvider = (network) => {
dispatch(addProvider(network))
dispatch(displayPopUp(`${network.name} provider added`))
}
const removeExternalProvider = (name) => {

@ -61,6 +61,7 @@ export function RunTabUI (props: RunTabProps) {
useEffect(() => {
initRunTab(plugin)(dispatch)
plugin.onInitDone()
}, [plugin])
useEffect(() => {

@ -34,6 +34,7 @@ export class RunTab extends ViewPlugin {
udappUI: any;
renderComponent(): void;
onReady(api: any): void;
onInitDone(): void;
recorder: Recorder;
}
import { ViewPlugin } from "@remixproject/engine-web/lib/view";

@ -98,6 +98,7 @@
"nightwatch_local_migrate_filesystem": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/migrateFileSystem.test.js --env=chrome",
"nightwatch_local_stress_editor": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/stressEditor.test.js --env=chromeDesktop",
"nightwatch_local_search": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/search.test.js --env=chromeDesktop",
"nightwatch_local_providers": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/providers.test.js --env=chromeDesktop",
"onchange": "onchange apps/remix-ide/build/app.js -- npm-run-all lint",
"remixd": "nx build remixd && chmod +x dist/libs/remixd/src/bin/remixd.js && dist/libs/remixd/src/bin/remixd.js -s ./apps/remix-ide/contracts --remix-ide http://127.0.0.1:8080",
"selenium": "selenium-standalone start",

Loading…
Cancel
Save