Skip to content

Commit

Permalink
Allow providers to detect their network after instantiation (#814).
Browse files Browse the repository at this point in the history
  • Loading branch information
ricmoo committed May 3, 2020
1 parent 0e3a66c commit 99ae946
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 41 deletions.
46 changes: 39 additions & 7 deletions packages/providers/src.ts/base-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ let nextPollId = 1;


export class BaseProvider extends Provider {
_networkPromise: Promise<Network>;
_network: Network;

_events: Array<Event>;
Expand Down Expand Up @@ -215,7 +216,6 @@ export class BaseProvider extends Provider {
* MUST set this. Standard named networks have a known chainId.
*
*/
ready: Promise<Network>;

constructor(network: Networkish | Promise<Network>) {
logger.checkNew(new.target, Provider);
Expand All @@ -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);
Expand All @@ -258,6 +254,42 @@ export class BaseProvider extends Provider {
this._fastQueryDate = 0;
}

async _ready(): Promise<Network> {
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<Network> {
return this._ready();
}

async detectNetwork(): Promise<Network> {
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();
Expand Down
5 changes: 4 additions & 1 deletion packages/providers/src.ts/etherscan-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -109,6 +109,9 @@ export class EtherscanProvider extends BaseProvider{
defineReadOnly(this, "apiKey", apiKey || defaultApiKey);
}

async detectNetwork(): Promise<Network> {
return this.network;
}

async perform(method: string, params: any): Promise<any> {
let url = this.baseUrl;
Expand Down
13 changes: 6 additions & 7 deletions packages/providers/src.ts/fallback-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -379,14 +379,8 @@ export class FallbackProvider extends BaseProvider {
const network = checkNetworks(providerConfigs.map((c) => (<any>(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());

This comment has been minimized.

Copy link
@danielattilasimon

danielattilasimon May 8, 2020

This is throwing me ReferenceError: must call super constructor before using 'this' in derived class constructor.

This comment has been minimized.

Copy link
@wighawag

wighawag May 8, 2020

for me too : #822

This comment has been minimized.

Copy link
@jarindr

jarindr May 8, 2020

how are you guys initializing the rpc instance ?

This comment has been minimized.

Copy link
@wighawag

wighawag May 8, 2020

new ethers.providers.JsonRpcProvider(url)

This comment has been minimized.

Copy link
@ricmoo

ricmoo May 8, 2020

Author Member

This is a compile-time TypeScript error? Or runtime? Can you remove your node_modules, package-lock.json and other lock files (yarn, etc) and npm install again? If that helps, I’ll run the lock-versions script today to prevent this from happening again.

This comment has been minimized.

Copy link
@danielattilasimon

danielattilasimon May 9, 2020

It's a runtime error for me. I am using Ethers in a create-react-app–based dapp. It's picking up the ESM build (lib.esm). The "normal" build (lib) probably wouldn't have exhibited the issue as it looks like it's transpiled to ES5, so no class/extends/super stuff (?)

This comment has been minimized.

Copy link
@ricmoo

ricmoo May 9, 2020

Author Member

Did 5.0.0-beta.186 fix this for you?

This comment has been minimized.

Copy link
@danielattilasimon

danielattilasimon May 9, 2020

I'm checking it out as we speak. Be right back :)

This comment has been minimized.

Copy link
@danielattilasimon

danielattilasimon May 9, 2020

Yep, it's fixed for me.
Awesome, thanks so much!

This comment has been minimized.

Copy link
@ricmoo

ricmoo May 9, 2020

Author Member

W00t! Thanks. :)

}

// Preserve a copy, so we do not get mutated
Expand All @@ -396,6 +390,11 @@ export class FallbackProvider extends BaseProvider {
this._highestBlockNumber = -1;
}

async detectNetwork(): Promise<Network> {
const networks = await Promise.all(this.providerConfigs.map((c) => c.provider.getNetwork()));
return checkNetworks(networks);
}

async perform(method: string, params: { [name: string]: any }): Promise<any> {

// Sending transactions is special; always broadcast it to all backends
Expand Down
55 changes: 29 additions & 26 deletions packages/providers/src.ts/json-rpc-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@ import { BaseProvider, Event } from "./base-provider";

function timer(timeout: number): Promise<any> {
return new Promise(function(resolve) {
setTimeout(function() {
resolve();
}, timeout);
setTimeout(resolve, timeout);
});
}

Expand Down Expand Up @@ -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<Network> = 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
Expand All @@ -280,6 +256,33 @@ export class JsonRpcProvider extends BaseProvider {
return "http:/\/localhost:8545";
}

async detectNetwork(): Promise<Network> {
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);
}
Expand Down
4 changes: 4 additions & 0 deletions packages/providers/src.ts/url-json-rpc-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ export abstract class UrlJsonRpcProvider extends JsonRpcProvider {
}
}

async detectNetwork(): Promise<Network> {
return this.network;
}

_startPending(): void {
logger.warn("WARNING: API provider does not support pending filters");
}
Expand Down

0 comments on commit 99ae946

Please sign in to comment.