@metamask-previews/network-controller 22.1.1-preview-bd04b248 → 22.1.1-preview-3fc797ab
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.
- package/dist/rpc-service/abstract-rpc-service.cjs +3 -0
- package/dist/rpc-service/abstract-rpc-service.cjs.map +1 -0
- package/dist/rpc-service/abstract-rpc-service.d.cts +11 -0
- package/dist/rpc-service/abstract-rpc-service.d.cts.map +1 -0
- package/dist/rpc-service/abstract-rpc-service.d.mts +11 -0
- package/dist/rpc-service/abstract-rpc-service.d.mts.map +1 -0
- package/dist/rpc-service/abstract-rpc-service.mjs +2 -0
- package/dist/rpc-service/abstract-rpc-service.mjs.map +1 -0
- package/dist/rpc-service/rpc-service.cjs +256 -0
- package/dist/rpc-service/rpc-service.cjs.map +1 -0
- package/dist/rpc-service/rpc-service.d.cts +121 -0
- package/dist/rpc-service/rpc-service.d.cts.map +1 -0
- package/dist/rpc-service/rpc-service.d.mts +121 -0
- package/dist/rpc-service/rpc-service.d.mts.map +1 -0
- package/dist/rpc-service/rpc-service.mjs +255 -0
- package/dist/rpc-service/rpc-service.mjs.map +1 -0
- package/dist/rpc-service/shared.cjs +3 -0
- package/dist/rpc-service/shared.cjs.map +1 -0
- package/dist/rpc-service/shared.d.cts +5 -0
- package/dist/rpc-service/shared.d.cts.map +1 -0
- package/dist/rpc-service/shared.d.mts +5 -0
- package/dist/rpc-service/shared.d.mts.map +1 -0
- package/dist/rpc-service/shared.mjs +2 -0
- package/dist/rpc-service/shared.mjs.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"abstract-rpc-service.cjs","sourceRoot":"","sources":["../../src/rpc-service/abstract-rpc-service.ts"],"names":[],"mappings":"","sourcesContent":["import type { ServicePolicy } from '@metamask/controller-utils';\nimport type {\n Json,\n JsonRpcParams,\n JsonRpcRequest,\n JsonRpcResponse,\n} from '@metamask/utils';\n\nimport type { FetchOptions } from './shared';\n\n/**\n * The interface for a service class responsible for making a request to an RPC\n * endpoint.\n */\nexport type AbstractRpcService = Partial<\n Pick<ServicePolicy, 'onBreak' | 'onRetry' | 'onDegraded'>\n> & {\n request<Params extends JsonRpcParams, Result extends Json>(\n jsonRpcRequest: JsonRpcRequest<Params>,\n fetchOptions?: FetchOptions,\n ): Promise<JsonRpcResponse<Result | null>>;\n};\n"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ServicePolicy } from "@metamask/controller-utils";
|
|
2
|
+
import type { Json, JsonRpcParams, JsonRpcRequest, JsonRpcResponse } from "@metamask/utils";
|
|
3
|
+
import type { FetchOptions } from "./shared.cjs";
|
|
4
|
+
/**
|
|
5
|
+
* The interface for a service class responsible for making a request to an RPC
|
|
6
|
+
* endpoint.
|
|
7
|
+
*/
|
|
8
|
+
export type AbstractRpcService = Partial<Pick<ServicePolicy, 'onBreak' | 'onRetry' | 'onDegraded'>> & {
|
|
9
|
+
request<Params extends JsonRpcParams, Result extends Json>(jsonRpcRequest: JsonRpcRequest<Params>, fetchOptions?: FetchOptions): Promise<JsonRpcResponse<Result | null>>;
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=abstract-rpc-service.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"abstract-rpc-service.d.cts","sourceRoot":"","sources":["../../src/rpc-service/abstract-rpc-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,mCAAmC;AAChE,OAAO,KAAK,EACV,IAAI,EACJ,aAAa,EACb,cAAc,EACd,eAAe,EAChB,wBAAwB;AAEzB,OAAO,KAAK,EAAE,YAAY,EAAE,qBAAiB;AAE7C;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG,OAAO,CACtC,IAAI,CAAC,aAAa,EAAE,SAAS,GAAG,SAAS,GAAG,YAAY,CAAC,CAC1D,GAAG;IACF,OAAO,CAAC,MAAM,SAAS,aAAa,EAAE,MAAM,SAAS,IAAI,EACvD,cAAc,EAAE,cAAc,CAAC,MAAM,CAAC,EACtC,YAAY,CAAC,EAAE,YAAY,GAC1B,OAAO,CAAC,eAAe,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC;CAC5C,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ServicePolicy } from "@metamask/controller-utils";
|
|
2
|
+
import type { Json, JsonRpcParams, JsonRpcRequest, JsonRpcResponse } from "@metamask/utils";
|
|
3
|
+
import type { FetchOptions } from "./shared.mjs";
|
|
4
|
+
/**
|
|
5
|
+
* The interface for a service class responsible for making a request to an RPC
|
|
6
|
+
* endpoint.
|
|
7
|
+
*/
|
|
8
|
+
export type AbstractRpcService = Partial<Pick<ServicePolicy, 'onBreak' | 'onRetry' | 'onDegraded'>> & {
|
|
9
|
+
request<Params extends JsonRpcParams, Result extends Json>(jsonRpcRequest: JsonRpcRequest<Params>, fetchOptions?: FetchOptions): Promise<JsonRpcResponse<Result | null>>;
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=abstract-rpc-service.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"abstract-rpc-service.d.mts","sourceRoot":"","sources":["../../src/rpc-service/abstract-rpc-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,mCAAmC;AAChE,OAAO,KAAK,EACV,IAAI,EACJ,aAAa,EACb,cAAc,EACd,eAAe,EAChB,wBAAwB;AAEzB,OAAO,KAAK,EAAE,YAAY,EAAE,qBAAiB;AAE7C;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG,OAAO,CACtC,IAAI,CAAC,aAAa,EAAE,SAAS,GAAG,SAAS,GAAG,YAAY,CAAC,CAC1D,GAAG;IACF,OAAO,CAAC,MAAM,SAAS,aAAa,EAAE,MAAM,SAAS,IAAI,EACvD,cAAc,EAAE,cAAc,CAAC,MAAM,CAAC,EACtC,YAAY,CAAC,EAAE,YAAY,GAC1B,OAAO,CAAC,eAAe,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC;CAC5C,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"abstract-rpc-service.mjs","sourceRoot":"","sources":["../../src/rpc-service/abstract-rpc-service.ts"],"names":[],"mappings":"","sourcesContent":["import type { ServicePolicy } from '@metamask/controller-utils';\nimport type {\n Json,\n JsonRpcParams,\n JsonRpcRequest,\n JsonRpcResponse,\n} from '@metamask/utils';\n\nimport type { FetchOptions } from './shared';\n\n/**\n * The interface for a service class responsible for making a request to an RPC\n * endpoint.\n */\nexport type AbstractRpcService = Partial<\n Pick<ServicePolicy, 'onBreak' | 'onRetry' | 'onDegraded'>\n> & {\n request<Params extends JsonRpcParams, Result extends Json>(\n jsonRpcRequest: JsonRpcRequest<Params>,\n fetchOptions?: FetchOptions,\n ): Promise<JsonRpcResponse<Result | null>>;\n};\n"]}
|
|
@@ -0,0 +1,256 @@
|
|
|
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_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 encode 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
|
+
*/
|
|
90
|
+
constructor({ fetch: givenFetch, btoa: givenBtoa, endpointUrl, fetchOptions = {}, }) {
|
|
91
|
+
_RpcService_instances.add(this);
|
|
92
|
+
/**
|
|
93
|
+
* The function used to make an HTTP request.
|
|
94
|
+
*/
|
|
95
|
+
_RpcService_fetch.set(this, void 0);
|
|
96
|
+
/**
|
|
97
|
+
* The URL of the RPC endpoint.
|
|
98
|
+
*/
|
|
99
|
+
_RpcService_endpointUrl.set(this, void 0);
|
|
100
|
+
/**
|
|
101
|
+
* A common set of options that the request options will extend.
|
|
102
|
+
*/
|
|
103
|
+
_RpcService_fetchOptions.set(this, void 0);
|
|
104
|
+
/**
|
|
105
|
+
* The policy that wraps the request.
|
|
106
|
+
*/
|
|
107
|
+
_RpcService_policy.set(this, void 0);
|
|
108
|
+
__classPrivateFieldSet(this, _RpcService_fetch, givenFetch, "f");
|
|
109
|
+
__classPrivateFieldSet(this, _RpcService_endpointUrl, getNormalizedEndpointUrl(endpointUrl), "f");
|
|
110
|
+
__classPrivateFieldSet(this, _RpcService_fetchOptions, __classPrivateFieldGet(this, _RpcService_instances, "m", _RpcService_getDefaultFetchOptions).call(this, __classPrivateFieldGet(this, _RpcService_endpointUrl, "f"), fetchOptions, givenBtoa), "f");
|
|
111
|
+
const policy = (0, controller_utils_1.createServicePolicy)({
|
|
112
|
+
maxRetries: 4,
|
|
113
|
+
maxConsecutiveFailures: 15,
|
|
114
|
+
retryFilterPolicy: (0, controller_utils_1.handleWhen)((error) => {
|
|
115
|
+
return (
|
|
116
|
+
// Ignore errors where the request failed to establish
|
|
117
|
+
isNetworkUnreachableError(error) ||
|
|
118
|
+
// Ignore server sent HTML error pages or truncated JSON responses
|
|
119
|
+
error.message.includes('not valid JSON') ||
|
|
120
|
+
// Ignore server overload errors
|
|
121
|
+
error.message.includes('Gateway timeout') ||
|
|
122
|
+
((0, utils_1.hasProperty)(error, 'code') &&
|
|
123
|
+
(error.code === 'ETIMEDOUT' || error.code === 'ECONNRESET')));
|
|
124
|
+
}),
|
|
125
|
+
});
|
|
126
|
+
__classPrivateFieldSet(this, _RpcService_policy, policy, "f");
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Listens for when the retry policy underlying this RPC service retries the
|
|
130
|
+
* request.
|
|
131
|
+
*
|
|
132
|
+
* @param listener - The callback to be called when the retry occurs.
|
|
133
|
+
* @returns What {@link ServicePolicy.onRetry} returns.
|
|
134
|
+
* @see {@link createServicePolicy}
|
|
135
|
+
*/
|
|
136
|
+
onRetry(listener) {
|
|
137
|
+
return __classPrivateFieldGet(this, _RpcService_policy, "f").onRetry(listener);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Listens for when the circuit breaker policy underlying this RPC service
|
|
141
|
+
* detects a broken circuit.
|
|
142
|
+
*
|
|
143
|
+
* @param listener - The callback to be called when the circuit is broken.
|
|
144
|
+
* @returns What {@link ServicePolicy.onBreak} returns.
|
|
145
|
+
* @see {@link createServicePolicy}
|
|
146
|
+
*/
|
|
147
|
+
onBreak(listener) {
|
|
148
|
+
return __classPrivateFieldGet(this, _RpcService_policy, "f").onBreak(listener);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Listens for when the policy underlying this RPC service detects a slow
|
|
152
|
+
* request.
|
|
153
|
+
*
|
|
154
|
+
* @param listener - The callback to be called when the request is slow.
|
|
155
|
+
* @returns What {@link ServicePolicy.onDegraded} returns.
|
|
156
|
+
* @see {@link createServicePolicy}
|
|
157
|
+
*/
|
|
158
|
+
onDegraded(listener) {
|
|
159
|
+
return __classPrivateFieldGet(this, _RpcService_policy, "f").onDegraded(listener);
|
|
160
|
+
}
|
|
161
|
+
async request(jsonRpcRequest, fetchOptions = {}) {
|
|
162
|
+
const completeFetchOptions = __classPrivateFieldGet(this, _RpcService_instances, "m", _RpcService_getCompleteFetchOptions).call(this, jsonRpcRequest, fetchOptions);
|
|
163
|
+
return await __classPrivateFieldGet(this, _RpcService_instances, "m", _RpcService_executePolicy).call(this, jsonRpcRequest, completeFetchOptions);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
exports.RpcService = RpcService;
|
|
167
|
+
_RpcService_fetch = new WeakMap(), _RpcService_endpointUrl = new WeakMap(), _RpcService_fetchOptions = new WeakMap(), _RpcService_policy = new WeakMap(), _RpcService_instances = new WeakSet(), _RpcService_getDefaultFetchOptions = function _RpcService_getDefaultFetchOptions(endpointUrl, fetchOptions, givenBtoa) {
|
|
168
|
+
if (endpointUrl.username && endpointUrl.password) {
|
|
169
|
+
const authString = `${endpointUrl.username}:${endpointUrl.password}`;
|
|
170
|
+
const encodedCredentials = givenBtoa(authString);
|
|
171
|
+
return (0, deepmerge_1.default)(fetchOptions, {
|
|
172
|
+
headers: { Authorization: `Basic ${encodedCredentials}` },
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
return fetchOptions;
|
|
176
|
+
}, _RpcService_getCompleteFetchOptions = function _RpcService_getCompleteFetchOptions(jsonRpcRequest, fetchOptions) {
|
|
177
|
+
const defaultOptions = {
|
|
178
|
+
method: 'POST',
|
|
179
|
+
headers: {
|
|
180
|
+
Accept: 'application/json',
|
|
181
|
+
'Content-Type': 'application/json',
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
const mergedOptions = (0, deepmerge_1.default)(defaultOptions, (0, deepmerge_1.default)(__classPrivateFieldGet(this, _RpcService_fetchOptions, "f"), fetchOptions));
|
|
185
|
+
const { id, jsonrpc, method, params } = jsonRpcRequest;
|
|
186
|
+
const body = JSON.stringify({
|
|
187
|
+
id,
|
|
188
|
+
jsonrpc,
|
|
189
|
+
method,
|
|
190
|
+
params,
|
|
191
|
+
});
|
|
192
|
+
return { ...mergedOptions, body };
|
|
193
|
+
}, _RpcService_executePolicy =
|
|
194
|
+
/**
|
|
195
|
+
* Makes the request using the Cockatiel policy that this service creates.
|
|
196
|
+
*
|
|
197
|
+
* @param jsonRpcRequest - The JSON-RPC request to send to the endpoint.
|
|
198
|
+
* @param fetchOptions - The options for `fetch`; will be combined with the
|
|
199
|
+
* fetch options passed to the constructor
|
|
200
|
+
* @returns The decoded JSON-RPC response from the endpoint.
|
|
201
|
+
* @throws A "method not found" error if the response status is 405.
|
|
202
|
+
* @throws A rate limiting error if the response HTTP status is 429.
|
|
203
|
+
* @throws A timeout error if the response HTTP status is 503 or 504.
|
|
204
|
+
* @throws A generic error if the response HTTP status is not 2xx but also not
|
|
205
|
+
* 405, 429, 503, or 504.
|
|
206
|
+
*/
|
|
207
|
+
async function _RpcService_executePolicy(jsonRpcRequest, fetchOptions) {
|
|
208
|
+
return await __classPrivateFieldGet(this, _RpcService_policy, "f").execute(async () => {
|
|
209
|
+
const response = await __classPrivateFieldGet(this, _RpcService_fetch, "f").call(this, __classPrivateFieldGet(this, _RpcService_endpointUrl, "f"), fetchOptions);
|
|
210
|
+
if (response.status === 405) {
|
|
211
|
+
throw rpc_errors_1.rpcErrors.methodNotFound();
|
|
212
|
+
}
|
|
213
|
+
if (response.status === 429) {
|
|
214
|
+
throw rpc_errors_1.rpcErrors.internal({ message: 'Request is being rate limited.' });
|
|
215
|
+
}
|
|
216
|
+
if (response.status === 503 || response.status === 504) {
|
|
217
|
+
throw rpc_errors_1.rpcErrors.internal({
|
|
218
|
+
message: 'Gateway timeout. The request took too long to process. This can happen when querying logs over too wide a block range.',
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
const text = await response.text();
|
|
222
|
+
if (jsonRpcRequest.method === 'eth_getBlockByNumber' &&
|
|
223
|
+
text === 'Not Found') {
|
|
224
|
+
return {
|
|
225
|
+
id: jsonRpcRequest.id,
|
|
226
|
+
jsonrpc: jsonRpcRequest.jsonrpc,
|
|
227
|
+
result: null,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
// Type annotation: We assume that if this response is valid JSON, it's a
|
|
231
|
+
// valid JSON-RPC response.
|
|
232
|
+
let json;
|
|
233
|
+
try {
|
|
234
|
+
json = JSON.parse(text);
|
|
235
|
+
}
|
|
236
|
+
catch (error) {
|
|
237
|
+
if (error instanceof SyntaxError) {
|
|
238
|
+
throw rpc_errors_1.rpcErrors.internal({
|
|
239
|
+
message: 'Could not parse response as it is not valid JSON',
|
|
240
|
+
data: text,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
throw error;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
if (!response.ok) {
|
|
248
|
+
throw rpc_errors_1.rpcErrors.internal({
|
|
249
|
+
message: `Non-200 status code: '${response.status}'`,
|
|
250
|
+
data: json,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
return json;
|
|
254
|
+
});
|
|
255
|
+
};
|
|
256
|
+
//# 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":";;;;;;;;;;;;;;;;;;AACA,iEAA6E;AAC7E,qDAAiD;AAEjD,2CAKyB;AACzB,0DAAkC;AAKlC;;;;;GAKG;AACU,QAAA,0BAA0B,GAAG,IAAI,GAAG,CAAC;IAChD,eAAe;IACf,iBAAiB;IACjB,iDAAiD;IACjD,gDAAgD;IAChD,aAAa;IACb,wBAAwB;IACxB,cAAc;IACd,YAAY,EAAE,mBAAmB;CAClC,CAAC,CAAC;AAEH;;;;;;;;;;;GAWG;AACH,SAAwB,yBAAyB,CAAC,KAAc;IAC9D,OAAO,CACL,KAAK,YAAY,SAAS,IAAI,kCAA0B,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAC5E,CAAC;AACJ,CAAC;AAJD,4CAIC;AAED;;;;;;;GAOG;AACH,SAAS,wBAAwB,CAAC,sBAAoC;IACpE,OAAO,sBAAsB,YAAY,GAAG;QAC1C,CAAC,CAAC,sBAAsB;QACxB,CAAC,CAAC,IAAI,GAAG,CAAC,sBAAsB,CAAC,CAAC;AACtC,CAAC;AAED;;;;;GAKG;AACH,MAAa,UAAU;IAqBrB;;;;;;;;;;;;;;OAcG;IACH,YAAY,EACV,KAAK,EAAE,UAAU,EACjB,IAAI,EAAE,SAAS,EACf,WAAW,EACX,YAAY,GAAG,EAAE,GAMlB;;QA7CD;;WAEG;QACM,oCAAqB;QAE9B;;WAEG;QACM,0CAAkB;QAE3B;;WAEG;QACM,2CAA4B;QAErC;;WAEG;QACM,qCAAuB;QA4B9B,uBAAA,IAAI,qBAAU,UAAU,MAAA,CAAC;QACzB,uBAAA,IAAI,2BAAgB,wBAAwB,CAAC,WAAW,CAAC,MAAA,CAAC;QAC1D,uBAAA,IAAI,4BAAiB,uBAAA,IAAI,iEAAwB,MAA5B,IAAI,EACvB,uBAAA,IAAI,+BAAa,EACjB,YAAY,EACZ,SAAS,CACV,MAAA,CAAC;QAEF,MAAM,MAAM,GAAG,IAAA,sCAAmB,EAAC;YACjC,UAAU,EAAE,CAAC;YACb,sBAAsB,EAAE,EAAE;YAC1B,iBAAiB,EAAE,IAAA,6BAAU,EAAC,CAAC,KAAK,EAAE,EAAE;gBACtC,OAAO;gBACL,sDAAsD;gBACtD,yBAAyB,CAAC,KAAK,CAAC;oBAChC,kEAAkE;oBAClE,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC;oBACxC,gCAAgC;oBAChC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC;oBACzC,CAAC,IAAA,mBAAW,EAAC,KAAK,EAAE,MAAM,CAAC;wBACzB,CAAC,KAAK,CAAC,IAAI,KAAK,WAAW,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,CAAC,CAAC,CAC/D,CAAC;YACJ,CAAC,CAAC;SACH,CAAC,CAAC;QACH,uBAAA,IAAI,sBAAW,MAAM,MAAA,CAAC;IACxB,CAAC;IAED;;;;;;;OAOG;IACH,OAAO,CAAC,QAAiD;QACvD,OAAO,uBAAA,IAAI,0BAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACxC,CAAC;IAED;;;;;;;OAOG;IACH,OAAO,CAAC,QAAiD;QACvD,OAAO,uBAAA,IAAI,0BAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACxC,CAAC;IAED;;;;;;;OAOG;IACH,UAAU,CAAC,QAAoD;QAC7D,OAAO,uBAAA,IAAI,0BAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC3C,CAAC;IA8CD,KAAK,CAAC,OAAO,CACX,cAAsC,EACtC,eAA6B,EAAE;QAE/B,MAAM,oBAAoB,GAAG,uBAAA,IAAI,kEAAyB,MAA7B,IAAI,EAC/B,cAAc,EACd,YAAY,CACb,CAAC;QAEF,OAAO,MAAM,uBAAA,IAAI,wDAAe,MAAnB,IAAI,EACf,cAAc,EACd,oBAAoB,CACrB,CAAC;IACJ,CAAC;CA8IF;AArTD,gCAqTC;kRAhIG,WAAgB,EAChB,YAA0B,EAC1B,SAA6C;IAE7C,IAAI,WAAW,CAAC,QAAQ,IAAI,WAAW,CAAC,QAAQ,EAAE;QAChD,MAAM,UAAU,GAAG,GAAG,WAAW,CAAC,QAAQ,IAAI,WAAW,CAAC,QAAQ,EAAE,CAAC;QACrE,MAAM,kBAAkB,GAAG,SAAS,CAAC,UAAU,CAAC,CAAC;QACjD,OAAO,IAAA,mBAAS,EAAC,YAAY,EAAE;YAC7B,OAAO,EAAE,EAAE,aAAa,EAAE,SAAS,kBAAkB,EAAE,EAAE;SAC1D,CAAC,CAAC;KACJ;IAED,OAAO,YAAY,CAAC;AACtB,CAAC,qFAWC,cAAsC,EACtC,YAA0B;IAE1B,MAAM,cAAc,GAAG;QACrB,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,MAAM,EAAE,kBAAkB;YAC1B,cAAc,EAAE,kBAAkB;SACnC;KACF,CAAC;IACF,MAAM,aAAa,GAAG,IAAA,mBAAS,EAC7B,cAAc,EACd,IAAA,mBAAS,EAAC,uBAAA,IAAI,gCAAc,EAAE,YAAY,CAAC,CAC5C,CAAC;IAEF,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,cAAc,CAAC;IACvD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC;QAC1B,EAAE;QACF,OAAO;QACP,MAAM;QACN,MAAM;KACP,CAAC,CAAC;IAEH,OAAO,EAAE,GAAG,aAAa,EAAE,IAAI,EAAE,CAAC;AACpC,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,KAAK,oCAKH,cAAuB,EACvB,YAA0B;IAE1B,OAAO,MAAM,uBAAA,IAAI,0BAAQ,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE;QAC3C,MAAM,QAAQ,GAAG,MAAM,uBAAA,IAAI,yBAAO,MAAX,IAAI,EAAQ,uBAAA,IAAI,+BAAa,EAAE,YAAY,CAAC,CAAC;QAEpE,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE;YAC3B,MAAM,sBAAS,CAAC,cAAc,EAAE,CAAC;SAClC;QAED,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE;YAC3B,MAAM,sBAAS,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,gCAAgC,EAAE,CAAC,CAAC;SACzE;QAED,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE;YACtD,MAAM,sBAAS,CAAC,QAAQ,CAAC;gBACvB,OAAO,EACL,wHAAwH;aAC3H,CAAC,CAAC;SACJ;QAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QAEnC,IACE,cAAc,CAAC,MAAM,KAAK,sBAAsB;YAChD,IAAI,KAAK,WAAW,EACpB;YACA,OAAO;gBACL,EAAE,EAAE,cAAc,CAAC,EAAE;gBACrB,OAAO,EAAE,cAAc,CAAC,OAAO;gBAC/B,MAAM,EAAE,IAAI;aACb,CAAC;SACH;QAED,yEAAyE;QACzE,2BAA2B;QAC3B,IAAI,IAA6B,CAAC;QAClC,IAAI;YACF,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;SACzB;QAAC,OAAO,KAAK,EAAE;YACd,IAAI,KAAK,YAAY,WAAW,EAAE;gBAChC,MAAM,sBAAS,CAAC,QAAQ,CAAC;oBACvB,OAAO,EAAE,kDAAkD;oBAC3D,IAAI,EAAE,IAAI;iBACX,CAAC,CAAC;aACJ;iBAAM;gBACL,MAAM,KAAK,CAAC;aACb;SACF;QAED,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE;YAChB,MAAM,sBAAS,CAAC,QAAQ,CAAC;gBACvB,OAAO,EAAE,yBAAyB,QAAQ,CAAC,MAAM,GAAG;gBACpD,IAAI,EAAE,IAAI;aACX,CAAC,CAAC;SACJ;QAED,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import type { ServicePolicy } from '@metamask/controller-utils';\nimport { createServicePolicy, handleWhen } 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 { 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 * 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 encode 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 */\n constructor({\n fetch: givenFetch,\n btoa: givenBtoa,\n endpointUrl,\n fetchOptions = {},\n }: {\n fetch: typeof fetch;\n btoa: typeof btoa;\n endpointUrl: URL | string;\n fetchOptions?: FetchOptions;\n }) {\n this.#fetch = givenFetch;\n this.#endpointUrl = getNormalizedEndpointUrl(endpointUrl);\n this.#fetchOptions = this.#getDefaultFetchOptions(\n this.#endpointUrl,\n fetchOptions,\n givenBtoa,\n );\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 retry policy underlying this RPC service retries the\n * 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(listener: Parameters<ServicePolicy['onRetry']>[0]) {\n return this.#policy.onRetry(listener);\n }\n\n /**\n * Listens for when the circuit breaker policy underlying this RPC service\n * detects a broken circuit.\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(listener: Parameters<ServicePolicy['onBreak']>[0]) {\n return this.#policy.onBreak(listener);\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(listener: Parameters<ServicePolicy['onDegraded']>[0]) {\n return this.#policy.onDegraded(listener);\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 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.\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 return await this.#executePolicy<Params, Result>(\n jsonRpcRequest,\n completeFetchOptions,\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"]}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { ServicePolicy } from "@metamask/controller-utils";
|
|
2
|
+
import type { JsonRpcRequest } from "@metamask/utils";
|
|
3
|
+
import { type Json, type JsonRpcParams, type JsonRpcResponse } from "@metamask/utils";
|
|
4
|
+
import type { AbstractRpcService } from "./abstract-rpc-service.cjs";
|
|
5
|
+
import type { FetchOptions } from "./shared.cjs";
|
|
6
|
+
/**
|
|
7
|
+
* The list of error messages that represent a failure to reach the network.
|
|
8
|
+
*
|
|
9
|
+
* This list was derived from Sindre Sorhus's `is-network-error` package:
|
|
10
|
+
* <https://github.com/sindresorhus/is-network-error/blob/7bbfa8be9482ce1427a21fbff60e3ee1650dd091/index.js>
|
|
11
|
+
*/
|
|
12
|
+
export declare const NETWORK_UNREACHABLE_ERRORS: Set<string>;
|
|
13
|
+
/**
|
|
14
|
+
* Determines whether the given error represents a failure to reach the network
|
|
15
|
+
* after request parameters have been validated.
|
|
16
|
+
*
|
|
17
|
+
* This is somewhat difficult to verify because JavaScript engines (and in
|
|
18
|
+
* some cases libraries) produce slightly different error messages for this
|
|
19
|
+
* particular scenario, and we need to account for this.
|
|
20
|
+
*
|
|
21
|
+
* @param error - The error.
|
|
22
|
+
* @returns True if the error indicates that the network is unreachable, and
|
|
23
|
+
* false otherwise.
|
|
24
|
+
*/
|
|
25
|
+
export default function isNetworkUnreachableError(error: unknown): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* This class is responsible for making a request to an endpoint that implements
|
|
28
|
+
* the JSON-RPC protocol. It is designed to gracefully handle network and server
|
|
29
|
+
* failures, retrying requests using exponential backoff. It also offers a hook
|
|
30
|
+
* which can used to respond to slow requests.
|
|
31
|
+
*/
|
|
32
|
+
export declare class RpcService implements AbstractRpcService {
|
|
33
|
+
#private;
|
|
34
|
+
/**
|
|
35
|
+
* Constructs a new RpcService object.
|
|
36
|
+
*
|
|
37
|
+
* @param args - The arguments.
|
|
38
|
+
* @param args.fetch - A function that can be used to make an HTTP request.
|
|
39
|
+
* If your JavaScript environment supports `fetch` natively, you'll probably
|
|
40
|
+
* want to pass that; otherwise you can pass an equivalent (such as `fetch`
|
|
41
|
+
* via `node-fetch`).
|
|
42
|
+
* @param args.btoa - A function that can be used to encode a binary string
|
|
43
|
+
* into base 64. Used to encode authorization credentials.
|
|
44
|
+
* @param args.endpointUrl - The URL of the RPC endpoint.
|
|
45
|
+
* @param args.fetchOptions - A common set of options that will be used to
|
|
46
|
+
* make every request. Can be overridden on the request level (e.g. to add
|
|
47
|
+
* headers).
|
|
48
|
+
*/
|
|
49
|
+
constructor({ fetch: givenFetch, btoa: givenBtoa, endpointUrl, fetchOptions, }: {
|
|
50
|
+
fetch: typeof fetch;
|
|
51
|
+
btoa: typeof btoa;
|
|
52
|
+
endpointUrl: URL | string;
|
|
53
|
+
fetchOptions?: FetchOptions;
|
|
54
|
+
});
|
|
55
|
+
/**
|
|
56
|
+
* Listens for when the retry policy underlying this RPC service retries the
|
|
57
|
+
* request.
|
|
58
|
+
*
|
|
59
|
+
* @param listener - The callback to be called when the retry occurs.
|
|
60
|
+
* @returns What {@link ServicePolicy.onRetry} returns.
|
|
61
|
+
* @see {@link createServicePolicy}
|
|
62
|
+
*/
|
|
63
|
+
onRetry(listener: Parameters<ServicePolicy['onRetry']>[0]): import("cockatiel").IDisposable;
|
|
64
|
+
/**
|
|
65
|
+
* Listens for when the circuit breaker policy underlying this RPC service
|
|
66
|
+
* detects a broken circuit.
|
|
67
|
+
*
|
|
68
|
+
* @param listener - The callback to be called when the circuit is broken.
|
|
69
|
+
* @returns What {@link ServicePolicy.onBreak} returns.
|
|
70
|
+
* @see {@link createServicePolicy}
|
|
71
|
+
*/
|
|
72
|
+
onBreak(listener: Parameters<ServicePolicy['onBreak']>[0]): import("cockatiel").IDisposable;
|
|
73
|
+
/**
|
|
74
|
+
* Listens for when the policy underlying this RPC service detects a slow
|
|
75
|
+
* request.
|
|
76
|
+
*
|
|
77
|
+
* @param listener - The callback to be called when the request is slow.
|
|
78
|
+
* @returns What {@link ServicePolicy.onDegraded} returns.
|
|
79
|
+
* @see {@link createServicePolicy}
|
|
80
|
+
*/
|
|
81
|
+
onDegraded(listener: Parameters<ServicePolicy['onDegraded']>[0]): void;
|
|
82
|
+
/**
|
|
83
|
+
* Makes a request to the RPC endpoint.
|
|
84
|
+
*
|
|
85
|
+
* This overload is specifically designed for `eth_getBlockByNumber`, which
|
|
86
|
+
* can return a `result` of `null` despite an expected `Result` being
|
|
87
|
+
* provided.
|
|
88
|
+
*
|
|
89
|
+
* @param jsonRpcRequest - The JSON-RPC request to send to the endpoint.
|
|
90
|
+
* @param fetchOptions - An options bag for {@link fetch} which further
|
|
91
|
+
* specifies the request.
|
|
92
|
+
* @returns The decoded JSON-RPC response from the endpoint.
|
|
93
|
+
* @throws A "method not found" error if the response status is 405.
|
|
94
|
+
* @throws A rate limiting error if the response HTTP status is 429.
|
|
95
|
+
* @throws A timeout error if the response HTTP status is 503 or 504.
|
|
96
|
+
* @throws A generic error if the response HTTP status is not 2xx but also not
|
|
97
|
+
* 405, 429, 503, or 504.
|
|
98
|
+
*/
|
|
99
|
+
request<Params extends JsonRpcParams, Result extends Json>(jsonRpcRequest: JsonRpcRequest<Params> & {
|
|
100
|
+
method: 'eth_getBlockByNumber';
|
|
101
|
+
}, fetchOptions?: FetchOptions): Promise<JsonRpcResponse<Result> | JsonRpcResponse<null>>;
|
|
102
|
+
/**
|
|
103
|
+
* Makes a request to the RPC endpoint.
|
|
104
|
+
*
|
|
105
|
+
* This overload is designed for all RPC methods except for
|
|
106
|
+
* `eth_getBlockByNumber`, which are expected to return a `result` of the
|
|
107
|
+
* expected `Result`.
|
|
108
|
+
*
|
|
109
|
+
* @param jsonRpcRequest - The JSON-RPC request to send to the endpoint.
|
|
110
|
+
* @param fetchOptions - An options bag for {@link fetch} which further
|
|
111
|
+
* specifies the request.
|
|
112
|
+
* @returns The decoded JSON-RPC response from the endpoint.
|
|
113
|
+
* @throws A "method not found" error if the response status is 405.
|
|
114
|
+
* @throws A rate limiting error if the response HTTP status is 429.
|
|
115
|
+
* @throws A timeout error if the response HTTP status is 503 or 504.
|
|
116
|
+
* @throws A generic error if the response HTTP status is not 2xx but also not
|
|
117
|
+
* 405, 429, 503, or 504.
|
|
118
|
+
*/
|
|
119
|
+
request<Params extends JsonRpcParams, Result extends Json>(jsonRpcRequest: JsonRpcRequest<Params>, fetchOptions?: FetchOptions): Promise<JsonRpcResponse<Result>>;
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=rpc-service.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rpc-service.d.cts","sourceRoot":"","sources":["../../src/rpc-service/rpc-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,mCAAmC;AAGhE,OAAO,KAAK,EAAE,cAAc,EAAE,wBAAwB;AACtD,OAAO,EAEL,KAAK,IAAI,EACT,KAAK,aAAa,EAClB,KAAK,eAAe,EACrB,wBAAwB;AAGzB,OAAO,KAAK,EAAE,kBAAkB,EAAE,mCAA+B;AACjE,OAAO,KAAK,EAAE,YAAY,EAAE,qBAAiB;AAE7C;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,aASrC,CAAC;AAEH;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,OAAO,UAAU,yBAAyB,CAAC,KAAK,EAAE,OAAO,WAI/D;AAgBD;;;;;GAKG;AACH,qBAAa,UAAW,YAAW,kBAAkB;;IAqBnD;;;;;;;;;;;;;;OAcG;gBACS,EACV,KAAK,EAAE,UAAU,EACjB,IAAI,EAAE,SAAS,EACf,WAAW,EACX,YAAiB,GAClB,EAAE;QACD,KAAK,EAAE,OAAO,KAAK,CAAC;QACpB,IAAI,EAAE,OAAO,IAAI,CAAC;QAClB,WAAW,EAAE,GAAG,GAAG,MAAM,CAAC;QAC1B,YAAY,CAAC,EAAE,YAAY,CAAC;KAC7B;IA4BD;;;;;;;OAOG;IACH,OAAO,CAAC,QAAQ,EAAE,UAAU,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;IAIzD;;;;;;;OAOG;IACH,OAAO,CAAC,QAAQ,EAAE,UAAU,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;IAIzD;;;;;;;OAOG;IACH,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;IAI/D;;;;;;;;;;;;;;;;OAgBG;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;;;;;;;;;;;;;;;;OAgBG;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;CA6JpC"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { ServicePolicy } from "@metamask/controller-utils";
|
|
2
|
+
import type { JsonRpcRequest } from "@metamask/utils";
|
|
3
|
+
import { type Json, type JsonRpcParams, type JsonRpcResponse } from "@metamask/utils";
|
|
4
|
+
import type { AbstractRpcService } from "./abstract-rpc-service.mjs";
|
|
5
|
+
import type { FetchOptions } from "./shared.mjs";
|
|
6
|
+
/**
|
|
7
|
+
* The list of error messages that represent a failure to reach the network.
|
|
8
|
+
*
|
|
9
|
+
* This list was derived from Sindre Sorhus's `is-network-error` package:
|
|
10
|
+
* <https://github.com/sindresorhus/is-network-error/blob/7bbfa8be9482ce1427a21fbff60e3ee1650dd091/index.js>
|
|
11
|
+
*/
|
|
12
|
+
export declare const NETWORK_UNREACHABLE_ERRORS: Set<string>;
|
|
13
|
+
/**
|
|
14
|
+
* Determines whether the given error represents a failure to reach the network
|
|
15
|
+
* after request parameters have been validated.
|
|
16
|
+
*
|
|
17
|
+
* This is somewhat difficult to verify because JavaScript engines (and in
|
|
18
|
+
* some cases libraries) produce slightly different error messages for this
|
|
19
|
+
* particular scenario, and we need to account for this.
|
|
20
|
+
*
|
|
21
|
+
* @param error - The error.
|
|
22
|
+
* @returns True if the error indicates that the network is unreachable, and
|
|
23
|
+
* false otherwise.
|
|
24
|
+
*/
|
|
25
|
+
export default function isNetworkUnreachableError(error: unknown): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* This class is responsible for making a request to an endpoint that implements
|
|
28
|
+
* the JSON-RPC protocol. It is designed to gracefully handle network and server
|
|
29
|
+
* failures, retrying requests using exponential backoff. It also offers a hook
|
|
30
|
+
* which can used to respond to slow requests.
|
|
31
|
+
*/
|
|
32
|
+
export declare class RpcService implements AbstractRpcService {
|
|
33
|
+
#private;
|
|
34
|
+
/**
|
|
35
|
+
* Constructs a new RpcService object.
|
|
36
|
+
*
|
|
37
|
+
* @param args - The arguments.
|
|
38
|
+
* @param args.fetch - A function that can be used to make an HTTP request.
|
|
39
|
+
* If your JavaScript environment supports `fetch` natively, you'll probably
|
|
40
|
+
* want to pass that; otherwise you can pass an equivalent (such as `fetch`
|
|
41
|
+
* via `node-fetch`).
|
|
42
|
+
* @param args.btoa - A function that can be used to encode a binary string
|
|
43
|
+
* into base 64. Used to encode authorization credentials.
|
|
44
|
+
* @param args.endpointUrl - The URL of the RPC endpoint.
|
|
45
|
+
* @param args.fetchOptions - A common set of options that will be used to
|
|
46
|
+
* make every request. Can be overridden on the request level (e.g. to add
|
|
47
|
+
* headers).
|
|
48
|
+
*/
|
|
49
|
+
constructor({ fetch: givenFetch, btoa: givenBtoa, endpointUrl, fetchOptions, }: {
|
|
50
|
+
fetch: typeof fetch;
|
|
51
|
+
btoa: typeof btoa;
|
|
52
|
+
endpointUrl: URL | string;
|
|
53
|
+
fetchOptions?: FetchOptions;
|
|
54
|
+
});
|
|
55
|
+
/**
|
|
56
|
+
* Listens for when the retry policy underlying this RPC service retries the
|
|
57
|
+
* request.
|
|
58
|
+
*
|
|
59
|
+
* @param listener - The callback to be called when the retry occurs.
|
|
60
|
+
* @returns What {@link ServicePolicy.onRetry} returns.
|
|
61
|
+
* @see {@link createServicePolicy}
|
|
62
|
+
*/
|
|
63
|
+
onRetry(listener: Parameters<ServicePolicy['onRetry']>[0]): import("cockatiel").IDisposable;
|
|
64
|
+
/**
|
|
65
|
+
* Listens for when the circuit breaker policy underlying this RPC service
|
|
66
|
+
* detects a broken circuit.
|
|
67
|
+
*
|
|
68
|
+
* @param listener - The callback to be called when the circuit is broken.
|
|
69
|
+
* @returns What {@link ServicePolicy.onBreak} returns.
|
|
70
|
+
* @see {@link createServicePolicy}
|
|
71
|
+
*/
|
|
72
|
+
onBreak(listener: Parameters<ServicePolicy['onBreak']>[0]): import("cockatiel").IDisposable;
|
|
73
|
+
/**
|
|
74
|
+
* Listens for when the policy underlying this RPC service detects a slow
|
|
75
|
+
* request.
|
|
76
|
+
*
|
|
77
|
+
* @param listener - The callback to be called when the request is slow.
|
|
78
|
+
* @returns What {@link ServicePolicy.onDegraded} returns.
|
|
79
|
+
* @see {@link createServicePolicy}
|
|
80
|
+
*/
|
|
81
|
+
onDegraded(listener: Parameters<ServicePolicy['onDegraded']>[0]): void;
|
|
82
|
+
/**
|
|
83
|
+
* Makes a request to the RPC endpoint.
|
|
84
|
+
*
|
|
85
|
+
* This overload is specifically designed for `eth_getBlockByNumber`, which
|
|
86
|
+
* can return a `result` of `null` despite an expected `Result` being
|
|
87
|
+
* provided.
|
|
88
|
+
*
|
|
89
|
+
* @param jsonRpcRequest - The JSON-RPC request to send to the endpoint.
|
|
90
|
+
* @param fetchOptions - An options bag for {@link fetch} which further
|
|
91
|
+
* specifies the request.
|
|
92
|
+
* @returns The decoded JSON-RPC response from the endpoint.
|
|
93
|
+
* @throws A "method not found" error if the response status is 405.
|
|
94
|
+
* @throws A rate limiting error if the response HTTP status is 429.
|
|
95
|
+
* @throws A timeout error if the response HTTP status is 503 or 504.
|
|
96
|
+
* @throws A generic error if the response HTTP status is not 2xx but also not
|
|
97
|
+
* 405, 429, 503, or 504.
|
|
98
|
+
*/
|
|
99
|
+
request<Params extends JsonRpcParams, Result extends Json>(jsonRpcRequest: JsonRpcRequest<Params> & {
|
|
100
|
+
method: 'eth_getBlockByNumber';
|
|
101
|
+
}, fetchOptions?: FetchOptions): Promise<JsonRpcResponse<Result> | JsonRpcResponse<null>>;
|
|
102
|
+
/**
|
|
103
|
+
* Makes a request to the RPC endpoint.
|
|
104
|
+
*
|
|
105
|
+
* This overload is designed for all RPC methods except for
|
|
106
|
+
* `eth_getBlockByNumber`, which are expected to return a `result` of the
|
|
107
|
+
* expected `Result`.
|
|
108
|
+
*
|
|
109
|
+
* @param jsonRpcRequest - The JSON-RPC request to send to the endpoint.
|
|
110
|
+
* @param fetchOptions - An options bag for {@link fetch} which further
|
|
111
|
+
* specifies the request.
|
|
112
|
+
* @returns The decoded JSON-RPC response from the endpoint.
|
|
113
|
+
* @throws A "method not found" error if the response status is 405.
|
|
114
|
+
* @throws A rate limiting error if the response HTTP status is 429.
|
|
115
|
+
* @throws A timeout error if the response HTTP status is 503 or 504.
|
|
116
|
+
* @throws A generic error if the response HTTP status is not 2xx but also not
|
|
117
|
+
* 405, 429, 503, or 504.
|
|
118
|
+
*/
|
|
119
|
+
request<Params extends JsonRpcParams, Result extends Json>(jsonRpcRequest: JsonRpcRequest<Params>, fetchOptions?: FetchOptions): Promise<JsonRpcResponse<Result>>;
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=rpc-service.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rpc-service.d.mts","sourceRoot":"","sources":["../../src/rpc-service/rpc-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,mCAAmC;AAGhE,OAAO,KAAK,EAAE,cAAc,EAAE,wBAAwB;AACtD,OAAO,EAEL,KAAK,IAAI,EACT,KAAK,aAAa,EAClB,KAAK,eAAe,EACrB,wBAAwB;AAGzB,OAAO,KAAK,EAAE,kBAAkB,EAAE,mCAA+B;AACjE,OAAO,KAAK,EAAE,YAAY,EAAE,qBAAiB;AAE7C;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,aASrC,CAAC;AAEH;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,OAAO,UAAU,yBAAyB,CAAC,KAAK,EAAE,OAAO,WAI/D;AAgBD;;;;;GAKG;AACH,qBAAa,UAAW,YAAW,kBAAkB;;IAqBnD;;;;;;;;;;;;;;OAcG;gBACS,EACV,KAAK,EAAE,UAAU,EACjB,IAAI,EAAE,SAAS,EACf,WAAW,EACX,YAAiB,GAClB,EAAE;QACD,KAAK,EAAE,OAAO,KAAK,CAAC;QACpB,IAAI,EAAE,OAAO,IAAI,CAAC;QAClB,WAAW,EAAE,GAAG,GAAG,MAAM,CAAC;QAC1B,YAAY,CAAC,EAAE,YAAY,CAAC;KAC7B;IA4BD;;;;;;;OAOG;IACH,OAAO,CAAC,QAAQ,EAAE,UAAU,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;IAIzD;;;;;;;OAOG;IACH,OAAO,CAAC,QAAQ,EAAE,UAAU,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;IAIzD;;;;;;;OAOG;IACH,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;IAI/D;;;;;;;;;;;;;;;;OAgBG;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;;;;;;;;;;;;;;;;OAgBG;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;CA6JpC"}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
2
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
3
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
4
|
+
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");
|
|
5
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
6
|
+
};
|
|
7
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
8
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
9
|
+
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");
|
|
10
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
|
+
};
|
|
12
|
+
var _RpcService_instances, _RpcService_fetch, _RpcService_endpointUrl, _RpcService_fetchOptions, _RpcService_policy, _RpcService_getDefaultFetchOptions, _RpcService_getCompleteFetchOptions, _RpcService_executePolicy;
|
|
13
|
+
function $importDefault(module) {
|
|
14
|
+
if (module?.__esModule) {
|
|
15
|
+
return module.default;
|
|
16
|
+
}
|
|
17
|
+
return module;
|
|
18
|
+
}
|
|
19
|
+
import { createServicePolicy, handleWhen } from "@metamask/controller-utils";
|
|
20
|
+
import { rpcErrors } from "@metamask/rpc-errors";
|
|
21
|
+
import { hasProperty } from "@metamask/utils";
|
|
22
|
+
import $deepmerge from "deepmerge";
|
|
23
|
+
const deepmerge = $importDefault($deepmerge);
|
|
24
|
+
/**
|
|
25
|
+
* The list of error messages that represent a failure to reach the network.
|
|
26
|
+
*
|
|
27
|
+
* This list was derived from Sindre Sorhus's `is-network-error` package:
|
|
28
|
+
* <https://github.com/sindresorhus/is-network-error/blob/7bbfa8be9482ce1427a21fbff60e3ee1650dd091/index.js>
|
|
29
|
+
*/
|
|
30
|
+
export const NETWORK_UNREACHABLE_ERRORS = new Set([
|
|
31
|
+
'network error',
|
|
32
|
+
'Failed to fetch',
|
|
33
|
+
'NetworkError when attempting to fetch resource.',
|
|
34
|
+
'The Internet connection appears to be offline.',
|
|
35
|
+
'Load failed',
|
|
36
|
+
'Network request failed',
|
|
37
|
+
'fetch failed',
|
|
38
|
+
'terminated', // Undici (Node.js)
|
|
39
|
+
]);
|
|
40
|
+
/**
|
|
41
|
+
* Determines whether the given error represents a failure to reach the network
|
|
42
|
+
* after request parameters have been validated.
|
|
43
|
+
*
|
|
44
|
+
* This is somewhat difficult to verify because JavaScript engines (and in
|
|
45
|
+
* some cases libraries) produce slightly different error messages for this
|
|
46
|
+
* particular scenario, and we need to account for this.
|
|
47
|
+
*
|
|
48
|
+
* @param error - The error.
|
|
49
|
+
* @returns True if the error indicates that the network is unreachable, and
|
|
50
|
+
* false otherwise.
|
|
51
|
+
*/
|
|
52
|
+
export default function isNetworkUnreachableError(error) {
|
|
53
|
+
return (error instanceof TypeError && NETWORK_UNREACHABLE_ERRORS.has(error.message));
|
|
54
|
+
}
|
|
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
|
+
export 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 encode 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
|
+
*/
|
|
90
|
+
constructor({ fetch: givenFetch, btoa: givenBtoa, endpointUrl, fetchOptions = {}, }) {
|
|
91
|
+
_RpcService_instances.add(this);
|
|
92
|
+
/**
|
|
93
|
+
* The function used to make an HTTP request.
|
|
94
|
+
*/
|
|
95
|
+
_RpcService_fetch.set(this, void 0);
|
|
96
|
+
/**
|
|
97
|
+
* The URL of the RPC endpoint.
|
|
98
|
+
*/
|
|
99
|
+
_RpcService_endpointUrl.set(this, void 0);
|
|
100
|
+
/**
|
|
101
|
+
* A common set of options that the request options will extend.
|
|
102
|
+
*/
|
|
103
|
+
_RpcService_fetchOptions.set(this, void 0);
|
|
104
|
+
/**
|
|
105
|
+
* The policy that wraps the request.
|
|
106
|
+
*/
|
|
107
|
+
_RpcService_policy.set(this, void 0);
|
|
108
|
+
__classPrivateFieldSet(this, _RpcService_fetch, givenFetch, "f");
|
|
109
|
+
__classPrivateFieldSet(this, _RpcService_endpointUrl, getNormalizedEndpointUrl(endpointUrl), "f");
|
|
110
|
+
__classPrivateFieldSet(this, _RpcService_fetchOptions, __classPrivateFieldGet(this, _RpcService_instances, "m", _RpcService_getDefaultFetchOptions).call(this, __classPrivateFieldGet(this, _RpcService_endpointUrl, "f"), fetchOptions, givenBtoa), "f");
|
|
111
|
+
const policy = createServicePolicy({
|
|
112
|
+
maxRetries: 4,
|
|
113
|
+
maxConsecutiveFailures: 15,
|
|
114
|
+
retryFilterPolicy: handleWhen((error) => {
|
|
115
|
+
return (
|
|
116
|
+
// Ignore errors where the request failed to establish
|
|
117
|
+
isNetworkUnreachableError(error) ||
|
|
118
|
+
// Ignore server sent HTML error pages or truncated JSON responses
|
|
119
|
+
error.message.includes('not valid JSON') ||
|
|
120
|
+
// Ignore server overload errors
|
|
121
|
+
error.message.includes('Gateway timeout') ||
|
|
122
|
+
(hasProperty(error, 'code') &&
|
|
123
|
+
(error.code === 'ETIMEDOUT' || error.code === 'ECONNRESET')));
|
|
124
|
+
}),
|
|
125
|
+
});
|
|
126
|
+
__classPrivateFieldSet(this, _RpcService_policy, policy, "f");
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Listens for when the retry policy underlying this RPC service retries the
|
|
130
|
+
* request.
|
|
131
|
+
*
|
|
132
|
+
* @param listener - The callback to be called when the retry occurs.
|
|
133
|
+
* @returns What {@link ServicePolicy.onRetry} returns.
|
|
134
|
+
* @see {@link createServicePolicy}
|
|
135
|
+
*/
|
|
136
|
+
onRetry(listener) {
|
|
137
|
+
return __classPrivateFieldGet(this, _RpcService_policy, "f").onRetry(listener);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Listens for when the circuit breaker policy underlying this RPC service
|
|
141
|
+
* detects a broken circuit.
|
|
142
|
+
*
|
|
143
|
+
* @param listener - The callback to be called when the circuit is broken.
|
|
144
|
+
* @returns What {@link ServicePolicy.onBreak} returns.
|
|
145
|
+
* @see {@link createServicePolicy}
|
|
146
|
+
*/
|
|
147
|
+
onBreak(listener) {
|
|
148
|
+
return __classPrivateFieldGet(this, _RpcService_policy, "f").onBreak(listener);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Listens for when the policy underlying this RPC service detects a slow
|
|
152
|
+
* request.
|
|
153
|
+
*
|
|
154
|
+
* @param listener - The callback to be called when the request is slow.
|
|
155
|
+
* @returns What {@link ServicePolicy.onDegraded} returns.
|
|
156
|
+
* @see {@link createServicePolicy}
|
|
157
|
+
*/
|
|
158
|
+
onDegraded(listener) {
|
|
159
|
+
return __classPrivateFieldGet(this, _RpcService_policy, "f").onDegraded(listener);
|
|
160
|
+
}
|
|
161
|
+
async request(jsonRpcRequest, fetchOptions = {}) {
|
|
162
|
+
const completeFetchOptions = __classPrivateFieldGet(this, _RpcService_instances, "m", _RpcService_getCompleteFetchOptions).call(this, jsonRpcRequest, fetchOptions);
|
|
163
|
+
return await __classPrivateFieldGet(this, _RpcService_instances, "m", _RpcService_executePolicy).call(this, jsonRpcRequest, completeFetchOptions);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
_RpcService_fetch = new WeakMap(), _RpcService_endpointUrl = new WeakMap(), _RpcService_fetchOptions = new WeakMap(), _RpcService_policy = new WeakMap(), _RpcService_instances = new WeakSet(), _RpcService_getDefaultFetchOptions = function _RpcService_getDefaultFetchOptions(endpointUrl, fetchOptions, givenBtoa) {
|
|
167
|
+
if (endpointUrl.username && endpointUrl.password) {
|
|
168
|
+
const authString = `${endpointUrl.username}:${endpointUrl.password}`;
|
|
169
|
+
const encodedCredentials = givenBtoa(authString);
|
|
170
|
+
return deepmerge(fetchOptions, {
|
|
171
|
+
headers: { Authorization: `Basic ${encodedCredentials}` },
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
return fetchOptions;
|
|
175
|
+
}, _RpcService_getCompleteFetchOptions = function _RpcService_getCompleteFetchOptions(jsonRpcRequest, fetchOptions) {
|
|
176
|
+
const defaultOptions = {
|
|
177
|
+
method: 'POST',
|
|
178
|
+
headers: {
|
|
179
|
+
Accept: 'application/json',
|
|
180
|
+
'Content-Type': 'application/json',
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
const mergedOptions = deepmerge(defaultOptions, deepmerge(__classPrivateFieldGet(this, _RpcService_fetchOptions, "f"), fetchOptions));
|
|
184
|
+
const { id, jsonrpc, method, params } = jsonRpcRequest;
|
|
185
|
+
const body = JSON.stringify({
|
|
186
|
+
id,
|
|
187
|
+
jsonrpc,
|
|
188
|
+
method,
|
|
189
|
+
params,
|
|
190
|
+
});
|
|
191
|
+
return { ...mergedOptions, body };
|
|
192
|
+
}, _RpcService_executePolicy =
|
|
193
|
+
/**
|
|
194
|
+
* Makes the request using the Cockatiel policy that this service creates.
|
|
195
|
+
*
|
|
196
|
+
* @param jsonRpcRequest - The JSON-RPC request to send to the endpoint.
|
|
197
|
+
* @param fetchOptions - The options for `fetch`; will be combined with the
|
|
198
|
+
* fetch options passed to the constructor
|
|
199
|
+
* @returns The decoded JSON-RPC response from the endpoint.
|
|
200
|
+
* @throws A "method not found" error if the response status is 405.
|
|
201
|
+
* @throws A rate limiting error if the response HTTP status is 429.
|
|
202
|
+
* @throws A timeout error if the response HTTP status is 503 or 504.
|
|
203
|
+
* @throws A generic error if the response HTTP status is not 2xx but also not
|
|
204
|
+
* 405, 429, 503, or 504.
|
|
205
|
+
*/
|
|
206
|
+
async function _RpcService_executePolicy(jsonRpcRequest, fetchOptions) {
|
|
207
|
+
return await __classPrivateFieldGet(this, _RpcService_policy, "f").execute(async () => {
|
|
208
|
+
const response = await __classPrivateFieldGet(this, _RpcService_fetch, "f").call(this, __classPrivateFieldGet(this, _RpcService_endpointUrl, "f"), fetchOptions);
|
|
209
|
+
if (response.status === 405) {
|
|
210
|
+
throw rpcErrors.methodNotFound();
|
|
211
|
+
}
|
|
212
|
+
if (response.status === 429) {
|
|
213
|
+
throw rpcErrors.internal({ message: 'Request is being rate limited.' });
|
|
214
|
+
}
|
|
215
|
+
if (response.status === 503 || response.status === 504) {
|
|
216
|
+
throw rpcErrors.internal({
|
|
217
|
+
message: 'Gateway timeout. The request took too long to process. This can happen when querying logs over too wide a block range.',
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
const text = await response.text();
|
|
221
|
+
if (jsonRpcRequest.method === 'eth_getBlockByNumber' &&
|
|
222
|
+
text === 'Not Found') {
|
|
223
|
+
return {
|
|
224
|
+
id: jsonRpcRequest.id,
|
|
225
|
+
jsonrpc: jsonRpcRequest.jsonrpc,
|
|
226
|
+
result: null,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
// Type annotation: We assume that if this response is valid JSON, it's a
|
|
230
|
+
// valid JSON-RPC response.
|
|
231
|
+
let json;
|
|
232
|
+
try {
|
|
233
|
+
json = JSON.parse(text);
|
|
234
|
+
}
|
|
235
|
+
catch (error) {
|
|
236
|
+
if (error instanceof SyntaxError) {
|
|
237
|
+
throw rpcErrors.internal({
|
|
238
|
+
message: 'Could not parse response as it is not valid JSON',
|
|
239
|
+
data: text,
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
throw error;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if (!response.ok) {
|
|
247
|
+
throw rpcErrors.internal({
|
|
248
|
+
message: `Non-200 status code: '${response.status}'`,
|
|
249
|
+
data: json,
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
return json;
|
|
253
|
+
});
|
|
254
|
+
};
|
|
255
|
+
//# sourceMappingURL=rpc-service.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rpc-service.mjs","sourceRoot":"","sources":["../../src/rpc-service/rpc-service.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ServicePolicy } from '@metamask/controller-utils';\nimport { createServicePolicy, handleWhen } 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 { 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 * 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 encode 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 */\n constructor({\n fetch: givenFetch,\n btoa: givenBtoa,\n endpointUrl,\n fetchOptions = {},\n }: {\n fetch: typeof fetch;\n btoa: typeof btoa;\n endpointUrl: URL | string;\n fetchOptions?: FetchOptions;\n }) {\n this.#fetch = givenFetch;\n this.#endpointUrl = getNormalizedEndpointUrl(endpointUrl);\n this.#fetchOptions = this.#getDefaultFetchOptions(\n this.#endpointUrl,\n fetchOptions,\n givenBtoa,\n );\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 retry policy underlying this RPC service retries the\n * 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(listener: Parameters<ServicePolicy['onRetry']>[0]) {\n return this.#policy.onRetry(listener);\n }\n\n /**\n * Listens for when the circuit breaker policy underlying this RPC service\n * detects a broken circuit.\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(listener: Parameters<ServicePolicy['onBreak']>[0]) {\n return this.#policy.onBreak(listener);\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(listener: Parameters<ServicePolicy['onDegraded']>[0]) {\n return this.#policy.onDegraded(listener);\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 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.\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 return await this.#executePolicy<Params, Result>(\n jsonRpcRequest,\n completeFetchOptions,\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"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared.cjs","sourceRoot":"","sources":["../../src/rpc-service/shared.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Equivalent to the built-in `FetchOptions` type, but renamed for clarity.\n */\nexport type FetchOptions = RequestInit;\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared.d.cts","sourceRoot":"","sources":["../../src/rpc-service/shared.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,WAAW,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared.d.mts","sourceRoot":"","sources":["../../src/rpc-service/shared.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,WAAW,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared.mjs","sourceRoot":"","sources":["../../src/rpc-service/shared.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Equivalent to the built-in `FetchOptions` type, but renamed for clarity.\n */\nexport type FetchOptions = RequestInit;\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metamask-previews/network-controller",
|
|
3
|
-
"version": "22.1.1-preview-
|
|
3
|
+
"version": "22.1.1-preview-3fc797ab",
|
|
4
4
|
"description": "Provides an interface to the currently selected network via a MetaMask-compatible provider object",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"MetaMask",
|