fix bug in injected and external provider

pull/3356/head
yann300 2 years ago
parent 3744c7dc00
commit f3bb8ea519
  1. 122
      apps/remix-ide/src/app/providers/abstract-provider.tsx
  2. 4
      apps/remix-ide/src/app/providers/injected-provider.tsx

@ -21,105 +21,78 @@ 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
blocked: boolean
blockchain: Blockchain blockchain: Blockchain
defaultUrl: string defaultUrl: string
connected: boolean connected: boolean
nodeUrl: string
constructor (profile, blockchain, defaultUrl) { constructor (profile, blockchain, defaultUrl) {
super(profile) super(profile)
this.defaultUrl = defaultUrl this.defaultUrl = defaultUrl
this.provider = null this.provider = null
this.blocked = false // used to block any call when trying to recover after a failed connection.
this.connected = false this.connected = false
this.blockchain = blockchain this.blockchain = blockchain
this.nodeUrl = 'http://localhost:8545'
} }
abstract body(): JSX.Element abstract body(): JSX.Element
onDeactivation () { onDeactivation () {
this.provider = null this.provider = null
this.blocked = false
} }
sendAsync (data: JsonDataRequest): Promise<any> { async init () {
// eslint-disable-next-line no-async-promise-executor this.nodeUrl = await ((): Promise<string> => {
return new Promise(async (resolve, reject) => { return new Promise((resolve, reject) => {
if (this.blocked) return reject(new Error('provider unable to connect')) const modalContent: AppModal = {
// If provider is not set, allow to open modal only when provider is trying to connect id: this.profile.name,
if (!this.provider) { title: this.profile.displayName,
let value: string message: this.body(),
try { modalType: ModalTypes.prompt,
value = await ((): Promise<string> => { okLabel: 'OK',
return new Promise((resolve, reject) => { cancelLabel: 'Cancel',
const modalContent: AppModal = { validationFn: (value) => {
id: this.profile.name, if (!value) return { valid: false, message: "value is empty" }
title: this.profile.displayName, if (value.startsWith('https://') || value.startsWith('http://')) {
message: this.body(), return {
modalType: ModalTypes.prompt, valid: true,
okLabel: 'OK', message: ''
cancelLabel: 'Cancel', }
validationFn: (value) => { } else {
if (!value) return { valid: false, message: "value is empty" } return {
if (value.startsWith('https://') || value.startsWith('http://')) { valid: false,
return { message: 'the provided value should contain the protocol ( e.g starts with http:// or https:// )'
valid: true,
message: ''
}
} else {
return {
valid: false,
message: 'the provided value should contain the protocol ( e.g starts with http:// or https:// )'
}
}
},
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 })
this.switchAway(false)
return
}
this.provider = new ethers.providers.JsonRpcProvider(value)
try {
setTimeout(() => {
if (!this.connected) {
this.switchAway(true)
reject('Unable to connect')
} }
}, 2000) },
await this.provider.detectNetwork() // this throws if the network cannot be detected okFn: (value: string) => {
this.connected = true setTimeout(() => resolve(value), 0)
} catch (e) { },
this.switchAway(true) cancelFn: () => {
reject('Unable to connect') setTimeout(() => reject(new Error('Canceled')), 0)
return },
hideFn: () => {
setTimeout(() => reject(new Error('Hide')), 0)
},
defaultValue: this.defaultUrl
} }
this.sendAsyncInternal(data, resolve, reject) this.call('notification', 'modal', modalContent)
} else { })
this.sendAsyncInternal(data, resolve, reject) })()
} this.provider = new ethers.providers.JsonRpcProvider(this.nodeUrl)
}
sendAsync (data: JsonDataRequest): Promise<any> {
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve, reject) => {
if (!this.provider) return reject(new Error('provider node set'))
this.sendAsyncInternal(data, resolve, reject)
}) })
} }
private async switchAway (showError) { private async switchAway (showError) {
if (!this.provider) return if (!this.provider) return
this.provider = null this.provider = null
this.blocked = true
this.connected = false this.connected = false
if (showError) { if (showError) {
const modalContent: AlertModal = { const modalContent: AlertModal = {
@ -130,16 +103,11 @@ export abstract class AbstractProvider extends Plugin {
this.call('notification', 'alert', modalContent) this.call('notification', 'alert', modalContent)
} }
await this.call('udapp', 'setEnvironmentMode', { context: 'vm-london'}) await this.call('udapp', 'setEnvironmentMode', { context: 'vm-london'})
setTimeout(_ => { this.blocked = false }, 1000) // we wait 1 second for letting remix to switch to vm
return return
} }
private async sendAsyncInternal (data: JsonDataRequest, resolve: SuccessRequest, reject: RejectRequest): Promise<void> { private async sendAsyncInternal (data: JsonDataRequest, resolve: SuccessRequest, reject: RejectRequest): Promise<void> {
if (this.provider) { 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 { try {
const result = await this.provider.send(data.method, data.params) const result = await this.provider.send(data.method, data.params)
resolve({ jsonrpc: '2.0', result, id: data.id }) resolve({ jsonrpc: '2.0', result, id: data.id })

@ -16,8 +16,8 @@ export class InjectedProvider extends Plugin {
} }
askPermission (throwIfNoInjectedProvider) { askPermission (throwIfNoInjectedProvider) {
if (typeof (window as any).ethereum !== "undefined" && typeof (window as any).request === "function") { if ((typeof (window as any).ethereum) !== "undefined" && (typeof (window as any).ethereum.request) === "function") {
(window as any).request({ method: "eth_requestAccounts" }) (window as any).ethereum.request({ method: "eth_requestAccounts" })
} else if (throwIfNoInjectedProvider) { } else if (throwIfNoInjectedProvider) {
throw new Error(noInjectedProviderMsg) throw new Error(noInjectedProviderMsg)
} }

Loading…
Cancel
Save