@metamask/network-controller 22.1.1 → 22.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/CHANGELOG.md +31 -1
  2. package/dist/NetworkController.cjs +61 -10
  3. package/dist/NetworkController.cjs.map +1 -1
  4. package/dist/NetworkController.d.cts +61 -5
  5. package/dist/NetworkController.d.cts.map +1 -1
  6. package/dist/NetworkController.d.mts +61 -5
  7. package/dist/NetworkController.d.mts.map +1 -1
  8. package/dist/NetworkController.mjs +61 -9
  9. package/dist/NetworkController.mjs.map +1 -1
  10. package/dist/create-network-client.cjs +1 -4
  11. package/dist/create-network-client.cjs.map +1 -1
  12. package/dist/create-network-client.d.cts.map +1 -1
  13. package/dist/create-network-client.d.mts.map +1 -1
  14. package/dist/create-network-client.mjs +1 -4
  15. package/dist/create-network-client.mjs.map +1 -1
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.cts +1 -0
  18. package/dist/index.d.cts.map +1 -1
  19. package/dist/index.d.mts +1 -0
  20. package/dist/index.d.mts.map +1 -1
  21. package/dist/index.mjs.map +1 -1
  22. package/dist/rpc-service/abstract-rpc-service.cjs +3 -0
  23. package/dist/rpc-service/abstract-rpc-service.cjs.map +1 -0
  24. package/dist/rpc-service/abstract-rpc-service.d.cts +46 -0
  25. package/dist/rpc-service/abstract-rpc-service.d.cts.map +1 -0
  26. package/dist/rpc-service/abstract-rpc-service.d.mts +46 -0
  27. package/dist/rpc-service/abstract-rpc-service.d.mts.map +1 -0
  28. package/dist/rpc-service/abstract-rpc-service.mjs +2 -0
  29. package/dist/rpc-service/abstract-rpc-service.mjs.map +1 -0
  30. package/dist/rpc-service/rpc-service-chain.cjs +109 -0
  31. package/dist/rpc-service/rpc-service-chain.cjs.map +1 -0
  32. package/dist/rpc-service/rpc-service-chain.d.cts +116 -0
  33. package/dist/rpc-service/rpc-service-chain.d.cts.map +1 -0
  34. package/dist/rpc-service/rpc-service-chain.d.mts +116 -0
  35. package/dist/rpc-service/rpc-service-chain.d.mts.map +1 -0
  36. package/dist/rpc-service/rpc-service-chain.mjs +105 -0
  37. package/dist/rpc-service/rpc-service-chain.mjs.map +1 -0
  38. package/dist/rpc-service/rpc-service.cjs +279 -0
  39. package/dist/rpc-service/rpc-service.cjs.map +1 -0
  40. package/dist/rpc-service/rpc-service.d.cts +134 -0
  41. package/dist/rpc-service/rpc-service.d.cts.map +1 -0
  42. package/dist/rpc-service/rpc-service.d.mts +134 -0
  43. package/dist/rpc-service/rpc-service.d.mts.map +1 -0
  44. package/dist/rpc-service/rpc-service.mjs +278 -0
  45. package/dist/rpc-service/rpc-service.mjs.map +1 -0
  46. package/dist/rpc-service/shared.cjs +3 -0
  47. package/dist/rpc-service/shared.cjs.map +1 -0
  48. package/dist/rpc-service/shared.d.cts +12 -0
  49. package/dist/rpc-service/shared.d.cts.map +1 -0
  50. package/dist/rpc-service/shared.d.mts +12 -0
  51. package/dist/rpc-service/shared.d.mts.map +1 -0
  52. package/dist/rpc-service/shared.mjs +2 -0
  53. package/dist/rpc-service/shared.mjs.map +1 -0
  54. package/package.json +7 -7
