activate hardhat provider at load

pull/5370/head
yann300 3 years ago
parent 968f8bc4bb
commit 2069d768c7
  1. 123
      apps/remix-ide/src/app/tabs/abstract-provider.tsx
  2. 106
      apps/remix-ide/src/app/tabs/hardhat-provider.tsx
  3. 8
      apps/remix-ide/src/app/udapp/run-tab.js
  4. 2
      apps/remix-ide/src/remixAppManager.js
  5. 3
      libs/remix-ui/run-tab/src/lib/actions/index.ts
  6. 1
      libs/remix-ui/run-tab/src/lib/run-tab.tsx
  7. 1
      libs/remix-ui/run-tab/src/lib/types/run-tab.d.ts

@ -0,0 +1,123 @@
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'
const profile = {
name: 'hardhat-provider',
displayName: 'Hardhat Provider',
kind: 'provider',
description: 'Hardhat provider',
methods: ['sendAsync'],
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 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
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 })
}
}
}

@ -4,6 +4,7 @@ import { AppModal, AlertModal, ModalTypes } from '@remix-ui/app'
import React from 'react' // eslint-disable-line import React from 'react' // eslint-disable-line
import { Blockchain } from '../../blockchain/blockchain' import { Blockchain } from '../../blockchain/blockchain'
import { ethers } from 'ethers' import { ethers } from 'ethers'
import { AbstractProvider } from './abstract-provider'
const profile = { const profile = {
name: 'hardhat-provider', name: 'hardhat-provider',
@ -14,41 +15,12 @@ const profile = {
version: packageJson.version version: packageJson.version
} }
type JsonDataRequest = { export class HardhatProvider extends AbstractProvider {
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
constructor (blockchain) { constructor (blockchain) {
super(profile) super(profile, blockchain, 'http://127.0.0.1:8545')
this.provider = null
this.blocked = false // used to block any call when trying to recover after a failed connection.
this.blockchain = blockchain
} }
onDeactivation () { body (): JSX.Element {
this.provider = null
this.blocked = false
}
hardhatProviderDialogBody (): JSX.Element {
return ( return (
<div> Note: To run Hardhat network node on your system, go to hardhat project folder and run command: <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> <div className="border p-1">npx hardhat node</div>
@ -57,74 +29,4 @@ export class HardhatProvider extends Plugin {
</div> </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') this.el = document.createElement('div')
} }
setupEvents () { setupEvents () {
this.blockchain.events.on('newTransaction', (tx, receipt) => { this.blockchain.events.on('newTransaction', (tx, receipt) => {
this.emit('newTransaction', tx, receipt) this.emit('newTransaction', tx, receipt)
@ -93,9 +92,12 @@ export class RunTab extends ViewPlugin {
return <div><RunTabUI plugin={this} /></div> return <div><RunTabUI plugin={this} /></div>
} }
onReady (api) { onReady (api) {
this.REACT_API = api this.REACT_API = api
}
onInitDone () {
this.call('manager', 'activatePlugin', 'hardhat-provider')
} }
writeFile (fileName, content) { writeFile (fileName, content) {

@ -8,7 +8,7 @@ const requiredModules = [ // services + layout views + system views
'manager', 'config', 'compilerArtefacts', 'compilerMetadata', 'contextualListener', 'editor', 'offsetToLineColumnConverter', 'network', 'theme', 'manager', 'config', 'compilerArtefacts', 'compilerMetadata', 'contextualListener', 'editor', 'offsetToLineColumnConverter', 'network', 'theme',
'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-logic', 'gistHandler', 'layout', '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) const dependentModules = ['git', 'hardhat', 'truffle', 'slither'] // module which shouldn't be manually activated (e.g git is activated by remixd)

@ -188,6 +188,7 @@ export const setGasFee = (value: number) => {
const addPluginProvider = (profile) => { const addPluginProvider = (profile) => {
if (profile.kind === 'provider') { if (profile.kind === 'provider') {
console.log(profile);
((profile, app) => { ((profile, app) => {
const web3Provider = { const web3Provider = {
async sendAsync (payload, callback) { async sendAsync (payload, callback) {
@ -232,8 +233,8 @@ export const setNetworkNameFromProvider = (networkName: string) => {
} }
const addExternalProvider = (network) => { const addExternalProvider = (network) => {
console.log('adding ext provider', network)
dispatch(addProvider(network)) dispatch(addProvider(network))
dispatch(displayPopUp(`${network.name} provider added`))
} }
const removeExternalProvider = (name) => { const removeExternalProvider = (name) => {

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

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

Loading…
Cancel
Save