diff --git a/packages/providers/src.ts/base-provider.ts b/packages/providers/src.ts/base-provider.ts index 8463343eb..45453ece4 100644 --- a/packages/providers/src.ts/base-provider.ts +++ b/packages/providers/src.ts/base-provider.ts @@ -175,6 +175,7 @@ let nextPollId = 1; export class BaseProvider extends Provider { + _networkPromise: Promise; _network: Network; _events: Array; @@ -215,7 +216,6 @@ export class BaseProvider extends Provider { * MUST set this. Standard named networks have a known chainId. * */ - ready: Promise; constructor(network: Networkish | Promise) { logger.checkNew(new.target, Provider); @@ -225,19 +225,15 @@ export class BaseProvider extends Provider { this.formatter = new.target.getFormatter(); if (network instanceof Promise) { - defineReadOnly(this, "ready", network.then((network) => { - defineReadOnly(this, "_network", network); - return network; - })); + this._networkPromise = network; // Squash any "unhandled promise" errors; that do not need to be handled - this.ready.catch((error) => { }); + network.catch((error) => { }); } else { const knownNetwork = getStatic<(network: Networkish) => Network>(new.target, "getNetwork")(network); if (knownNetwork) { defineReadOnly(this, "_network", knownNetwork); - defineReadOnly(this, "ready", Promise.resolve(this._network)); } else { logger.throwArgumentError("invalid network", "network", network); @@ -258,6 +254,42 @@ export class BaseProvider extends Provider { this._fastQueryDate = 0; } + async _ready(): Promise { + if (this._network == null) { + let network: Network = null; + if (this._networkPromise) { + try { + network = await this._networkPromise; + } catch (error) { } + } + + // Try the Provider's network detection (this MUST throw if it cannot) + if (network == null) { + network = await this.detectNetwork(); + } + + // This should never happen; every Provider sub-class should have + // suggested a network by here (or thrown). + if (!network) { + logger.throwError("no network detected", Logger.errors.UNKNOWN_ERROR, { }); + } + + defineReadOnly(this, "_network", network); + } + + return this._network; + } + + get ready(): Promise { + return this._ready(); + } + + async detectNetwork(): Promise { + return logger.throwError("provider does not support network detection", Logger.errors.UNSUPPORTED_OPERATION, { + operation: "provider.detectNetwork" + }); + } + static getFormatter(): Formatter { if (defaultFormatter == null) { defaultFormatter = new Formatter(); diff --git a/packages/providers/src.ts/etherscan-provider.ts b/packages/providers/src.ts/etherscan-provider.ts index a83b49dd9..fdd6e56ba 100644 --- a/packages/providers/src.ts/etherscan-provider.ts +++ b/packages/providers/src.ts/etherscan-provider.ts @@ -2,7 +2,7 @@ import { BlockTag, TransactionRequest, TransactionResponse } from "@ethersproject/abstract-provider"; import { hexlify, hexValue } from "@ethersproject/bytes"; -import { Networkish } from "@ethersproject/networks"; +import { Network, Networkish } from "@ethersproject/networks"; import { deepCopy, defineReadOnly } from "@ethersproject/properties"; import { fetchJson } from "@ethersproject/web"; @@ -109,6 +109,9 @@ export class EtherscanProvider extends BaseProvider{ defineReadOnly(this, "apiKey", apiKey || defaultApiKey); } + async detectNetwork(): Promise { + return this.network; + } async perform(method: string, params: any): Promise { let url = this.baseUrl; diff --git a/packages/providers/src.ts/fallback-provider.ts b/packages/providers/src.ts/fallback-provider.ts index e36e2d79f..b61f3c177 100644 --- a/packages/providers/src.ts/fallback-provider.ts +++ b/packages/providers/src.ts/fallback-provider.ts @@ -379,14 +379,8 @@ export class FallbackProvider extends BaseProvider { const network = checkNetworks(providerConfigs.map((c) => ((c.provider)).network)); if (network) { super(network); - } else { - // The network won't be known until all child providers know - const ready = Promise.all(providerConfigs.map((c) => c.provider.getNetwork())).then((networks) => { - return checkNetworks(networks); - }); - - super(ready); + super(this.detectNetwork()); } // Preserve a copy, so we do not get mutated @@ -396,6 +390,11 @@ export class FallbackProvider extends BaseProvider { this._highestBlockNumber = -1; } + async detectNetwork(): Promise { + const networks = await Promise.all(this.providerConfigs.map((c) => c.provider.getNetwork())); + return checkNetworks(networks); + } + async perform(method: string, params: { [name: string]: any }): Promise { // Sending transactions is special; always broadcast it to all backends diff --git a/packages/providers/src.ts/json-rpc-provider.ts b/packages/providers/src.ts/json-rpc-provider.ts index 4c31c2015..ed4bb3397 100644 --- a/packages/providers/src.ts/json-rpc-provider.ts +++ b/packages/providers/src.ts/json-rpc-provider.ts @@ -20,9 +20,7 @@ import { BaseProvider, Event } from "./base-provider"; function timer(timeout: number): Promise { return new Promise(function(resolve) { - setTimeout(function() { - resolve(); - }, timeout); + setTimeout(resolve, timeout); }); } @@ -235,31 +233,9 @@ export class JsonRpcProvider extends BaseProvider { if (network) { // The network has been specified explicitly, we can use it super(network); - } else { - // The network is unknown, query the JSON-RPC for it - const ready: Promise = new Promise((resolve, reject) => { - setTimeout(async () => { - let chainId = null; - try { - chainId = await this.send("eth_chainId", [ ]); - } catch (error) { - try { - chainId = await this.send("net_version", [ ]); - } catch (error) { } - } - - if (chainId != null) { - try { - return resolve(getNetwork(BigNumber.from(chainId).toNumber())); - } catch (error) { } - } - - reject(logger.makeError("could not detect network", Logger.errors.NETWORK_ERROR)); - }, 0); - }); - super(ready); + super(this.detectNetwork()); } // Default URL @@ -280,6 +256,33 @@ export class JsonRpcProvider extends BaseProvider { return "http:/\/localhost:8545"; } + async detectNetwork(): Promise { + await timer(0); + + let chainId = null; + try { + chainId = await this.send("eth_chainId", [ ]); + } catch (error) { + try { + chainId = await this.send("net_version", [ ]); + } catch (error) { } + } + + if (chainId != null) { + const getNetwork = getStatic<(network: Networkish) => Network>(this.constructor, "getNetwork"); + try { + return getNetwork(BigNumber.from(chainId).toNumber()); + } catch (error) { + return logger.throwError("could not detect network", Logger.errors.NETWORK_ERROR, { + chainId: chainId, + serverError: error + }); + } + } + + return logger.throwError("could not detect network", Logger.errors.NETWORK_ERROR); + } + getSigner(addressOrIndex?: string | number): JsonRpcSigner { return new JsonRpcSigner(_constructorGuard, this, addressOrIndex); } diff --git a/packages/providers/src.ts/url-json-rpc-provider.ts b/packages/providers/src.ts/url-json-rpc-provider.ts index b57259d1c..2eea5fc77 100644 --- a/packages/providers/src.ts/url-json-rpc-provider.ts +++ b/packages/providers/src.ts/url-json-rpc-provider.ts @@ -36,6 +36,10 @@ export abstract class UrlJsonRpcProvider extends JsonRpcProvider { } } + async detectNetwork(): Promise { + return this.network; + } + _startPending(): void { logger.warn("WARNING: API provider does not support pending filters"); }