@@ -0,0 +1,116 @@
1
+ import type { Json, JsonRpcParams, JsonRpcRequest, JsonRpcResponse } from "@metamask/utils";
2
+ import type { AbstractRpcService } from "./abstract-rpc-service.mjs";
3
+ import { RpcService } from "./rpc-service.mjs";
4
+ import type { FetchOptions } from "./shared.mjs";
5
+ /**
6
+ * The subset of options accepted by the RpcServiceChain constructor which
7
+ * represent a single endpoint.
8
+ */
9
+ type RpcServiceConfiguration = {
10
+ /**
11
+ * The URL of the endpoint.
12
+ */
13
+ endpointUrl: URL | string;
14
+ /**
15
+ * The options to pass to `fetch` when making the request to the endpoint.
16
+ */
17
+ fetchOptions?: FetchOptions;
18
+ };
19
+ /**
20
+ * This class constructs a chain of RpcService objects which represent a
21
+ * particular network. The first object in the chain is intended to be the primary
22
+ * way of reaching the network and the remaining objects are used as failovers.
23
+ */
24
+ export declare class RpcServiceChain implements AbstractRpcService {
25
+ #private;
26
+ /**
27
+ * Constructs a new RpcServiceChain object.
28
+ *
29
+ * @param args - The arguments.
30
+ * @param args.fetch - A function that can be used to make an HTTP request.
31
+ * If your JavaScript environment supports `fetch` natively, you'll probably
32
+ * want to pass that; otherwise you can pass an equivalent (such as `fetch`
33
+ * via `node-fetch`).
34
+ * @param args.btoa - A function that can be used to convert a binary string
35
+ * into base-64. Used to encode authorization credentials.
36
+ * @param args.serviceConfigurations - The options for the RPC services that
37
+ * you want to construct. This class takes a set of configuration objects and
38
+ * not literal `RpcService`s to account for the possibility that we may want
39
+ * to send request headers to official Infura endpoints and not failovers.
40
+ */
41
+ constructor({ fetch: givenFetch, btoa: givenBtoa, serviceConfigurations, }: {
42
+ fetch: typeof fetch;
43
+ btoa: typeof btoa;
44
+ serviceConfigurations: RpcServiceConfiguration[];
45
+ });
46
+ /**
47
+ * Listens for when any of the RPC services retry a request.
48
+ *
49
+ * @param listener - The callback to be called when the retry occurs.
50
+ * @returns What {@link RpcService.onRetry} returns.
51
+ */
52
+ onRetry(listener: Parameters<RpcService['onRetry']>[0]): {
53
+ dispose(): void;
54
+ };
55
+ /**
56
+ * Listens for when any of the RPC services retry the request too many times
57
+ * in a row.
58
+ *
59
+ * @param listener - The callback to be called when the retry occurs.
60
+ * @returns What {@link RpcService.onBreak} returns.
61
+ */
62
+ onBreak(listener: Parameters<RpcService['onBreak']>[0]): {
63
+ dispose(): void;
64
+ };
65
+ /**
66
+ * Listens for when any of the RPC services send a slow request.
67
+ *
68
+ * @param listener - The callback to be called when the retry occurs.
69
+ * @returns What {@link RpcService.onRetry} returns.
70
+ */
71
+ onDegraded(listener: Parameters<RpcService['onDegraded']>[0]): {
72
+ dispose(): void;
73
+ };
74
+ /**
75
+ * Makes a request to the first RPC service in the chain. If this service is
76
+ * down, then the request is forwarded to the next service in the chain, etc.
77
+ *
78
+ * This overload is specifically designed for `eth_getBlockByNumber`, which
79
+ * can return a `result` of `null` despite an expected `Result` being
80
+ * provided.
81
+ *
82
+ * @param jsonRpcRequest - The JSON-RPC request to send to the endpoint.
83
+ * @param fetchOptions - An options bag for {@link fetch} which further
84
+ * specifies the request.
85
+ * @returns The decoded JSON-RPC response from the endpoint.
86
+ * @throws A "method not found" error if the response status is 405.
87
+ * @throws A rate limiting error if the response HTTP status is 429.
88
+ * @throws A timeout error if the response HTTP status is 503 or 504.
89
+ * @throws A generic error if the response HTTP status is not 2xx but also not
90
+ * 405, 429, 503, or 504.
91
+ */
92
+ request<Params extends JsonRpcParams, Result extends Json>(jsonRpcRequest: JsonRpcRequest<Params> & {
93
+ method: 'eth_getBlockByNumber';
94
+ }, fetchOptions?: FetchOptions): Promise<JsonRpcResponse<Result> | JsonRpcResponse<null>>;
95
+ /**
96
+ * Makes a request to the first RPC service in the chain. If this service is
97
+ * down, then the request is forwarded to the next service in the chain, etc.
98
+ *
99
+ * This overload is designed for all RPC methods except for
100
+ * `eth_getBlockByNumber`, which are expected to return a `result` of the
101
+ * expected `Result`.
102
+ *
103
+ * @param jsonRpcRequest - The JSON-RPC request to send to the endpoint.
104
+ * @param fetchOptions - An options bag for {@link fetch} which further
105
+ * specifies the request.
106
+ * @returns The decoded JSON-RPC response from the endpoint.
107
+ * @throws A "method not found" error if the response status is 405.
108
+ * @throws A rate limiting error if the response HTTP status is 429.
109
+ * @throws A timeout error if the response HTTP status is 503 or 504.
110
+ * @throws A generic error if the response HTTP status is not 2xx but also not
111
+ * 405, 429, 503, or 504.
112
+ */
113
+ request<Params extends JsonRpcParams, Result extends Json>(jsonRpcRequest: JsonRpcRequest<Params>, fetchOptions?: FetchOptions): Promise<JsonRpcResponse<Result>>;
114
+ }
115
+ export {};
116
+ //# sourceMappingURL=rpc-service-chain.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rpc-service-chain.d.mts","sourceRoot":"","sources":["../../src/rpc-service/rpc-service-chain.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,IAAI,EACJ,aAAa,EACb,cAAc,EACd,eAAe,EAChB,wBAAwB;AAEzB,OAAO,KAAK,EAAE,kBAAkB,EAAE,mCAA+B;AACjE,OAAO,EAAE,UAAU,EAAE,0BAAsB;AAC3C,OAAO,KAAK,EAAE,YAAY,EAAE,qBAAiB;AAE7C;;;GAGG;AACH,KAAK,uBAAuB,GAAG;IAC7B;;OAEG;IACH,WAAW,EAAE,GAAG,GAAG,MAAM,CAAC;IAC1B;;OAEG;IACH,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B,CAAC;AAEF;;;;GAIG;AACH,qBAAa,eAAgB,YAAW,kBAAkB;;IAGxD;;;;;;;;;;;;;;OAcG;gBACS,EACV,KAAK,EAAE,UAAU,EACjB,IAAI,EAAE,SAAS,EACf,qBAAqB,GACtB,EAAE;QACD,KAAK,EAAE,OAAO,KAAK,CAAC;QACpB,IAAI,EAAE,OAAO,IAAI,CAAC;QAClB,qBAAqB,EAAE,uBAAuB,EAAE,CAAC;KAClD;IAQD;;;;;OAKG;IACH,OAAO,CAAC,QAAQ,EAAE,UAAU,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;;;IAYtD;;;;;;OAMG;IACH,OAAO,CAAC,QAAQ,EAAE,UAAU,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;;;IAYtD;;;;;OAKG;IACH,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;;;IAY5D;;;;;;;;;;;;;;;;;OAiBG;IACG,OAAO,CAAC,MAAM,SAAS,aAAa,EAAE,MAAM,SAAS,IAAI,EAC7D,cAAc,EAAE,cAAc,CAAC,MAAM,CAAC,GAAG;QAAE,MAAM,EAAE,sBAAsB,CAAA;KAAE,EAC3E,YAAY,CAAC,EAAE,YAAY,GAC1B,OAAO,CAAC,eAAe,CAAC,MAAM,CAAC,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;IAE3D;;;;;;;;;;;;;;;;;OAiBG;IACG,OAAO,CAAC,MAAM,SAAS,aAAa,EAAE,MAAM,SAAS,IAAI,EAC7D,cAAc,EAAE,cAAc,CAAC,MAAM,CAAC,EACtC,YAAY,CAAC,EAAE,YAAY,GAC1B,OAAO,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;CA4CpC"}
@@ -0,0 +1,105 @@
1
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
2
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
3
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
4
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
5
+ };
6
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
7
+ if (kind === "m") throw new TypeError("Private method is not writable");
8
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
9
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
10
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
11
+ };
12
+ var _RpcServiceChain_instances, _RpcServiceChain_services, _RpcServiceChain_buildRpcServiceChain;
13
+ import { RpcService } from "./rpc-service.mjs";
14
+ /**
15
+ * This class constructs a chain of RpcService objects which represent a
16
+ * particular network. The first object in the chain is intended to be the primary
17
+ * way of reaching the network and the remaining objects are used as failovers.
18
+ */
19
+ export class RpcServiceChain {
20
+ /**
21
+ * Constructs a new RpcServiceChain object.
22
+ *
23
+ * @param args - The arguments.
24
+ * @param args.fetch - A function that can be used to make an HTTP request.
25
+ * If your JavaScript environment supports `fetch` natively, you'll probably
26
+ * want to pass that; otherwise you can pass an equivalent (such as `fetch`
27
+ * via `node-fetch`).
28
+ * @param args.btoa - A function that can be used to convert a binary string
29
+ * into base-64. Used to encode authorization credentials.
30
+ * @param args.serviceConfigurations - The options for the RPC services that
31
+ * you want to construct. This class takes a set of configuration objects and
32
+ * not literal `RpcService`s to account for the possibility that we may want
33
+ * to send request headers to official Infura endpoints and not failovers.
34
+ */
35
+ constructor({ fetch: givenFetch, btoa: givenBtoa, serviceConfigurations, }) {
36
+ _RpcServiceChain_instances.add(this);
37
+ _RpcServiceChain_services.set(this, void 0);
38
+ __classPrivateFieldSet(this, _RpcServiceChain_services, __classPrivateFieldGet(this, _RpcServiceChain_instances, "m", _RpcServiceChain_buildRpcServiceChain).call(this, {
39
+ serviceConfigurations,
40
+ fetch: givenFetch,
41
+ btoa: givenBtoa,
42
+ }), "f");
43
+ }
44
+ /**
45
+ * Listens for when any of the RPC services retry a request.
46
+ *
47
+ * @param listener - The callback to be called when the retry occurs.
48
+ * @returns What {@link RpcService.onRetry} returns.
49
+ */
50
+ onRetry(listener) {
51
+ const disposables = __classPrivateFieldGet(this, _RpcServiceChain_services, "f").map((service) => service.onRetry(listener));
52
+ return {
53
+ dispose() {
54
+ disposables.forEach((disposable) => disposable.dispose());
55
+ },
56
+ };
57
+ }
58
+ /**
59
+ * Listens for when any of the RPC services retry the request too many times
60
+ * in a row.
61
+ *
62
+ * @param listener - The callback to be called when the retry occurs.
63
+ * @returns What {@link RpcService.onBreak} returns.
64
+ */
65
+ onBreak(listener) {
66
+ const disposables = __classPrivateFieldGet(this, _RpcServiceChain_services, "f").map((service) => service.onBreak(listener));
67
+ return {
68
+ dispose() {
69
+ disposables.forEach((disposable) => disposable.dispose());
70
+ },
71
+ };
72
+ }
73
+ /**
74
+ * Listens for when any of the RPC services send a slow request.
75
+ *
76
+ * @param listener - The callback to be called when the retry occurs.
77
+ * @returns What {@link RpcService.onRetry} returns.
78
+ */
79
+ onDegraded(listener) {
80
+ const disposables = __classPrivateFieldGet(this, _RpcServiceChain_services, "f").map((service) => service.onDegraded(listener));
81
+ return {
82
+ dispose() {
83
+ disposables.forEach((disposable) => disposable.dispose());
84
+ },
85
+ };
86
+ }
87
+ async request(jsonRpcRequest, fetchOptions = {}) {
88
+ return __classPrivateFieldGet(this, _RpcServiceChain_services, "f")[0].request(jsonRpcRequest, fetchOptions);
89
+ }
90
+ }
91
+ _RpcServiceChain_services = new WeakMap(), _RpcServiceChain_instances = new WeakSet(), _RpcServiceChain_buildRpcServiceChain = function _RpcServiceChain_buildRpcServiceChain({ serviceConfigurations, fetch: givenFetch, btoa: givenBtoa, }) {
92
+ return [...serviceConfigurations]
93
+ .reverse()
94
+ .reduce((workingServices, serviceConfiguration, index) => {
95
+ const failoverService = index > 0 ? workingServices[0] : undefined;
96
+ const service = new RpcService({
97
+ fetch: givenFetch,
98
+ btoa: givenBtoa,
99
+ ...serviceConfiguration,
100
+ failoverService,
101
+ });
102
+ return [service, ...workingServices];
103
+ }, []);
104
+ };
105
+ //# sourceMappingURL=rpc-service-chain.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rpc-service-chain.mjs","sourceRoot":"","sources":["../../src/rpc-service/rpc-service-chain.ts"],"names":[],"mappings":";;;;;;;;;;;;AAQA,OAAO,EAAE,UAAU,EAAE,0BAAsB;AAkB3C;;;;GAIG;AACH,MAAM,OAAO,eAAe;IAG1B;;;;;;;;;;;;;;OAcG;IACH,YAAY,EACV,KAAK,EAAE,UAAU,EACjB,IAAI,EAAE,SAAS,EACf,qBAAqB,GAKtB;;QAzBQ,4CAAwB;QA0B/B,uBAAA,IAAI,6BAAa,uBAAA,IAAI,yEAAsB,MAA1B,IAAI,EAAuB;YAC1C,qBAAqB;YACrB,KAAK,EAAE,UAAU;YACjB,IAAI,EAAE,SAAS;SAChB,CAAC,MAAA,CAAC;IACL,CAAC;IAED;;;;;OAKG;IACH,OAAO,CAAC,QAA8C;QACpD,MAAM,WAAW,GAAG,uBAAA,IAAI,iCAAU,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CACjD,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAC1B,CAAC;QAEF,OAAO;YACL,OAAO;gBACL,WAAW,CAAC,OAAO,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC;YAC5D,CAAC;SACF,CAAC;IACJ,CAAC;IAED;;;;;;OAMG;IACH,OAAO,CAAC,QAA8C;QACpD,MAAM,WAAW,GAAG,uBAAA,IAAI,iCAAU,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CACjD,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAC1B,CAAC;QAEF,OAAO;YACL,OAAO;gBACL,WAAW,CAAC,OAAO,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC;YAC5D,CAAC;SACF,CAAC;IACJ,CAAC;IAED;;;;;OAKG;IACH,UAAU,CAAC,QAAiD;QAC1D,MAAM,WAAW,GAAG,uBAAA,IAAI,iCAAU,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CACjD,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,CAC7B,CAAC;QAEF,OAAO;YACL,OAAO;gBACL,WAAW,CAAC,OAAO,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC;YAC5D,CAAC;SACF,CAAC;IACJ,CAAC;IAgDD,KAAK,CAAC,OAAO,CACX,cAAsC,EACtC,eAA6B,EAAE;QAE/B,OAAO,uBAAA,IAAI,iCAAU,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,YAAY,CAAC,CAAC;IACjE,CAAC;CAqCF;8KAtBuB,EACpB,qBAAqB,EACrB,KAAK,EAAE,UAAU,EACjB,IAAI,EAAE,SAAS,GAKhB;IACC,OAAO,CAAC,GAAG,qBAAqB,CAAC;SAC9B,OAAO,EAAE;SACT,MAAM,CAAC,CAAC,eAA6B,EAAE,oBAAoB,EAAE,KAAK,EAAE,EAAE;QACrE,MAAM,eAAe,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACnE,MAAM,OAAO,GAAG,IAAI,UAAU,CAAC;YAC7B,KAAK,EAAE,UAAU;YACjB,IAAI,EAAE,SAAS;YACf,GAAG,oBAAoB;YACvB,eAAe;SAChB,CAAC,CAAC;QACH,OAAO,CAAC,OAAO,EAAE,GAAG,eAAe,CAAC,CAAC;IACvC,CAAC,EAAE,EAAE,CAAC,CAAC;AACX,CAAC","sourcesContent":["import type {\n Json,\n JsonRpcParams,\n JsonRpcRequest,\n JsonRpcResponse,\n} from '@metamask/utils';\n\nimport type { AbstractRpcService } from './abstract-rpc-service';\nimport { RpcService } from './rpc-service';\nimport type { FetchOptions } from './shared';\n\n/**\n * The subset of options accepted by the RpcServiceChain constructor which\n * represent a single endpoint.\n */\ntype RpcServiceConfiguration = {\n /**\n * The URL of the endpoint.\n */\n endpointUrl: URL | string;\n /**\n * The options to pass to `fetch` when making the request to the endpoint.\n */\n fetchOptions?: FetchOptions;\n};\n\n/**\n * This class constructs a chain of RpcService objects which represent a\n * particular network. The first object in the chain is intended to be the primary\n * way of reaching the network and the remaining objects are used as failovers.\n */\nexport class RpcServiceChain implements AbstractRpcService {\n readonly #services: RpcService[];\n\n /**\n * Constructs a new RpcServiceChain object.\n *\n * @param args - The arguments.\n * @param args.fetch - A function that can be used to make an HTTP request.\n * If your JavaScript environment supports `fetch` natively, you'll probably\n * want to pass that; otherwise you can pass an equivalent (such as `fetch`\n * via `node-fetch`).\n * @param args.btoa - A function that can be used to convert a binary string\n * into base-64. Used to encode authorization credentials.\n * @param args.serviceConfigurations - The options for the RPC services that\n * you want to construct. This class takes a set of configuration objects and\n * not literal `RpcService`s to account for the possibility that we may want\n * to send request headers to official Infura endpoints and not failovers.\n */\n constructor({\n fetch: givenFetch,\n btoa: givenBtoa,\n serviceConfigurations,\n }: {\n fetch: typeof fetch;\n btoa: typeof btoa;\n serviceConfigurations: RpcServiceConfiguration[];\n }) {\n this.#services = this.#buildRpcServiceChain({\n serviceConfigurations,\n fetch: givenFetch,\n btoa: givenBtoa,\n });\n }\n\n /**\n * Listens for when any of the RPC services retry a request.\n *\n * @param listener - The callback to be called when the retry occurs.\n * @returns What {@link RpcService.onRetry} returns.\n */\n onRetry(listener: Parameters<RpcService['onRetry']>[0]) {\n const disposables = this.#services.map((service) =>\n service.onRetry(listener),\n );\n\n return {\n dispose() {\n disposables.forEach((disposable) => disposable.dispose());\n },\n };\n }\n\n /**\n * Listens for when any of the RPC services retry the request too many times\n * in a row.\n *\n * @param listener - The callback to be called when the retry occurs.\n * @returns What {@link RpcService.onBreak} returns.\n */\n onBreak(listener: Parameters<RpcService['onBreak']>[0]) {\n const disposables = this.#services.map((service) =>\n service.onBreak(listener),\n );\n\n return {\n dispose() {\n disposables.forEach((disposable) => disposable.dispose());\n },\n };\n }\n\n /**\n * Listens for when any of the RPC services send a slow request.\n *\n * @param listener - The callback to be called when the retry occurs.\n * @returns What {@link RpcService.onRetry} returns.\n */\n onDegraded(listener: Parameters<RpcService['onDegraded']>[0]) {\n const disposables = this.#services.map((service) =>\n service.onDegraded(listener),\n );\n\n return {\n dispose() {\n disposables.forEach((disposable) => disposable.dispose());\n },\n };\n }\n\n /**\n * Makes a request to the first RPC service in the chain. If this service is\n * down, then the request is forwarded to the next service in the chain, etc.\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 A \"method not found\" error if the response status is 405.\n * @throws A rate limiting error if the response HTTP status is 429.\n * @throws A timeout error if the response HTTP status is 503 or 504.\n * @throws A generic error if the response HTTP status is not 2xx but also not\n * 405, 429, 503, or 504.\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 first RPC service in the chain. If this service is\n * down, then the request is forwarded to the next service in the chain, etc.\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 A \"method not found\" error if the response status is 405.\n * @throws A rate limiting error if the response HTTP status is 429.\n * @throws A timeout error if the response HTTP status is 503 or 504.\n * @throws A generic error if the response HTTP status is not 2xx but also not\n * 405, 429, 503, or 504.\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 return this.#services[0].request(jsonRpcRequest, fetchOptions);\n }\n\n /**\n * Constructs the chain of RPC services. The second RPC service is\n * configured as the failover for the first, the third service is\n * configured as the failover for the second, etc.\n *\n * @param args - The arguments.\n * @param args.serviceConfigurations - The options for the RPC services that\n * you want to construct.\n * @param args.fetch - A function that can be used to make an HTTP request.\n * @param args.btoa - A function that can be used to convert a binary string\n * into base-64. Used to encode authorization credentials.\n * @returns The constructed chain of RPC services.\n */\n #buildRpcServiceChain({\n serviceConfigurations,\n fetch: givenFetch,\n btoa: givenBtoa,\n }: {\n serviceConfigurations: RpcServiceConfiguration[];\n fetch: typeof fetch;\n btoa: typeof btoa;\n }): RpcService[] {\n return [...serviceConfigurations]\n .reverse()\n .reduce((workingServices: RpcService[], serviceConfiguration, index) => {\n const failoverService = index > 0 ? workingServices[0] : undefined;\n const service = new RpcService({\n fetch: givenFetch,\n btoa: givenBtoa,\n ...serviceConfiguration,\n failoverService,\n });\n return [service, ...workingServices];\n }, []);\n }\n}\n"]}
@@ -0,0 +1,279 @@
1
+ "use strict";
2
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
3
+ if (kind === "m") throw new TypeError("Private method is not writable");
4
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
5
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
6
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
7
+ };
8
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
10
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
11
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
+ };
13
+ var __importDefault = (this && this.__importDefault) || function (mod) {
14
+ return (mod && mod.__esModule) ? mod : { "default": mod };
15
+ };
16
+ var _RpcService_instances, _RpcService_fetch, _RpcService_endpointUrl, _RpcService_fetchOptions, _RpcService_failoverService, _RpcService_policy, _RpcService_getDefaultFetchOptions, _RpcService_getCompleteFetchOptions, _RpcService_executePolicy;
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.RpcService = exports.NETWORK_UNREACHABLE_ERRORS = void 0;
19
+ const controller_utils_1 = require("@metamask/controller-utils");
20
+ const rpc_errors_1 = require("@metamask/rpc-errors");
21
+ const utils_1 = require("@metamask/utils");
22
+ const deepmerge_1 = __importDefault(require("deepmerge"));
23
+ /**
24
+ * The list of error messages that represent a failure to reach the network.
25
+ *
26
+ * This list was derived from Sindre Sorhus's `is-network-error` package:
27
+ * <https://github.com/sindresorhus/is-network-error/blob/7bbfa8be9482ce1427a21fbff60e3ee1650dd091/index.js>
28
+ */
29
+ exports.NETWORK_UNREACHABLE_ERRORS = new Set([
30
+ 'network error',
31
+ 'Failed to fetch',
32
+ 'NetworkError when attempting to fetch resource.',
33
+ 'The Internet connection appears to be offline.',
34
+ 'Load failed',
35
+ 'Network request failed',
36
+ 'fetch failed',
37
+ 'terminated', // Undici (Node.js)
38
+ ]);
39
+ /**
40
+ * Determines whether the given error represents a failure to reach the network
41
+ * after request parameters have been validated.
42
+ *
43
+ * This is somewhat difficult to verify because JavaScript engines (and in
44
+ * some cases libraries) produce slightly different error messages for this
45
+ * particular scenario, and we need to account for this.
46
+ *
47
+ * @param error - The error.
48
+ * @returns True if the error indicates that the network is unreachable, and
49
+ * false otherwise.
50
+ */
51
+ function isNetworkUnreachableError(error) {
52
+ return (error instanceof TypeError && exports.NETWORK_UNREACHABLE_ERRORS.has(error.message));
53
+ }
54
+ exports.default = isNetworkUnreachableError;
55
+ /**
56
+ * Guarantees a URL, even given a string. This is useful for checking components
57
+ * of that URL.
58
+ *
59
+ * @param endpointUrlOrUrlString - Either a URL object or a string that
60
+ * represents the URL of an endpoint.
61
+ * @returns A URL object.
62
+ */
63
+ function getNormalizedEndpointUrl(endpointUrlOrUrlString) {
64
+ return endpointUrlOrUrlString instanceof URL
65
+ ? endpointUrlOrUrlString
66
+ : new URL(endpointUrlOrUrlString);
67
+ }
68
+ /**
69
+ * This class is responsible for making a request to an endpoint that implements
70
+ * the JSON-RPC protocol. It is designed to gracefully handle network and server
71
+ * failures, retrying requests using exponential backoff. It also offers a hook
72
+ * which can used to respond to slow requests.
73
+ */
74
+ class RpcService {
75
+ /**
76
+ * Constructs a new RpcService object.
77
+ *
78
+ * @param args - The arguments.
79
+ * @param args.fetch - A function that can be used to make an HTTP request.
80
+ * If your JavaScript environment supports `fetch` natively, you'll probably
81
+ * want to pass that; otherwise you can pass an equivalent (such as `fetch`
82
+ * via `node-fetch`).
83
+ * @param args.btoa - A function that can be used to convert a binary string
84
+ * into base-64. Used to encode authorization credentials.
85
+ * @param args.endpointUrl - The URL of the RPC endpoint.
86
+ * @param args.fetchOptions - A common set of options that will be used to
87
+ * make every request. Can be overridden on the request level (e.g. to add
88
+ * headers).
89
+ * @param args.failoverService - An RPC service that represents a failover
90
+ * endpoint which will be invoked while the circuit for _this_ service is
91
+ * open.
92
+ */
93
+ constructor({ fetch: givenFetch, btoa: givenBtoa, endpointUrl, fetchOptions = {}, failoverService, }) {
94
+ _RpcService_instances.add(this);
95
+ /**
96
+ * The function used to make an HTTP request.
97
+ */
98
+ _RpcService_fetch.set(this, void 0);
99
+ /**
100
+ * The URL of the RPC endpoint.
101
+ */
102
+ _RpcService_endpointUrl.set(this, void 0);
103
+ /**
104
+ * A common set of options that the request options will extend.
105
+ */
106
+ _RpcService_fetchOptions.set(this, void 0);
107
+ /**
108
+ * An RPC service that represents a failover endpoint which will be invoked
109
+ * while the circuit for _this_ service is open.
110
+ */
111
+ _RpcService_failoverService.set(this, void 0);
112
+ /**
113
+ * The policy that wraps the request.
114
+ */
115
+ _RpcService_policy.set(this, void 0);
116
+ __classPrivateFieldSet(this, _RpcService_fetch, givenFetch, "f");
117
+ __classPrivateFieldSet(this, _RpcService_endpointUrl, getNormalizedEndpointUrl(endpointUrl), "f");
118
+ __classPrivateFieldSet(this, _RpcService_fetchOptions, __classPrivateFieldGet(this, _RpcService_instances, "m", _RpcService_getDefaultFetchOptions).call(this, __classPrivateFieldGet(this, _RpcService_endpointUrl, "f"), fetchOptions, givenBtoa), "f");
119
+ __classPrivateFieldSet(this, _RpcService_failoverService, failoverService, "f");
120
+ const policy = (0, controller_utils_1.createServicePolicy)({
121
+ maxRetries: 4,
122
+ maxConsecutiveFailures: 15,
123
+ retryFilterPolicy: (0, controller_utils_1.handleWhen)((error) => {
124
+ return (
125
+ // Ignore errors where the request failed to establish
126
+ isNetworkUnreachableError(error) ||
127
+ // Ignore server sent HTML error pages or truncated JSON responses
128
+ error.message.includes('not valid JSON') ||
129
+ // Ignore server overload errors
130
+ error.message.includes('Gateway timeout') ||
131
+ ((0, utils_1.hasProperty)(error, 'code') &&
132
+ (error.code === 'ETIMEDOUT' || error.code === 'ECONNRESET')));
133
+ }),
134
+ });
135
+ __classPrivateFieldSet(this, _RpcService_policy, policy, "f");
136
+ }
137
+ /**
138
+ * Listens for when the RPC service retries the request.
139
+ *
140
+ * @param listener - The callback to be called when the retry occurs.
141
+ * @returns What {@link ServicePolicy.onRetry} returns.
142
+ * @see {@link createServicePolicy}
143
+ */
144
+ onRetry(listener) {
145
+ return __classPrivateFieldGet(this, _RpcService_policy, "f").onRetry((data) => {
146
+ listener({ ...data, endpointUrl: __classPrivateFieldGet(this, _RpcService_endpointUrl, "f").toString() });
147
+ });
148
+ }
149
+ /**
150
+ * Listens for when the RPC service retries the request too many times in a
151
+ * row.
152
+ *
153
+ * @param listener - The callback to be called when the circuit is broken.
154
+ * @returns What {@link ServicePolicy.onBreak} returns.
155
+ * @see {@link createServicePolicy}
156
+ */
157
+ onBreak(listener) {
158
+ return __classPrivateFieldGet(this, _RpcService_policy, "f").onBreak((data) => {
159
+ listener({ ...data, endpointUrl: __classPrivateFieldGet(this, _RpcService_endpointUrl, "f").toString() });
160
+ });
161
+ }
162
+ /**
163
+ * Listens for when the policy underlying this RPC service detects a slow
164
+ * request.
165
+ *
166
+ * @param listener - The callback to be called when the request is slow.
167
+ * @returns What {@link ServicePolicy.onDegraded} returns.
168
+ * @see {@link createServicePolicy}
169
+ */
170
+ onDegraded(listener) {
171
+ return __classPrivateFieldGet(this, _RpcService_policy, "f").onDegraded(() => {
172
+ listener({ endpointUrl: __classPrivateFieldGet(this, _RpcService_endpointUrl, "f").toString() });
173
+ });
174
+ }
175
+ async request(jsonRpcRequest, fetchOptions = {}) {
176
+ const completeFetchOptions = __classPrivateFieldGet(this, _RpcService_instances, "m", _RpcService_getCompleteFetchOptions).call(this, jsonRpcRequest, fetchOptions);
177
+ try {
178
+ return await __classPrivateFieldGet(this, _RpcService_instances, "m", _RpcService_executePolicy).call(this, jsonRpcRequest, completeFetchOptions);
179
+ }
180
+ catch (error) {
181
+ if (__classPrivateFieldGet(this, _RpcService_policy, "f").circuitBreakerPolicy.state === controller_utils_1.CircuitState.Open &&
182
+ __classPrivateFieldGet(this, _RpcService_failoverService, "f") !== undefined) {
183
+ return await __classPrivateFieldGet(this, _RpcService_failoverService, "f").request(jsonRpcRequest, completeFetchOptions);
184
+ }
185
+ throw error;
186
+ }
187
+ }
188
+ }
189
+ exports.RpcService = RpcService;
190
+ _RpcService_fetch = new WeakMap(), _RpcService_endpointUrl = new WeakMap(), _RpcService_fetchOptions = new WeakMap(), _RpcService_failoverService = new WeakMap(), _RpcService_policy = new WeakMap(), _RpcService_instances = new WeakSet(), _RpcService_getDefaultFetchOptions = function _RpcService_getDefaultFetchOptions(endpointUrl, fetchOptions, givenBtoa) {
191
+ if (endpointUrl.username && endpointUrl.password) {
192
+ const authString = `${endpointUrl.username}:${endpointUrl.password}`;
193
+ const encodedCredentials = givenBtoa(authString);
194
+ return (0, deepmerge_1.default)(fetchOptions, {
195
+ headers: { Authorization: `Basic ${encodedCredentials}` },
196
+ });
197
+ }
198
+ return fetchOptions;
199
+ }, _RpcService_getCompleteFetchOptions = function _RpcService_getCompleteFetchOptions(jsonRpcRequest, fetchOptions) {
200
+ const defaultOptions = {
201
+ method: 'POST',
202
+ headers: {
203
+ Accept: 'application/json',
204
+ 'Content-Type': 'application/json',
205
+ },
206
+ };
207
+ const mergedOptions = (0, deepmerge_1.default)(defaultOptions, (0, deepmerge_1.default)(__classPrivateFieldGet(this, _RpcService_fetchOptions, "f"), fetchOptions));
208
+ const { id, jsonrpc, method, params } = jsonRpcRequest;
209
+ const body = JSON.stringify({
210
+ id,
211
+ jsonrpc,
212
+ method,
213
+ params,
214
+ });
215
+ return { ...mergedOptions, body };
216
+ }, _RpcService_executePolicy =
217
+ /**
218
+ * Makes the request using the Cockatiel policy that this service creates.
219
+ *
220
+ * @param jsonRpcRequest - The JSON-RPC request to send to the endpoint.
221
+ * @param fetchOptions - The options for `fetch`; will be combined with the
222
+ * fetch options passed to the constructor
223
+ * @returns The decoded JSON-RPC response from the endpoint.
224
+ * @throws A "method not found" error if the response status is 405.
225
+ * @throws A rate limiting error if the response HTTP status is 429.
226
+ * @throws A timeout error if the response HTTP status is 503 or 504.
227
+ * @throws A generic error if the response HTTP status is not 2xx but also not
228
+ * 405, 429, 503, or 504.
229
+ */
230
+ async function _RpcService_executePolicy(jsonRpcRequest, fetchOptions) {
231
+ return await __classPrivateFieldGet(this, _RpcService_policy, "f").execute(async () => {
232
+ const response = await __classPrivateFieldGet(this, _RpcService_fetch, "f").call(this, __classPrivateFieldGet(this, _RpcService_endpointUrl, "f"), fetchOptions);
233
+ if (response.status === 405) {
234
+ throw rpc_errors_1.rpcErrors.methodNotFound();
235
+ }
236
+ if (response.status === 429) {
237
+ throw rpc_errors_1.rpcErrors.internal({ message: 'Request is being rate limited.' });
238
+ }
239
+ if (response.status === 503 || response.status === 504) {
240
+ throw rpc_errors_1.rpcErrors.internal({
241
+ message: 'Gateway timeout. The request took too long to process. This can happen when querying logs over too wide a block range.',
242
+ });
243
+ }
244
+ const text = await response.text();
245
+ if (jsonRpcRequest.method === 'eth_getBlockByNumber' &&
246
+ text === 'Not Found') {
247
+ return {
248
+ id: jsonRpcRequest.id,
249
+ jsonrpc: jsonRpcRequest.jsonrpc,
250
+ result: null,
251
+ };
252
+ }
253
+ // Type annotation: We assume that if this response is valid JSON, it's a
254
+ // valid JSON-RPC response.
255
+ let json;
256
+ try {
257
+ json = JSON.parse(text);
258
+ }
259
+ catch (error) {
260
+ if (error instanceof SyntaxError) {
261
+ throw rpc_errors_1.rpcErrors.internal({
262
+ message: 'Could not parse response as it is not valid JSON',
263
+ data: text,
264
+ });
265
+ }
266
+ else {
267
+ throw error;
268
+ }
269
+ }
270
+ if (!response.ok) {
271
+ throw rpc_errors_1.rpcErrors.internal({
272
+ message: `Non-200 status code: '${response.status}'`,
273
+ data: json,
274
+ });
275
+ }
276
+ return json;
277
+ });
278
+ };
279
+ //# sourceMappingURL=rpc-service.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rpc-service.cjs","sourceRoot":"","sources":["../../src/rpc-service/rpc-service.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ServicePolicy } from '@metamask/controller-utils';\nimport {\n CircuitState,\n createServicePolicy,\n handleWhen,\n} from '@metamask/controller-utils';\nimport { rpcErrors } from '@metamask/rpc-errors';\nimport type { JsonRpcRequest } from '@metamask/utils';\nimport {\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 * The list of error messages that represent a failure to reach 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 NETWORK_UNREACHABLE_ERRORS = new Set([\n 'network error', // Chrome\n 'Failed to fetch', // Chrome\n 'NetworkError when attempting to fetch resource.', // Firefox\n 'The Internet connection appears to be offline.', // Safari 16\n 'Load failed', // Safari 17+\n 'Network request failed', // `cross-fetch`\n 'fetch failed', // Undici (Node.js)\n 'terminated', // Undici (Node.js)\n]);\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 is unreachable, and\n * false otherwise.\n */\nexport default function isNetworkUnreachableError(error: unknown) {\n return (\n error instanceof TypeError && NETWORK_UNREACHABLE_ERRORS.has(error.message)\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: AbstractRpcService | undefined;\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 args - The arguments.\n * @param args.fetch - A function that can be used to make an HTTP request.\n * If your JavaScript environment supports `fetch` natively, you'll probably\n * want to pass that; otherwise you can pass an equivalent (such as `fetch`\n * via `node-fetch`).\n * @param args.btoa - A function that can be used to convert a binary string\n * into base-64. Used to encode authorization credentials.\n * @param args.endpointUrl - The URL of the RPC endpoint.\n * @param args.fetchOptions - A common set of options that will be used to\n * make every request. Can be overridden on the request level (e.g. to add\n * headers).\n * @param args.failoverService - An RPC service that represents a failover\n * endpoint which will be invoked while the circuit for _this_ service is\n * open.\n */\n constructor({\n fetch: givenFetch,\n btoa: givenBtoa,\n endpointUrl,\n fetchOptions = {},\n failoverService,\n }: {\n fetch: typeof fetch;\n btoa: typeof btoa;\n endpointUrl: URL | string;\n fetchOptions?: FetchOptions;\n failoverService?: AbstractRpcService;\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: 4,\n maxConsecutiveFailures: 15,\n retryFilterPolicy: handleWhen((error) => {\n return (\n // Ignore errors where the request failed to establish\n isNetworkUnreachableError(error) ||\n // Ignore server sent HTML error pages or truncated JSON responses\n error.message.includes('not valid JSON') ||\n // Ignore server overload errors\n error.message.includes('Gateway timeout') ||\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 }\n >,\n ) {\n return this.#policy.onBreak((data) => {\n listener({ ...data, endpointUrl: this.#endpointUrl.toString() });\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 A \"method not found\" error if the response status is 405.\n * @throws A rate limiting error if the response HTTP status is 429.\n * @throws A timeout error if the response HTTP status is 503 or 504.\n * @throws A generic error if the response HTTP status is not 2xx but also not\n * 405, 429, 503, or 504.\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 A \"method not found\" error if the response status is 405.\n * @throws A rate limiting error if the response HTTP status is 429.\n * @throws A timeout error if the response HTTP status is 503 or 504.\n * @throws A generic error if the response HTTP status is not 2xx but also not\n * 405, 429, 503, or 504.\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.#executePolicy<Params, Result>(\n jsonRpcRequest,\n completeFetchOptions,\n );\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 jsonRpcRequest - The JSON-RPC request to send to the endpoint.\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 A \"method not found\" error if the response status is 405.\n * @throws A rate limiting error if the response HTTP status is 429.\n * @throws A timeout error if the response HTTP status is 503 or 504.\n * @throws A generic error if the response HTTP status is not 2xx but also not\n * 405, 429, 503, or 504.\n */\n async #executePolicy<\n Params extends JsonRpcParams,\n Result extends Json,\n Request extends JsonRpcRequest = JsonRpcRequest<Params>,\n >(\n jsonRpcRequest: Request,\n fetchOptions: FetchOptions,\n ): Promise<JsonRpcResponse<Result> | JsonRpcResponse<null>> {\n return await this.#policy.execute(async () => {\n const response = await this.#fetch(this.#endpointUrl, fetchOptions);\n\n if (response.status === 405) {\n throw rpcErrors.methodNotFound();\n }\n\n if (response.status === 429) {\n throw rpcErrors.internal({ message: 'Request is being rate limited.' });\n }\n\n if (response.status === 503 || response.status === 504) {\n throw rpcErrors.internal({\n message:\n 'Gateway timeout. The request took too long to process. This can happen when querying logs over too wide a block range.',\n });\n }\n\n const text = await response.text();\n\n if (\n jsonRpcRequest.method === 'eth_getBlockByNumber' &&\n text === 'Not Found'\n ) {\n return {\n id: jsonRpcRequest.id,\n jsonrpc: jsonRpcRequest.jsonrpc,\n result: null,\n };\n }\n\n // Type annotation: We assume that if this response is valid JSON, it's a\n // valid JSON-RPC response.\n let json: JsonRpcResponse<Result>;\n try {\n json = JSON.parse(text);\n } catch (error) {\n if (error instanceof SyntaxError) {\n throw rpcErrors.internal({\n message: 'Could not parse response as it is not valid JSON',\n data: text,\n });\n } else {\n throw error;\n }\n }\n\n if (!response.ok) {\n throw rpcErrors.internal({\n message: `Non-200 status code: '${response.status}'`,\n data: json,\n });\n }\n\n return json;\n });\n }\n}\n"]}