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

@ -190,7 +190,7 @@ export type DebugEventJsonRpcApiProvider = {
*/
export type JsonRpcApiProviderOptions = {
polling?: boolean;
staticNetwork?: null | Network;
staticNetwork?: null | boolean | Network;
batchStallTime?: number;
batchMaxSize?: number;
batchMaxCount?: number;
@ -463,6 +463,7 @@ export abstract class JsonRpcApiProvider extends AbstractProvider {
};
#network: null | Network;
#pendingDetectNetwork: null | Promise<Network>;
#scheduleDrain(): void {
if (this.#drainTimer) { return; }
@ -554,6 +555,7 @@ export abstract class JsonRpcApiProvider extends AbstractProvider {
this.#drainTimer = null;
this.#network = null;
this.#pendingDetectNetwork = null;
{
let resolve: null | ((value: void) => void) = null;
@ -563,9 +565,15 @@ export abstract class JsonRpcApiProvider extends AbstractProvider {
this.#notReady = { promise, resolve };
}
// Make sure any static network is compatbile with the provided netwrok
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),
"staticNetwork MUST match network object", "options", options);
this.#network = staticNetwork;
@ -641,36 +649,56 @@ export abstract class JsonRpcApiProvider extends AbstractProvider {
*/
async _detectNetwork(): Promise<Network> {
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 (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
this.#pendingDetectNetwork = (async () => {
const payload: JsonRpcPayload = {
id: this.#nextId++, method: "eth_chainId", params: [ ], jsonrpc: "2.0"
};
const payload: JsonRpcPayload = {
id: this.#nextId++, method: "eth_chainId", params: [ ], jsonrpc: "2.0"
};
this.emit("debug", { action: "sendRpcPayload", payload });
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;
try {
result = (await this._send(payload))[0];
} catch (error) {
this.emit("debug", { action: "receiveRpcError", error });
throw error;
}
this.emit("debug", { action: "receiveRpcResult", result });
this.emit("debug", { action: "receiveRpcResult", result });
if ("result" in result) {
return Network.from(getBigInt(result.result));
}
if ("result" in result) {
return Network.from(getBigInt(result.result));
}
throw this.getRpcError(payload, 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 { 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";
@ -194,8 +196,23 @@ export class SocketProvider extends JsonRpcApiProvider {
*
* If unspecified, the network will be discovered.
*/
constructor(network?: Networkish) {
super(network, { batchMaxCount: 1 });
constructor(network?: Networkish, _options?: JsonRpcApiProviderOptions) {
// 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.#subs = 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 type { JsonRpcApiProviderOptions} from "./provider-jsonrpc.js";
import type { Networkish } from "./network.js";
/**
@ -45,8 +46,8 @@ export class WebSocketProvider extends SocketProvider {
return this.#websocket;
}
constructor(url: string | WebSocketLike | WebSocketCreator, network?: Networkish) {
super(network);
constructor(url: string | WebSocketLike | WebSocketCreator, network?: Networkish, options?: JsonRpcApiProviderOptions) {
super(network, options);
if (typeof(url) === "string") {
this.#connect = () => { return new _WebSocket(url); };
this.#websocket = this.#connect();