From 6926ab13d73b56b7bce37a133fa300ca1e7b0ef1 Mon Sep 17 00:00:00 2001 From: yann300 Date: Fri, 18 Mar 2022 18:38:53 +0100 Subject: [PATCH] activate hardhat provider at load --- .../src/app/tabs/abstract-provider.tsx | 123 ++++++++++++++++++ .../src/app/tabs/hardhat-provider.tsx | 106 +-------------- apps/remix-ide/src/app/udapp/run-tab.js | 8 +- apps/remix-ide/src/remixAppManager.js | 2 +- .../remix-ui/run-tab/src/lib/actions/index.ts | 3 +- libs/remix-ui/run-tab/src/lib/run-tab.tsx | 1 + .../run-tab/src/lib/types/run-tab.d.ts | 1 + 7 files changed, 137 insertions(+), 107 deletions(-) create mode 100644 apps/remix-ide/src/app/tabs/abstract-provider.tsx diff --git a/apps/remix-ide/src/app/tabs/abstract-provider.tsx b/apps/remix-ide/src/app/tabs/abstract-provider.tsx new file mode 100644 index 0000000000..90ba5af3b5 --- /dev/null +++ b/apps/remix-ide/src/app/tabs/abstract-provider.tsx @@ -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, +} + +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 { + 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 => { + 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 { + 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 }) + } + } +} diff --git a/apps/remix-ide/src/app/tabs/hardhat-provider.tsx b/apps/remix-ide/src/app/tabs/hardhat-provider.tsx index d17f4d1281..89a2156e82 100644 --- a/apps/remix-ide/src/app/tabs/hardhat-provider.tsx +++ b/apps/remix-ide/src/app/tabs/hardhat-provider.tsx @@ -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, -} - -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 (
Note: To run Hardhat network node on your system, go to hardhat project folder and run command:
npx hardhat node
@@ -57,74 +29,4 @@ export class HardhatProvider extends Plugin {
) } - - sendAsync (data: JsonDataRequest): Promise { - 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 => { - 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 { - 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 }) - } - } } \ 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 313161b121..4868ff2ade 100644 --- a/apps/remix-ide/src/app/udapp/run-tab.js +++ b/apps/remix-ide/src/app/udapp/run-tab.js @@ -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,9 +92,12 @@ export class RunTab extends ViewPlugin { return
} - onReady (api) { - this.REACT_API = api + this.REACT_API = api + } + + onInitDone () { + this.call('manager', 'activatePlugin', 'hardhat-provider') } writeFile (fileName, content) { diff --git a/apps/remix-ide/src/remixAppManager.js b/apps/remix-ide/src/remixAppManager.js index e14e98d66f..53de9df3cc 100644 --- a/apps/remix-ide/src/remixAppManager.js +++ b/apps/remix-ide/src/remixAppManager.js @@ -8,7 +8,7 @@ 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) diff --git a/libs/remix-ui/run-tab/src/lib/actions/index.ts b/libs/remix-ui/run-tab/src/lib/actions/index.ts index 6e0ebeae9c..329bb5ad2b 100644 --- a/libs/remix-ui/run-tab/src/lib/actions/index.ts +++ b/libs/remix-ui/run-tab/src/lib/actions/index.ts @@ -188,6 +188,7 @@ export const setGasFee = (value: number) => { const addPluginProvider = (profile) => { if (profile.kind === 'provider') { + console.log(profile); ((profile, app) => { const web3Provider = { async sendAsync (payload, callback) { @@ -232,8 +233,8 @@ export const setNetworkNameFromProvider = (networkName: string) => { } const addExternalProvider = (network) => { + console.log('adding ext provider', network) dispatch(addProvider(network)) - dispatch(displayPopUp(`${network.name} provider added`)) } const removeExternalProvider = (name) => { diff --git a/libs/remix-ui/run-tab/src/lib/run-tab.tsx b/libs/remix-ui/run-tab/src/lib/run-tab.tsx index 2b94114799..c8c74966c8 100644 --- a/libs/remix-ui/run-tab/src/lib/run-tab.tsx +++ b/libs/remix-ui/run-tab/src/lib/run-tab.tsx @@ -61,6 +61,7 @@ export function RunTabUI (props: RunTabProps) { useEffect(() => { initRunTab(plugin)(dispatch) + plugin.onInitDone() }, [plugin]) useEffect(() => { diff --git a/libs/remix-ui/run-tab/src/lib/types/run-tab.d.ts b/libs/remix-ui/run-tab/src/lib/types/run-tab.d.ts index 37783c2119..cecdbe121d 100644 --- a/libs/remix-ui/run-tab/src/lib/types/run-tab.d.ts +++ b/libs/remix-ui/run-tab/src/lib/types/run-tab.d.ts @@ -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";