{"version":3,"file":"rpc-service.mjs","sourceRoot":"","sources":["../../src/rpc-service/rpc-service.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n  CreateServicePolicyOptions,\n  ServicePolicy,\n} from '@fortuna-wallet/controller-utils';\nimport {\n  CircuitState,\n  HttpError,\n  createServicePolicy,\n  handleWhen,\n} from '@fortuna-wallet/controller-utils';\nimport { JsonRpcError, rpcErrors } from '@metamask/rpc-errors';\nimport type { JsonRpcRequest } from '@metamask/utils';\nimport {\n  getErrorMessage,\n  hasProperty,\n  type Json,\n  type JsonRpcParams,\n  type JsonRpcResponse,\n} from '@metamask/utils';\nimport deepmerge from 'deepmerge';\n\nimport type { AbstractRpcService } from './abstract-rpc-service';\nimport type { AddToCockatielEventData, FetchOptions } 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   * An RPC service that represents a failover endpoint which will be invoked\n   * while the circuit for _this_ service is open.\n   */\n  failoverService?: AbstractRpcService;\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   * 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\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) {\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) {\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 */\nfunction isJsonParseError(error: unknown) {\n  return (\n    error instanceof SyntaxError ||\n    /invalid json/iu.test(getErrorMessage(error))\n  );\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 * 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 implements AbstractRpcService {\n  /**\n   * The function used to make an HTTP request.\n   */\n  readonly #fetch: typeof fetch;\n\n  /**\n   * The URL of the RPC endpoint.\n   */\n  readonly endpointUrl: URL;\n\n  /**\n   * A common set of options that the request options will extend.\n   */\n  readonly #fetchOptions: FetchOptions;\n\n  /**\n   * An RPC service that represents a failover endpoint which will be invoked\n   * while the circuit for _this_ service is open.\n   */\n  readonly #failoverService: RpcServiceOptions['failoverService'];\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      failoverService,\n      fetch: givenFetch,\n      fetchOptions = {},\n      policyOptions = {},\n    } = options;\n\n    this.#fetch = givenFetch;\n    this.endpointUrl = getNormalizedEndpointUrl(endpointUrl);\n    this.#fetchOptions = this.#getDefaultFetchOptions(\n      this.endpointUrl,\n      fetchOptions,\n      givenBtoa,\n    );\n    this.#failoverService = failoverService;\n\n    const policy = createServicePolicy({\n      maxRetries: DEFAULT_MAX_RETRIES,\n      maxConsecutiveFailures: DEFAULT_MAX_CONSECUTIVE_FAILURES,\n      ...policyOptions,\n      retryFilterPolicy: handleWhen((error) => {\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          ('httpStatus' in error &&\n            (error.httpStatus === 502 ||\n              error.httpStatus === 503 ||\n              error.httpStatus === 504)) ||\n          (hasProperty(error, 'code') &&\n            (error.code === 'ETIMEDOUT' || error.code === 'ECONNRESET'))\n        );\n      }),\n    });\n    this.#policy = policy;\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: AddToCockatielEventData<\n      Parameters<ServicePolicy['onRetry']>[0],\n      { endpointUrl: string }\n    >,\n  ) {\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.\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: AddToCockatielEventData<\n      Parameters<ServicePolicy['onBreak']>[0],\n      { endpointUrl: string; failoverEndpointUrl?: string }\n    >,\n  ) {\n    return this.#policy.onBreak((data) => {\n      listener({\n        ...data,\n        endpointUrl: this.endpointUrl.toString(),\n        failoverEndpointUrl: this.#failoverService\n          ? this.#failoverService.endpointUrl.toString()\n          : undefined,\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: AddToCockatielEventData<\n      Parameters<ServicePolicy['onDegraded']>[0],\n      { endpointUrl: string }\n    >,\n  ) {\n    return this.#policy.onDegraded(() => {\n      listener({ endpointUrl: this.endpointUrl.toString() });\n    });\n  }\n\n  /**\n   * Makes a request to the RPC endpoint. If the circuit is open because this\n   * request has failed too many times, the request is forwarded to a failover\n   * service (if provided).\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. If the circuit is open because this\n   * request has failed too many times, the request is forwarded to a failover\n   * service (if provided).\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    jsonRpcRequest: JsonRpcRequest<Params>,\n    fetchOptions: FetchOptions = {},\n  ): Promise<JsonRpcResponse<Result | null>> {\n    const completeFetchOptions = this.#getCompleteFetchOptions(\n      jsonRpcRequest,\n      fetchOptions,\n    );\n\n    try {\n      return await this.#processRequest<Result>(completeFetchOptions);\n    } catch (error) {\n      if (\n        this.#policy.circuitBreakerPolicy.state === CircuitState.Open &&\n        this.#failoverService !== undefined\n      ) {\n        return await this.#failoverService.request(\n          jsonRpcRequest,\n          completeFetchOptions,\n        );\n      }\n      throw error;\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: 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   * @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 #processRequest<Result extends Json>(\n    fetchOptions: FetchOptions,\n  ): Promise<JsonRpcResponse<Result> | JsonRpcResponse<null>> {\n    let response: Response | undefined;\n    try {\n      return await this.#policy.execute(async () => {\n        response = await this.#fetch(this.endpointUrl, fetchOptions);\n        if (!response.ok) {\n          throw new HttpError(response.status);\n        }\n        return await response.json();\n      });\n    } catch (error) {\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      }\n      throw error;\n    }\n  }\n}\n"]}