diff --git a/src.ts/providers/abstract-provider.ts b/src.ts/providers/abstract-provider.ts index 63e07ae6d8..65f8cb18b7 100644 --- a/src.ts/providers/abstract-provider.ts +++ b/src.ts/providers/abstract-provider.ts @@ -398,6 +398,24 @@ type _PerformAccountRequest = { method: "getStorage", position: bigint } +/** + * Options for configuring some internal aspects of an [[AbstractProvider]]. + * + * **``cacheTimeout``** - how long to cache a low-level ``_perform`` + * for, based on input parameters. This reduces the number of calls + * to getChainId and getBlockNumber, but may break test chains which + * can perform operations (internally) synchronously. Use ``-1`` to + * disable, ``0`` will only buffer within the same event loop and + * any other value is in ms. (default: ``250``) + */ +export type AbstractProviderOptions = { + cacheTimeout?: number; +}; + +const defaultOptions = { + cacheTimeout: 250 +}; + type CcipArgs = { sender: string; urls: Array; @@ -436,12 +454,15 @@ export class AbstractProvider implements Provider { #disableCcipRead: boolean; + #options: Required; + /** * Create a new **AbstractProvider** connected to %%network%%, or * use the various network detection capabilities to discover the * [[Network]] if necessary. */ - constructor(_network?: "any" | Networkish) { + constructor(_network?: "any" | Networkish, options?: AbstractProviderOptions) { + this.#options = Object.assign({ }, defaultOptions, options || { }); if (_network === "any") { this.#anyNetwork = true; @@ -512,19 +533,25 @@ export class AbstractProvider implements Provider { // Shares multiple identical requests made during the same 250ms async #perform(req: PerformActionRequest): Promise { + const timeout = this.#options.cacheTimeout; + + // Caching disabled + if (timeout < 0) { return await this._perform(req); } + // Create a tag const tag = getTag(req.method, req); let perform = this.#performCache.get(tag); if (!perform) { perform = this._perform(req); + this.#performCache.set(tag, perform); setTimeout(() => { if (this.#performCache.get(tag) === perform) { this.#performCache.delete(tag); } - }, 250); + }, timeout); } return await perform; diff --git a/src.ts/providers/provider-jsonrpc.ts b/src.ts/providers/provider-jsonrpc.ts index f8e885f7c6..021910cf13 100644 --- a/src.ts/providers/provider-jsonrpc.ts +++ b/src.ts/providers/provider-jsonrpc.ts @@ -34,7 +34,7 @@ import { PollingEventSubscriber } from "./subscriber-polling.js"; import type { TypedDataDomain, TypedDataField } from "../hash/index.js"; import type { TransactionLike } from "../transaction/index.js"; -import type { PerformActionRequest, Subscriber, Subscription } from "./abstract-provider.js"; +import type { AbstractProviderOptions, PerformActionRequest, Subscriber, Subscription } from "./abstract-provider.js"; import type { Networkish } from "./network.js"; import type { Provider, TransactionRequest, TransactionResponse } from "./provider.js"; import type { Signer } from "./signer.js"; @@ -185,6 +185,8 @@ export type DebugEventJsonRpcApiProvider = { * * **``batchMaxCount``** - maximum number of requests to allow in a batch. * If ``batchMaxCount = 1``, then batching is disabled. (default: ``100``) + * + * **``cacheTimeout``** - passed as [[AbstractProviderOptions]]. */ export type JsonRpcApiProviderOptions = { polling?: boolean; @@ -192,6 +194,8 @@ export type JsonRpcApiProviderOptions = { batchStallTime?: number; batchMaxSize?: number; batchMaxCount?: number; + + cacheTimeout?: number; }; const defaultOptions = { @@ -200,7 +204,9 @@ const defaultOptions = { batchStallTime: 10, // 10ms batchMaxSize: (1 << 20), // 1Mb - batchMaxCount: 100 // 100 requests + batchMaxCount: 100, // 100 requests + + cacheTimeout: 250 } /** @@ -537,7 +543,11 @@ export abstract class JsonRpcApiProvider extends AbstractProvider { } constructor(network?: Networkish, options?: JsonRpcApiProviderOptions) { - super(network); + const superOptions: AbstractProviderOptions = { }; + if (options && options.cacheTimeout != null) { + superOptions.cacheTimeout = options.cacheTimeout; + } + super(network, superOptions); this.#nextId = 1; this.#options = Object.assign({ }, defaultOptions, options || { });