Add auto-detected static network support to providers and allow customizing socket provider options (#4199, #4418, #4441).

This commit is contained in:
Richard Moore 2023-11-27 05:23:10 -05:00
parent 9e74d14432
commit 4681b83d51
4 changed files with 76 additions and 28 deletions

@ -3,6 +3,8 @@ import { connect } from "net";
import { SocketProvider } from "./provider-socket.js"; import { SocketProvider } from "./provider-socket.js";
import type { Socket } from "net"; import type { Socket } from "net";
import type { JsonRpcApiProviderOptions } from "./provider-jsonrpc.js";
import type { Networkish } from "./network.js"; import type { Networkish } from "./network.js";
@ -35,8 +37,8 @@ export class IpcSocketProvider extends SocketProvider {
*/ */
get socket(): Socket { return this.#socket; } get socket(): Socket { return this.#socket; }
constructor(path: string, network?: Networkish) { constructor(path: string, network?: Networkish, options?: JsonRpcApiProviderOptions) {
super(network); super(network, options);
this.#socket = connect(path); this.#socket = connect(path);
this.socket.on("ready", async () => { this.socket.on("ready", async () => {

@ -190,7 +190,7 @@ export type DebugEventJsonRpcApiProvider = {
*/ */
export type JsonRpcApiProviderOptions = { export type JsonRpcApiProviderOptions = {
polling?: boolean; polling?: boolean;
staticNetwork?: null | Network; staticNetwork?: null | boolean | Network;
batchStallTime?: number; batchStallTime?: number;
batchMaxSize?: number; batchMaxSize?: number;
batchMaxCount?: number; batchMaxCount?: number;
@ -463,6 +463,7 @@ export abstract class JsonRpcApiProvider extends AbstractProvider {
}; };
#network: null | Network; #network: null | Network;
#pendingDetectNetwork: null | Promise<Network>;
#scheduleDrain(): void { #scheduleDrain(): void {
if (this.#drainTimer) { return; } if (this.#drainTimer) { return; }
@ -554,6 +555,7 @@ export abstract class JsonRpcApiProvider extends AbstractProvider {
this.#drainTimer = null; this.#drainTimer = null;
this.#network = null; this.#network = null;
this.#pendingDetectNetwork = null;
{ {
let resolve: null | ((value: void) => void) = null; let resolve: null | ((value: void) => void) = null;
@ -563,9 +565,15 @@ export abstract class JsonRpcApiProvider extends AbstractProvider {
this.#notReady = { promise, resolve }; this.#notReady = { promise, resolve };
} }
// Make sure any static network is compatbile with the provided netwrok
const staticNetwork = this._getOption("staticNetwork"); const staticNetwork = this._getOption("staticNetwork");
if (staticNetwork) { if (typeof(staticNetwork) === "boolean") {
assertArgument(!staticNetwork || network !== "any", "staticNetwork cannot be used on special network 'any'", "options", options);
if (staticNetwork && network != null) {
this.#network = Network.from(network);
}
} else if (staticNetwork) {
// Make sure any static network is compatbile with the provided netwrok
assertArgument(network == null || staticNetwork.matches(network), assertArgument(network == null || staticNetwork.matches(network),
"staticNetwork MUST match network object", "options", options); "staticNetwork MUST match network object", "options", options);
this.#network = staticNetwork; this.#network = staticNetwork;
@ -641,36 +649,56 @@ export abstract class JsonRpcApiProvider extends AbstractProvider {
*/ */
async _detectNetwork(): Promise<Network> { async _detectNetwork(): Promise<Network> {
const network = this._getOption("staticNetwork"); const network = this._getOption("staticNetwork");
if (network) { return network; } if (network) {
if (network === true) {
if (this.#network) { return this.#network; }
} else {
return network;
}
}
if (this.#pendingDetectNetwork) {
return await this.#pendingDetectNetwork;
}
// If we are ready, use ``send``, which enabled requests to be batched // If we are ready, use ``send``, which enabled requests to be batched
if (this.ready) { if (this.ready) {
return Network.from(getBigInt(await this.send("eth_chainId", [ ]))); this.#pendingDetectNetwork = (async () => {
const result = Network.from(getBigInt(await this.send("eth_chainId", [ ])));
this.#pendingDetectNetwork = null;
return result;
})();
return await this.#pendingDetectNetwork;
} }
// We are not ready yet; use the primitive _send // We are not ready yet; use the primitive _send
this.#pendingDetectNetwork = (async () => {
const payload: JsonRpcPayload = {
id: this.#nextId++, method: "eth_chainId", params: [ ], jsonrpc: "2.0"
};
const payload: JsonRpcPayload = { this.emit("debug", { action: "sendRpcPayload", payload });
id: this.#nextId++, method: "eth_chainId", params: [ ], jsonrpc: "2.0"
};
this.emit("debug", { action: "sendRpcPayload", payload }); let result: JsonRpcResult | JsonRpcError;
try {
result = (await this._send(payload))[0];
this.#pendingDetectNetwork = null;
} catch (error) {
this.#pendingDetectNetwork = null;
this.emit("debug", { action: "receiveRpcError", error });
throw error;
}
let result: JsonRpcResult | JsonRpcError; this.emit("debug", { action: "receiveRpcResult", result });
try {
result = (await this._send(payload))[0];
} catch (error) {
this.emit("debug", { action: "receiveRpcError", error });
throw error;
}
this.emit("debug", { action: "receiveRpcResult", result }); if ("result" in result) {
return Network.from(getBigInt(result.result));
}
if ("result" in result) { throw this.getRpcError(payload, result);
return Network.from(getBigInt(result.result)); })();
}
throw this.getRpcError(payload, result); return await this.#pendingDetectNetwork;
} }
/** /**

@ -15,7 +15,9 @@ import { JsonRpcApiProvider } from "./provider-jsonrpc.js";
import type { Subscriber, Subscription } from "./abstract-provider.js"; import type { Subscriber, Subscription } from "./abstract-provider.js";
import type { EventFilter } from "./provider.js"; import type { EventFilter } from "./provider.js";
import type { JsonRpcError, JsonRpcPayload, JsonRpcResult } from "./provider-jsonrpc.js"; import type {
JsonRpcApiProviderOptions, JsonRpcError, JsonRpcPayload, JsonRpcResult
} from "./provider-jsonrpc.js";
import type { Networkish } from "./network.js"; import type { Networkish } from "./network.js";
@ -194,8 +196,23 @@ export class SocketProvider extends JsonRpcApiProvider {
* *
* If unspecified, the network will be discovered. * If unspecified, the network will be discovered.
*/ */
constructor(network?: Networkish) { constructor(network?: Networkish, _options?: JsonRpcApiProviderOptions) {
super(network, { batchMaxCount: 1 }); // Copy the options
const options = Object.assign({ }, (_options != null) ? _options: { });
// Support for batches is generally not supported for
// connection-base providers; if this changes in the future
// the _send should be updated to reflect this
assertArgument(options.batchMaxCount == null || options.batchMaxCount === 1,
"sockets-based providers do not support batches", "options.batchMaxCount", _options);
options.batchMaxCount = 1;
// Socket-based Providers (generally) cannot change their network,
// since they have a long-lived connection; but let people override
// this if they have just cause.
if (options.staticNetwork == null) { options.staticNetwork = true; }
super(network, options);
this.#callbacks = new Map(); this.#callbacks = new Map();
this.#subs = new Map(); this.#subs = new Map();
this.#pending = new Map(); this.#pending = new Map();

@ -4,6 +4,7 @@ import { WebSocket as _WebSocket } from "./ws.js"; /*-browser*/
import { SocketProvider } from "./provider-socket.js"; import { SocketProvider } from "./provider-socket.js";
import type { JsonRpcApiProviderOptions} from "./provider-jsonrpc.js";
import type { Networkish } from "./network.js"; import type { Networkish } from "./network.js";
/** /**
@ -45,8 +46,8 @@ export class WebSocketProvider extends SocketProvider {
return this.#websocket; return this.#websocket;
} }
constructor(url: string | WebSocketLike | WebSocketCreator, network?: Networkish) { constructor(url: string | WebSocketLike | WebSocketCreator, network?: Networkish, options?: JsonRpcApiProviderOptions) {
super(network); super(network, options);
if (typeof(url) === "string") { if (typeof(url) === "string") {
this.#connect = () => { return new _WebSocket(url); }; this.#connect = () => { return new _WebSocket(url); };
this.#websocket = this.#connect(); this.#websocket = this.#connect();