{"version":3,"file":"rpc-service.cjs","sourceRoot":"","sources":["../../src/rpc-service/rpc-service.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n  CreateServicePolicyOptions,\n  ServicePolicy,\n} from '@metamask/controller-utils';\nimport {\n  BrokenCircuitError,\n  HttpError,\n  createServicePolicy,\n  handleWhen,\n} from '@metamask/controller-utils';\nimport { JsonRpcError, rpcErrors } from '@metamask/rpc-errors';\nimport { Duration, getErrorMessage, hasProperty } from '@metamask/utils';\nimport type {\n  Json,\n  JsonRpcParams,\n  JsonRpcRequest,\n  JsonRpcResponse,\n} from '@metamask/utils';\nimport { CircuitState } from 'cockatiel';\nimport deepmerge from 'deepmerge';\nimport type { Logger } from 'loglevel';\n\nimport { projectLogger, createModuleLogger } from '../logger';\nimport type {\n  CockatielEventToEventListenerWithData,\n  ExcludeCockatielEventData,\n  ExtendCockatielEventData,\n  ExtractCockatielEventData,\n  FetchOptions,\n} from './shared';\n\n/**\n * Options for the RpcService constructor.\n */\nexport type RpcServiceOptions = {\n  /**\n   * A function that can be used to convert a binary string into a\n   * base64-encoded ASCII string. Used to encode authorization credentials.\n   */\n  btoa: typeof btoa;\n  /**\n   * The URL of the RPC endpoint to hit.\n   */\n  endpointUrl: URL | string;\n  /**\n   * A function that can be used to make an HTTP request. If your JavaScript\n   * environment supports `fetch` natively, you'll probably want to pass that;\n   * otherwise you can pass an equivalent (such as `fetch` via `node-fetch`).\n   */\n  fetch: typeof fetch;\n  /**\n   * A common set of options that will be used to make every request. Can be\n   * overridden on the request level (e.g. to add headers).\n   */\n  fetchOptions?: FetchOptions;\n  /**\n   * A `loglevel` logger.\n   */\n  logger?: Pick<Logger, 'warn'>;\n  /**\n   * Options to pass to `createServicePolicy`. Note that `retryFilterPolicy` is\n   * not accepted, as it is overwritten. See {@link createServicePolicy}.\n   */\n  policyOptions?: Omit<CreateServicePolicyOptions, 'retryFilterPolicy'>;\n  /**\n   * A function that checks if the user is currently offline. If it returns true,\n   * connection errors will not be retried, preventing degraded and break\n   * callbacks from being triggered.\n   */\n  isOffline: () => boolean;\n};\n\nconst log = createModuleLogger(projectLogger, 'RpcService');\n\n/**\n * The maximum number of times that a failing service should be re-run before\n * giving up.\n */\nexport const DEFAULT_MAX_RETRIES = 4;\n\n/**\n * The maximum number of times that the service is allowed to fail before\n * pausing further retries. This is set to a value such that if given a\n * service that continually fails, the policy needs to be executed 3 times\n * before further retries are paused.\n */\nexport const DEFAULT_MAX_CONSECUTIVE_FAILURES = (1 + DEFAULT_MAX_RETRIES) * 3;\n\n/**\n * The list of error messages that represent a failure to connect to the network.\n *\n * This list was derived from Sindre Sorhus's `is-network-error` package:\n * <https://github.com/sindresorhus/is-network-error/blob/7bbfa8be9482ce1427a21fbff60e3ee1650dd091/index.js>\n */\nexport const CONNECTION_ERRORS = [\n  // Chrome\n  {\n    constructorName: 'TypeError',\n    pattern: /network error/u,\n  },\n  // Chrome\n  {\n    constructorName: 'TypeError',\n    pattern: /Failed to fetch/u,\n  },\n  // Firefox\n  {\n    constructorName: 'TypeError',\n    pattern: /NetworkError when attempting to fetch resource\\./u,\n  },\n  // Safari 16\n  {\n    constructorName: 'TypeError',\n    pattern: /The Internet connection appears to be offline\\./u,\n  },\n  // Safari 17+\n  {\n    constructorName: 'TypeError',\n    pattern: /Load failed/u,\n  },\n  // `cross-fetch`\n  {\n    constructorName: 'TypeError',\n    pattern: /Network request failed/u,\n  },\n  // `node-fetch`\n  {\n    constructorName: 'FetchError',\n    pattern: /request to (.+) failed/u,\n  },\n  // Undici (Node.js)\n  {\n    constructorName: 'TypeError',\n    pattern: /fetch failed/u,\n  },\n  // Undici (Node.js)\n  {\n    constructorName: 'TypeError',\n    pattern: /terminated/u,\n  },\n];\n\n/**\n * Custom JSON-RPC error codes for specific cases.\n *\n * These should be moved to `@metamask/rpc-errors` eventually.\n */\nexport const CUSTOM_RPC_ERRORS = {\n  unauthorized: -32006,\n  httpClientError: -32080,\n} as const;\n\n/**\n * Determines whether the given error represents a failure to reach the network\n * after request parameters have been validated.\n *\n * This is somewhat difficult to verify because JavaScript engines (and in\n * some cases libraries) produce slightly different error messages for this\n * particular scenario, and we need to account for this.\n *\n * @param error - The error.\n * @returns True if the error indicates that the network cannot be connected to,\n * and false otherwise.\n */\nexport function isConnectionError(error: unknown): boolean {\n  if (!(typeof error === 'object' && error !== null && 'message' in error)) {\n    return false;\n  }\n\n  const { message } = error;\n\n  return (\n    typeof message === 'string' &&\n    !isNockError(message) &&\n    CONNECTION_ERRORS.some(({ constructorName, pattern }) => {\n      return (\n        error.constructor.name === constructorName && pattern.test(message)\n      );\n    })\n  );\n}\n\n/**\n * Determines whether the given error message refers to a Nock error.\n *\n * It's important that if we failed to mock a request in a test, the resulting\n * error does not cause the request to be retried so that we can see it right\n * away.\n *\n * @param message - The error message to test.\n * @returns True if the message indicates a missing Nock mock, false otherwise.\n */\nfunction isNockError(message: string): boolean {\n  return message.includes('Nock:');\n}\n\n/**\n * Determine whether the given error message indicates a failure to parse JSON.\n *\n * This is different in tests vs. implementation code because it may manifest as\n * a FetchError or a SyntaxError.\n *\n * @param error - The error object to test.\n * @returns True if the error indicates a JSON parse error, false otherwise.\n */\nexport function isJsonParseError(error: unknown): boolean {\n  return (\n    error instanceof SyntaxError ||\n    /invalid json/iu.test(getErrorMessage(error))\n  );\n}\n\n/**\n * Determines whether the given error represents a HTTP server error\n * (502, 503, or 504) that should be retried.\n *\n * @param error - The error object to test.\n * @returns True if the error has an httpStatus of 502, 503, or 504.\n */\nexport function isHttpServerError(error: Error): boolean {\n  return (\n    'httpStatus' in error &&\n    (error.httpStatus === 502 ||\n      error.httpStatus === 503 ||\n      error.httpStatus === 504)\n  );\n}\n\n/**\n * Determines whether the given error has a `code` property of `ETIMEDOUT`.\n *\n * @param error - The error object to test.\n * @returns True if the error code is `ETIMEDOUT`.\n */\nexport function isTimeoutError(error: Error): boolean {\n  return hasProperty(error, 'code') && error.code === 'ETIMEDOUT';\n}\n\n/**\n * Determines whether the given error has a `code` property of `ECONNRESET`.\n *\n * @param error - The error object to test.\n * @returns True if the error code is `ECONNRESET`.\n */\nexport function isConnectionResetError(error: Error): boolean {\n  return hasProperty(error, 'code') && error.code === 'ECONNRESET';\n}\n\n/**\n * Guarantees a URL, even given a string. This is useful for checking components\n * of that URL.\n *\n * @param endpointUrlOrUrlString - Either a URL object or a string that\n * represents the URL of an endpoint.\n * @returns A URL object.\n */\nfunction getNormalizedEndpointUrl(endpointUrlOrUrlString: URL | string): URL {\n  return endpointUrlOrUrlString instanceof URL\n    ? endpointUrlOrUrlString\n    : new URL(endpointUrlOrUrlString);\n}\n\n/**\n * Strips username and password from a URL.\n *\n * @param url - The URL to strip credentials from.\n * @returns A new URL object with credentials removed.\n */\nfunction stripCredentialsFromUrl(url: URL): URL {\n  const strippedUrl = new URL(url.toString());\n  strippedUrl.username = '';\n  strippedUrl.password = '';\n  return strippedUrl;\n}\n\n/**\n * This class is responsible for making a request to an endpoint that implements\n * the JSON-RPC protocol. It is designed to gracefully handle network and server\n * failures, retrying requests using exponential backoff. It also offers a hook\n * which can used to respond to slow requests.\n */\nexport class RpcService {\n  /**\n   * The URL of the RPC endpoint.\n   */\n  readonly endpointUrl: URL;\n\n  /**\n   * The last error that the retry policy captured (or `undefined` if the last\n   * execution of the service was successful).\n   */\n  lastError: Error | undefined;\n\n  /**\n   * The RPC method name of the current request being processed. This is passed\n   * to `onDegraded` event listeners.\n   *\n   * Initialised to `''` so the type is `string` throughout the event chain.\n   * The empty string is unreachable in practice because the method name is\n   * guaranteed to be set after the current request is completed but before\n   * any `onDegraded` callbacks are called.\n   */\n  #currentRpcMethodName = '';\n\n  /**\n   * The trace ID from the `X-Trace-Id` response header of the most recent\n   * request. Passed to `onDegraded` event listeners for debugging.\n   *\n   * `undefined` when no response has been received yet or when the response\n   * did not include the header.\n   */\n  #currentTraceId: string | undefined;\n\n  /**\n   * The function used to make an HTTP request.\n   */\n  readonly #fetch: typeof fetch;\n\n  /**\n   * A common set of options that the request options will extend.\n   */\n  readonly #fetchOptions: FetchOptions;\n\n  /**\n   * A `loglevel` logger.\n   */\n  readonly #logger: RpcServiceOptions['logger'];\n\n  /**\n   * The policy that wraps the request.\n   */\n  readonly #policy: ServicePolicy;\n\n  /**\n   * Constructs a new RpcService object.\n   *\n   * @param options - The options. See {@link RpcServiceOptions}.\n   */\n  constructor(options: RpcServiceOptions) {\n    const {\n      btoa: givenBtoa,\n      endpointUrl,\n      fetch: givenFetch,\n      logger,\n      fetchOptions = {},\n      policyOptions = {},\n      isOffline,\n    } = options;\n\n    this.#fetch = givenFetch;\n    const normalizedUrl = getNormalizedEndpointUrl(endpointUrl);\n    this.#fetchOptions = this.#getDefaultFetchOptions(\n      normalizedUrl,\n      fetchOptions,\n      givenBtoa,\n    );\n    this.endpointUrl = stripCredentialsFromUrl(normalizedUrl);\n    this.#logger = logger;\n\n    this.#policy = createServicePolicy({\n      maxRetries: DEFAULT_MAX_RETRIES,\n      maxConsecutiveFailures: DEFAULT_MAX_CONSECUTIVE_FAILURES,\n      ...policyOptions,\n      retryFilterPolicy: handleWhen((error) => {\n        // If user is offline, don't retry any errors\n        // This prevents degraded/break callbacks from being triggered\n        if (isOffline()) {\n          return false;\n        }\n\n        return (\n          // Ignore errors where the request failed to establish\n          isConnectionError(error) ||\n          // Ignore server sent HTML error pages or truncated JSON responses\n          isJsonParseError(error) ||\n          // Ignore server overload errors\n          isHttpServerError(error) ||\n          // Ignore timeout errors\n          isTimeoutError(error) ||\n          // Ignore connection reset errors\n          isConnectionResetError(error)\n        );\n      }),\n    });\n  }\n\n  /**\n   * Resets the underlying composite Cockatiel policy.\n   *\n   * This is useful in a collection of RpcServices where some act as failovers\n   * for others where you effectively want to invalidate the failovers when the\n   * primary recovers.\n   */\n  resetPolicy(): void {\n    this.#policy.reset();\n  }\n\n  /**\n   * @returns The state of the underlying circuit.\n   */\n  getCircuitState(): CircuitState {\n    return this.#policy.getCircuitState();\n  }\n\n  /**\n   * Listens for when the RPC service retries the request.\n   *\n   * @param listener - The callback to be called when the retry occurs.\n   * @returns What {@link ServicePolicy.onRetry} returns.\n   * @see {@link createServicePolicy}\n   */\n  onRetry(\n    listener: CockatielEventToEventListenerWithData<\n      ServicePolicy['onRetry'],\n      { endpointUrl: string }\n    >,\n  ): ReturnType<ServicePolicy['onRetry']> {\n    return this.#policy.onRetry((data) => {\n      listener({ ...data, endpointUrl: this.endpointUrl.toString() });\n    });\n  }\n\n  /**\n   * Listens for when the RPC service retries the request too many times in a\n   * row, causing the underlying circuit to break.\n   *\n   * @param listener - The callback to be called when the circuit is broken.\n   * @returns What {@link ServicePolicy.onBreak} returns.\n   * @see {@link createServicePolicy}\n   */\n  onBreak(\n    listener: (\n      data: ExcludeCockatielEventData<\n        ExtendCockatielEventData<\n          ExtractCockatielEventData<ServicePolicy['onBreak']>,\n          { endpointUrl: string }\n        >,\n        'isolated'\n      >,\n    ) => void,\n  ): ReturnType<ServicePolicy['onBreak']> {\n    return this.#policy.onBreak((data) => {\n      // `{ isolated: true }` is a special object that shows up when `isolate`\n      // is called on the circuit breaker. Usually `isolate` is used to hold the\n      // circuit open, but we (ab)use this method in `createServicePolicy` to\n      // reset the circuit breaker policy. When we do this, we don't want to\n      // call `onBreak` handlers, because then it causes\n      // `NetworkController:rpcEndpointUnavailable` and\n      // `NetworkController:rpcEndpointChainUnavailable` to be published. So we\n      // have to ignore that object here. The consequence is that `isolate`\n      // doesn't function the way it is intended, at least in the context of an\n      // RpcService. However, we are making a bet that we won't need to use it\n      // other than how we are already using it.\n      if (!('isolated' in data)) {\n        listener({\n          ...data,\n          endpointUrl: this.endpointUrl.toString(),\n        });\n      }\n    });\n  }\n\n  /**\n   * Listens for when the policy underlying this RPC service detects a slow\n   * request.\n   *\n   * @param listener - The callback to be called when the request is slow.\n   * @returns What {@link ServicePolicy.onDegraded} returns.\n   * @see {@link createServicePolicy}\n   */\n  onDegraded(\n    listener: CockatielEventToEventListenerWithData<\n      ServicePolicy['onDegraded'],\n      {\n        duration?: number;\n        endpointUrl: string;\n        rpcMethodName: string;\n        traceId?: string;\n      }\n    >,\n  ): ReturnType<ServicePolicy['onDegraded']> {\n    return this.#policy.onDegraded((data) => {\n      listener({\n        ...data,\n        endpointUrl: this.endpointUrl.toString(),\n        rpcMethodName: this.#currentRpcMethodName,\n        traceId: this.#currentTraceId,\n      });\n    });\n  }\n\n  /**\n   * Listens for when the policy underlying this RPC service is available.\n   *\n   * @param listener - The callback to be called when the request is available.\n   * @returns What {@link ServicePolicy.onAvailable} returns.\n   * @see {@link createServicePolicy}\n   */\n  onAvailable(\n    listener: CockatielEventToEventListenerWithData<\n      ServicePolicy['onAvailable'],\n      { endpointUrl: string }\n    >,\n  ): ReturnType<ServicePolicy['onAvailable']> {\n    return this.#policy.onAvailable(() => {\n      listener({ endpointUrl: this.endpointUrl.toString() });\n    });\n  }\n\n  /**\n   * Makes a request to the RPC endpoint.\n   *\n   * This overload is specifically designed for `eth_getBlockByNumber`, which\n   * can return a `result` of `null` despite an expected `Result` being\n   * provided.\n   *\n   * @param jsonRpcRequest - The JSON-RPC request to send to the endpoint.\n   * @param fetchOptions - An options bag for {@link fetch} which further\n   * specifies the request.\n   * @returns The decoded JSON-RPC response from the endpoint.\n   * @throws An \"authorized\" JSON-RPC error (code -32006) if the response HTTP status is 401.\n   * @throws A \"rate limiting\" JSON-RPC error (code -32005) if the response HTTP status is 429.\n   * @throws A \"resource unavailable\" JSON-RPC error (code -32002) if the response HTTP status is 402, 404, or any 5xx.\n   * @throws A generic HTTP client JSON-RPC error (code -32050) for any other 4xx HTTP status codes.\n   * @throws A \"parse\" JSON-RPC error (code -32700) if the response is not valid JSON.\n   */\n  async request<Params extends JsonRpcParams, Result extends Json>(\n    jsonRpcRequest: JsonRpcRequest<Params> & { method: 'eth_getBlockByNumber' },\n    fetchOptions?: FetchOptions,\n  ): Promise<JsonRpcResponse<Result> | JsonRpcResponse<null>>;\n\n  /**\n   * Makes a request to the RPC endpoint.\n   *\n   * This overload is designed for all RPC methods except for\n   * `eth_getBlockByNumber`, which are expected to return a `result` of the\n   * expected `Result`.\n   *\n   * @param jsonRpcRequest - The JSON-RPC request to send to the endpoint.\n   * @param fetchOptions - An options bag for {@link fetch} which further\n   * specifies the request.\n   * @returns The decoded JSON-RPC response from the endpoint.\n   * @throws An \"authorized\" JSON-RPC error (code -32006) if the response HTTP status is 401.\n   * @throws A \"rate limiting\" JSON-RPC error (code -32005) if the response HTTP status is 429.\n   * @throws A \"resource unavailable\" JSON-RPC error (code -32002) if the response HTTP status is 402, 404, or any 5xx.\n   * @throws A generic HTTP client JSON-RPC error (code -32050) for any other 4xx HTTP status codes.\n   * @throws A \"parse\" JSON-RPC error (code -32700) if the response is not valid JSON.\n   */\n  async request<Params extends JsonRpcParams, Result extends Json>(\n    jsonRpcRequest: JsonRpcRequest<Params>,\n    fetchOptions?: FetchOptions,\n  ): Promise<JsonRpcResponse<Result>>;\n\n  async request<Params extends JsonRpcParams, Result extends Json>(\n    // The request object may be frozen and must not be mutated.\n    jsonRpcRequest: Readonly<JsonRpcRequest<Params>>,\n    fetchOptions: FetchOptions = {},\n  ): Promise<JsonRpcResponse<Result | null>> {\n    const completeFetchOptions = this.#getCompleteFetchOptions(\n      jsonRpcRequest,\n      fetchOptions,\n    );\n    return await this.#executeAndProcessRequest<Result>(\n      completeFetchOptions,\n      jsonRpcRequest.method,\n    );\n  }\n\n  /**\n   * Constructs a default set of options to `fetch`.\n   *\n   * If a username and password are present in the URL, they are extracted to an\n   * Authorization header.\n   *\n   * @param endpointUrl - The endpoint URL.\n   * @param fetchOptions - The options to `fetch`.\n   * @param givenBtoa - An implementation of `btoa`.\n   * @returns The default fetch options.\n   */\n  #getDefaultFetchOptions(\n    endpointUrl: URL,\n    fetchOptions: FetchOptions,\n    givenBtoa: (stringToEncode: string) => string,\n  ): FetchOptions {\n    if (endpointUrl.username && endpointUrl.password) {\n      const authString = `${endpointUrl.username}:${endpointUrl.password}`;\n      const encodedCredentials = givenBtoa(authString);\n      return deepmerge(fetchOptions, {\n        headers: { Authorization: `Basic ${encodedCredentials}` },\n      });\n    }\n\n    return fetchOptions;\n  }\n\n  /**\n   * Constructs a final set of options to pass to `fetch`. Note that the method\n   * defaults to `post`, and the JSON-RPC request is automatically JSON-encoded.\n   *\n   * @param jsonRpcRequest - The JSON-RPC request.\n   * @param fetchOptions - Custom `fetch` options.\n   * @returns The complete set of `fetch` options.\n   */\n  #getCompleteFetchOptions<Params extends JsonRpcParams>(\n    jsonRpcRequest: Readonly<JsonRpcRequest<Params>>,\n    fetchOptions: FetchOptions,\n  ): FetchOptions {\n    const defaultOptions = {\n      method: 'POST',\n      headers: {\n        Accept: 'application/json',\n        'Content-Type': 'application/json',\n      },\n    };\n    const mergedOptions = deepmerge(\n      defaultOptions,\n      deepmerge(this.#fetchOptions, fetchOptions),\n    );\n\n    const { id, jsonrpc, method, params } = jsonRpcRequest;\n    const body = JSON.stringify({\n      id,\n      jsonrpc,\n      method,\n      params,\n    });\n\n    return { ...mergedOptions, body };\n  }\n\n  /**\n   * Makes the request using the Cockatiel policy that this service creates.\n   *\n   * @param fetchOptions - The options for `fetch`; will be combined with the\n   * fetch options passed to the constructor\n   * @param rpcMethodName - The JSON-RPC method name of the current request.\n   * @returns The decoded JSON-RPC response from the endpoint.\n   * @throws An \"authorized\" JSON-RPC error (code -32006) if the response HTTP status is 401.\n   * @throws A \"rate limiting\" JSON-RPC error (code -32005) if the response HTTP status is 429.\n   * @throws A \"resource unavailable\" JSON-RPC error (code -32002) if the response HTTP status is 402, 404, or any 5xx.\n   * @throws A generic HTTP client JSON-RPC error (code -32050) for any other 4xx HTTP status codes.\n   * @throws A \"parse\" JSON-RPC error (code -32700) if the response is not valid JSON.\n   */\n  async #executeAndProcessRequest<Result extends Json>(\n    fetchOptions: FetchOptions,\n    rpcMethodName: string,\n  ): Promise<JsonRpcResponse<Result> | JsonRpcResponse<null>> {\n    let response: Response | undefined;\n    try {\n      log(\n        `[${this.endpointUrl}] Circuit state`,\n        this.#policy.getCircuitState(),\n      );\n      const jsonDecodedResponse = await this.#policy.execute(\n        async (context) => {\n          // Reset response so that if this attempt throws before\n          // assigning a new response, the finally block does not read\n          // a stale response from a previous attempt.\n          response = undefined;\n          try {\n            log(\n              'REQUEST INITIATED:',\n              this.endpointUrl.toString(),\n              '::',\n              fetchOptions,\n              // @ts-expect-error This property _is_ here, the type of\n              // ServicePolicy is just wrong.\n              `(attempt ${context.attempt + 1})`,\n            );\n            response = await this.#fetch(this.endpointUrl, fetchOptions);\n            if (!response.ok) {\n              throw new HttpError(response.status);\n            }\n            log(\n              'REQUEST SUCCESSFUL:',\n              this.endpointUrl.toString(),\n              response.status,\n            );\n            return await response.json();\n          } finally {\n            // Track the RPC method and trace ID for the request that has just\n            // taken place. We pass these properties to `onDegraded` event\n            // listeners.\n            //\n            // We set these properties after the request completes and not\n            // before the request starts to account for race conditions. That\n            // is, if there are two requests that are being performed\n            // concurrently, and the second request fails fast but the first\n            // request succeeds slowly, when `onDegraded` is called we want it\n            // to include the first request as the RPC method, not the second.\n            //\n            // Also, we set these properties within a `finally` block inside of\n            // the function passed to `policy.execute` to ensure that they are\n            // set before `onDegraded` gets called, no matter the outcome of\n            // the request.\n            this.#currentRpcMethodName = rpcMethodName;\n            this.#currentTraceId =\n              response?.headers.get('X-Trace-Id') ?? undefined;\n          }\n        },\n      );\n      this.lastError = undefined;\n      return jsonDecodedResponse;\n    } catch (error) {\n      log('REQUEST ERROR:', this.endpointUrl.toString(), error);\n\n      this.lastError =\n        error instanceof Error ? error : new Error(getErrorMessage(error));\n\n      if (error instanceof HttpError) {\n        const status = error.httpStatus;\n        if (status === 401) {\n          throw new JsonRpcError(\n            CUSTOM_RPC_ERRORS.unauthorized,\n            'Unauthorized.',\n            {\n              httpStatus: status,\n            },\n          );\n        }\n        if (status === 429) {\n          throw rpcErrors.limitExceeded({\n            message: 'Request is being rate limited.',\n            data: {\n              httpStatus: status,\n            },\n          });\n        }\n        if (status >= 500 || status === 402 || status === 404) {\n          throw rpcErrors.resourceUnavailable({\n            message: 'RPC endpoint not found or unavailable.',\n            data: {\n              httpStatus: status,\n            },\n          });\n        }\n\n        // Handle all other 4xx errors as generic HTTP client errors\n        throw new JsonRpcError(\n          CUSTOM_RPC_ERRORS.httpClientError,\n          'RPC endpoint returned HTTP client error.',\n          {\n            httpStatus: status,\n          },\n        );\n      } else if (isJsonParseError(error)) {\n        throw rpcErrors.parse({\n          message: 'RPC endpoint did not return JSON.',\n        });\n      } else if (error instanceof BrokenCircuitError) {\n        this.#logger?.warn(error);\n        const remainingCircuitOpenDuration =\n          this.#policy.getRemainingCircuitOpenDuration();\n        const formattedRemainingCircuitOpenDuration = Intl.NumberFormat(\n          undefined,\n          { maximumFractionDigits: 2 },\n        ).format(\n          (remainingCircuitOpenDuration ?? this.#policy.circuitBreakDuration) /\n            Duration.Minute,\n        );\n        throw rpcErrors.resourceUnavailable({\n          message: `RPC endpoint returned too many errors, retrying in ${formattedRemainingCircuitOpenDuration} minutes. Consider using a different RPC endpoint.`,\n        });\n      }\n      throw error;\n    }\n  }\n}\n"]}