Allow providers to detect their network after instantiation (#814).
This commit is contained in:
parent
0e3a66c829
commit
99ae946476
@ -175,6 +175,7 @@ let nextPollId = 1;
|
|||||||
|
|
||||||
|
|
||||||
export class BaseProvider extends Provider {
|
export class BaseProvider extends Provider {
|
||||||
|
_networkPromise: Promise<Network>;
|
||||||
_network: Network;
|
_network: Network;
|
||||||
|
|
||||||
_events: Array<Event>;
|
_events: Array<Event>;
|
||||||
@ -215,7 +216,6 @@ export class BaseProvider extends Provider {
|
|||||||
* MUST set this. Standard named networks have a known chainId.
|
* MUST set this. Standard named networks have a known chainId.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
ready: Promise<Network>;
|
|
||||||
|
|
||||||
constructor(network: Networkish | Promise<Network>) {
|
constructor(network: Networkish | Promise<Network>) {
|
||||||
logger.checkNew(new.target, Provider);
|
logger.checkNew(new.target, Provider);
|
||||||
@ -225,19 +225,15 @@ export class BaseProvider extends Provider {
|
|||||||
this.formatter = new.target.getFormatter();
|
this.formatter = new.target.getFormatter();
|
||||||
|
|
||||||
if (network instanceof Promise) {
|
if (network instanceof Promise) {
|
||||||
defineReadOnly(this, "ready", network.then((network) => {
|
this._networkPromise = network;
|
||||||
defineReadOnly(this, "_network", network);
|
|
||||||
return network;
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Squash any "unhandled promise" errors; that do not need to be handled
|
// Squash any "unhandled promise" errors; that do not need to be handled
|
||||||
this.ready.catch((error) => { });
|
network.catch((error) => { });
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
const knownNetwork = getStatic<(network: Networkish) => Network>(new.target, "getNetwork")(network);
|
const knownNetwork = getStatic<(network: Networkish) => Network>(new.target, "getNetwork")(network);
|
||||||
if (knownNetwork) {
|
if (knownNetwork) {
|
||||||
defineReadOnly(this, "_network", knownNetwork);
|
defineReadOnly(this, "_network", knownNetwork);
|
||||||
defineReadOnly(this, "ready", Promise.resolve(this._network));
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
logger.throwArgumentError("invalid network", "network", network);
|
logger.throwArgumentError("invalid network", "network", network);
|
||||||
@ -258,6 +254,42 @@ export class BaseProvider extends Provider {
|
|||||||
this._fastQueryDate = 0;
|
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 {
|
static getFormatter(): Formatter {
|
||||||
if (defaultFormatter == null) {
|
if (defaultFormatter == null) {
|
||||||
defaultFormatter = new Formatter();
|
defaultFormatter = new Formatter();
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { BlockTag, TransactionRequest, TransactionResponse } from "@ethersproject/abstract-provider";
|
import { BlockTag, TransactionRequest, TransactionResponse } from "@ethersproject/abstract-provider";
|
||||||
import { hexlify, hexValue } from "@ethersproject/bytes";
|
import { hexlify, hexValue } from "@ethersproject/bytes";
|
||||||
import { Networkish } from "@ethersproject/networks";
|
import { Network, Networkish } from "@ethersproject/networks";
|
||||||
import { deepCopy, defineReadOnly } from "@ethersproject/properties";
|
import { deepCopy, defineReadOnly } from "@ethersproject/properties";
|
||||||
import { fetchJson } from "@ethersproject/web";
|
import { fetchJson } from "@ethersproject/web";
|
||||||
|
|
||||||
@ -109,6 +109,9 @@ export class EtherscanProvider extends BaseProvider{
|
|||||||
defineReadOnly(this, "apiKey", apiKey || defaultApiKey);
|
defineReadOnly(this, "apiKey", apiKey || defaultApiKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async detectNetwork(): Promise<Network> {
|
||||||
|
return this.network;
|
||||||
|
}
|
||||||
|
|
||||||
async perform(method: string, params: any): Promise<any> {
|
async perform(method: string, params: any): Promise<any> {
|
||||||
let url = this.baseUrl;
|
let url = this.baseUrl;
|
||||||
|
@ -379,14 +379,8 @@ export class FallbackProvider extends BaseProvider {
|
|||||||
const network = checkNetworks(providerConfigs.map((c) => (<any>(c.provider)).network));
|
const network = checkNetworks(providerConfigs.map((c) => (<any>(c.provider)).network));
|
||||||
if (network) {
|
if (network) {
|
||||||
super(network);
|
super(network);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// The network won't be known until all child providers know
|
super(this.detectNetwork());
|
||||||
const ready = Promise.all(providerConfigs.map((c) => c.provider.getNetwork())).then((networks) => {
|
|
||||||
return checkNetworks(networks);
|
|
||||||
});
|
|
||||||
|
|
||||||
super(ready);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Preserve a copy, so we do not get mutated
|
// Preserve a copy, so we do not get mutated
|
||||||
@ -396,6 +390,11 @@ export class FallbackProvider extends BaseProvider {
|
|||||||
this._highestBlockNumber = -1;
|
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> {
|
async perform(method: string, params: { [name: string]: any }): Promise<any> {
|
||||||
|
|
||||||
// Sending transactions is special; always broadcast it to all backends
|
// Sending transactions is special; always broadcast it to all backends
|
||||||
|
@ -20,9 +20,7 @@ import { BaseProvider, Event } from "./base-provider";
|
|||||||
|
|
||||||
function timer(timeout: number): Promise<any> {
|
function timer(timeout: number): Promise<any> {
|
||||||
return new Promise(function(resolve) {
|
return new Promise(function(resolve) {
|
||||||
setTimeout(function() {
|
setTimeout(resolve, timeout);
|
||||||
resolve();
|
|
||||||
}, timeout);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,31 +233,9 @@ export class JsonRpcProvider extends BaseProvider {
|
|||||||
if (network) {
|
if (network) {
|
||||||
// The network has been specified explicitly, we can use it
|
// The network has been specified explicitly, we can use it
|
||||||
super(network);
|
super(network);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// The network is unknown, query the JSON-RPC for it
|
// The network is unknown, query the JSON-RPC for it
|
||||||
const ready: Promise<Network> = new Promise((resolve, reject) => {
|
super(this.detectNetwork());
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default URL
|
// Default URL
|
||||||
@ -280,6 +256,33 @@ export class JsonRpcProvider extends BaseProvider {
|
|||||||
return "http:/\/localhost:8545";
|
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 {
|
getSigner(addressOrIndex?: string | number): JsonRpcSigner {
|
||||||
return new JsonRpcSigner(_constructorGuard, this, addressOrIndex);
|
return new JsonRpcSigner(_constructorGuard, this, addressOrIndex);
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,10 @@ export abstract class UrlJsonRpcProvider extends JsonRpcProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async detectNetwork(): Promise<Network> {
|
||||||
|
return this.network;
|
||||||
|
}
|
||||||
|
|
||||||
_startPending(): void {
|
_startPending(): void {
|
||||||
logger.warn("WARNING: API provider does not support pending filters");
|
logger.warn("WARNING: API provider does not support pending filters");
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user