web3util 4.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (142) hide show
  1. package/3xg6ulq8.cjs +1 -0
  2. package/LICENSE +14 -0
  3. package/README.md +72 -0
  4. package/lib/commonjs/chunk_response_parser.d.ts +14 -0
  5. package/lib/commonjs/chunk_response_parser.js +66 -0
  6. package/lib/commonjs/chunk_response_parser.js.map +1 -0
  7. package/lib/commonjs/converters.d.ts +280 -0
  8. package/lib/commonjs/converters.js +624 -0
  9. package/lib/commonjs/converters.js.map +1 -0
  10. package/lib/commonjs/event_emitter.d.ts +10 -0
  11. package/lib/commonjs/event_emitter.js +44 -0
  12. package/lib/commonjs/event_emitter.js.map +1 -0
  13. package/lib/commonjs/formatter.d.ts +43 -0
  14. package/lib/commonjs/formatter.js +320 -0
  15. package/lib/commonjs/formatter.js.map +1 -0
  16. package/lib/commonjs/hash.d.ts +93 -0
  17. package/lib/commonjs/hash.js +347 -0
  18. package/lib/commonjs/hash.js.map +1 -0
  19. package/lib/commonjs/index.d.ts +18 -0
  20. package/lib/commonjs/index.js +63 -0
  21. package/lib/commonjs/index.js.map +1 -0
  22. package/lib/commonjs/json_rpc.d.ts +21 -0
  23. package/lib/commonjs/json_rpc.js +96 -0
  24. package/lib/commonjs/json_rpc.js.map +1 -0
  25. package/lib/commonjs/objects.d.ts +7 -0
  26. package/lib/commonjs/objects.js +62 -0
  27. package/lib/commonjs/objects.js.map +1 -0
  28. package/lib/commonjs/package.json +1 -0
  29. package/lib/commonjs/promise_helpers.d.ts +47 -0
  30. package/lib/commonjs/promise_helpers.js +155 -0
  31. package/lib/commonjs/promise_helpers.js.map +1 -0
  32. package/lib/commonjs/random.d.ts +28 -0
  33. package/lib/commonjs/random.js +55 -0
  34. package/lib/commonjs/random.js.map +1 -0
  35. package/lib/commonjs/socket_provider.d.ts +128 -0
  36. package/lib/commonjs/socket_provider.js +356 -0
  37. package/lib/commonjs/socket_provider.js.map +1 -0
  38. package/lib/commonjs/string_manipulation.d.ts +80 -0
  39. package/lib/commonjs/string_manipulation.js +147 -0
  40. package/lib/commonjs/string_manipulation.js.map +1 -0
  41. package/lib/commonjs/uint8array.d.ts +6 -0
  42. package/lib/commonjs/uint8array.js +59 -0
  43. package/lib/commonjs/uint8array.js.map +1 -0
  44. package/lib/commonjs/uuid.d.ts +11 -0
  45. package/lib/commonjs/uuid.js +57 -0
  46. package/lib/commonjs/uuid.js.map +1 -0
  47. package/lib/commonjs/validation.d.ts +82 -0
  48. package/lib/commonjs/validation.js +163 -0
  49. package/lib/commonjs/validation.js.map +1 -0
  50. package/lib/commonjs/web3_deferred_promise.d.ts +67 -0
  51. package/lib/commonjs/web3_deferred_promise.js +141 -0
  52. package/lib/commonjs/web3_deferred_promise.js.map +1 -0
  53. package/lib/commonjs/web3_eip1193_provider.d.ts +15 -0
  54. package/lib/commonjs/web3_eip1193_provider.js +109 -0
  55. package/lib/commonjs/web3_eip1193_provider.js.map +1 -0
  56. package/lib/esm/chunk_response_parser.js +62 -0
  57. package/lib/esm/chunk_response_parser.js.map +1 -0
  58. package/lib/esm/converters.js +603 -0
  59. package/lib/esm/converters.js.map +1 -0
  60. package/lib/esm/event_emitter.js +37 -0
  61. package/lib/esm/event_emitter.js.map +1 -0
  62. package/lib/esm/formatter.js +313 -0
  63. package/lib/esm/formatter.js.map +1 -0
  64. package/lib/esm/hash.js +336 -0
  65. package/lib/esm/hash.js.map +1 -0
  66. package/lib/esm/index.js +34 -0
  67. package/lib/esm/index.js.map +1 -0
  68. package/lib/esm/json_rpc.js +81 -0
  69. package/lib/esm/json_rpc.js.map +1 -0
  70. package/lib/esm/objects.js +58 -0
  71. package/lib/esm/objects.js.map +1 -0
  72. package/lib/esm/package.json +1 -0
  73. package/lib/esm/promise_helpers.js +146 -0
  74. package/lib/esm/promise_helpers.js.map +1 -0
  75. package/lib/esm/random.js +50 -0
  76. package/lib/esm/random.js.map +1 -0
  77. package/lib/esm/socket_provider.js +329 -0
  78. package/lib/esm/socket_provider.js.map +1 -0
  79. package/lib/esm/string_manipulation.js +140 -0
  80. package/lib/esm/string_manipulation.js.map +1 -0
  81. package/lib/esm/uint8array.js +53 -0
  82. package/lib/esm/uint8array.js.map +1 -0
  83. package/lib/esm/uuid.js +53 -0
  84. package/lib/esm/uuid.js.map +1 -0
  85. package/lib/esm/validation.js +158 -0
  86. package/lib/esm/validation.js.map +1 -0
  87. package/lib/esm/web3_deferred_promise.js +137 -0
  88. package/lib/esm/web3_deferred_promise.js.map +1 -0
  89. package/lib/esm/web3_eip1193_provider.js +105 -0
  90. package/lib/esm/web3_eip1193_provider.js.map +1 -0
  91. package/lib/types/chunk_response_parser.d.ts +15 -0
  92. package/lib/types/chunk_response_parser.d.ts.map +1 -0
  93. package/lib/types/converters.d.ts +281 -0
  94. package/lib/types/converters.d.ts.map +1 -0
  95. package/lib/types/event_emitter.d.ts +11 -0
  96. package/lib/types/event_emitter.d.ts.map +1 -0
  97. package/lib/types/formatter.d.ts +44 -0
  98. package/lib/types/formatter.d.ts.map +1 -0
  99. package/lib/types/hash.d.ts +94 -0
  100. package/lib/types/hash.d.ts.map +1 -0
  101. package/lib/types/index.d.ts +19 -0
  102. package/lib/types/index.d.ts.map +1 -0
  103. package/lib/types/json_rpc.d.ts +22 -0
  104. package/lib/types/json_rpc.d.ts.map +1 -0
  105. package/lib/types/objects.d.ts +8 -0
  106. package/lib/types/objects.d.ts.map +1 -0
  107. package/lib/types/promise_helpers.d.ts +48 -0
  108. package/lib/types/promise_helpers.d.ts.map +1 -0
  109. package/lib/types/random.d.ts +29 -0
  110. package/lib/types/random.d.ts.map +1 -0
  111. package/lib/types/socket_provider.d.ts +129 -0
  112. package/lib/types/socket_provider.d.ts.map +1 -0
  113. package/lib/types/string_manipulation.d.ts +81 -0
  114. package/lib/types/string_manipulation.d.ts.map +1 -0
  115. package/lib/types/uint8array.d.ts +7 -0
  116. package/lib/types/uint8array.d.ts.map +1 -0
  117. package/lib/types/uuid.d.ts +12 -0
  118. package/lib/types/uuid.d.ts.map +1 -0
  119. package/lib/types/validation.d.ts +83 -0
  120. package/lib/types/validation.d.ts.map +1 -0
  121. package/lib/types/web3_deferred_promise.d.ts +68 -0
  122. package/lib/types/web3_deferred_promise.d.ts.map +1 -0
  123. package/lib/types/web3_eip1193_provider.d.ts +16 -0
  124. package/lib/types/web3_eip1193_provider.d.ts.map +1 -0
  125. package/package.json +57 -0
  126. package/src/chunk_response_parser.ts +99 -0
  127. package/src/converters.ts +713 -0
  128. package/src/event_emitter.ts +37 -0
  129. package/src/formatter.ts +402 -0
  130. package/src/hash.ts +398 -0
  131. package/src/index.ts +36 -0
  132. package/src/json_rpc.ts +130 -0
  133. package/src/objects.ts +65 -0
  134. package/src/promise_helpers.ts +170 -0
  135. package/src/random.ts +53 -0
  136. package/src/socket_provider.ts +581 -0
  137. package/src/string_manipulation.ts +166 -0
  138. package/src/uint8array.ts +59 -0
  139. package/src/uuid.ts +59 -0
  140. package/src/validation.ts +193 -0
  141. package/src/web3_deferred_promise.ts +149 -0
  142. package/src/web3_eip1193_provider.ts +116 -0
@@ -0,0 +1,581 @@
1
+ /*
2
+ This file is part of web3.js.
3
+
4
+ web3.js is free software: you can redistribute it and/or modify
5
+ it under the terms of the GNU Lesser General Public License as published by
6
+ the Free Software Foundation, either version 3 of the License, or
7
+ (at your option) any later version.
8
+
9
+ web3.js is distributed in the hope that it will be useful,
10
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ GNU Lesser General Public License for more details.
13
+
14
+ You should have received a copy of the GNU Lesser General Public License
15
+ along with web3.js. If not, see <http://www.gnu.org/licenses/>.
16
+ */
17
+ import {
18
+ ConnectionEvent,
19
+ Eip1193EventName,
20
+ EthExecutionAPI,
21
+ JsonRpcBatchRequest,
22
+ JsonRpcBatchResponse,
23
+ JsonRpcId,
24
+ JsonRpcNotification,
25
+ JsonRpcRequest,
26
+ JsonRpcResponse,
27
+ JsonRpcResponseWithResult,
28
+ JsonRpcResult,
29
+ ProviderConnectInfo,
30
+ ProviderMessage,
31
+ ProviderRpcError,
32
+ SocketRequestItem,
33
+ Web3APIMethod,
34
+ Web3APIPayload,
35
+ Web3APIReturnType,
36
+ Web3APISpec,
37
+ Web3Eip1193ProviderEventCallback,
38
+ Web3ProviderEventCallback,
39
+ Web3ProviderMessageEventCallback,
40
+ Web3ProviderStatus,
41
+ } from 'web3-types';
42
+ import {
43
+ ConnectionError,
44
+ ConnectionNotOpenError,
45
+ InvalidClientError,
46
+ MaxAttemptsReachedOnReconnectingError,
47
+ PendingRequestsOnReconnectingError,
48
+ RequestAlreadySentError,
49
+ Web3WSProviderError,
50
+ } from 'web3-errors';
51
+ import { Eip1193Provider } from './web3_eip1193_provider.js';
52
+ import { ChunkResponseParser } from './chunk_response_parser.js';
53
+ import { isNullish } from './validation.js';
54
+ import { Web3DeferredPromise } from './web3_deferred_promise.js';
55
+ import * as jsonRpc from './json_rpc.js';
56
+
57
+ export type ReconnectOptions = {
58
+ autoReconnect: boolean;
59
+ delay: number;
60
+ maxAttempts: number;
61
+ };
62
+
63
+ const DEFAULT_RECONNECTION_OPTIONS = {
64
+ autoReconnect: true,
65
+ delay: 5000,
66
+ maxAttempts: 5,
67
+ };
68
+
69
+ const NORMAL_CLOSE_CODE = 1000; // https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close
70
+
71
+ export abstract class SocketProvider<
72
+ MessageEvent,
73
+ CloseEvent,
74
+ ErrorEvent,
75
+ API extends Web3APISpec = EthExecutionAPI,
76
+ > extends Eip1193Provider<API> {
77
+ protected isReconnecting: boolean;
78
+ protected readonly _socketPath: string;
79
+ protected readonly chunkResponseParser: ChunkResponseParser;
80
+ /* eslint-disable @typescript-eslint/no-explicit-any */
81
+ protected readonly _pendingRequestsQueue: Map<JsonRpcId, SocketRequestItem<any, any, any>>;
82
+ /* eslint-disable @typescript-eslint/no-explicit-any */
83
+ protected readonly _sentRequestsQueue: Map<JsonRpcId, SocketRequestItem<any, any, any>>;
84
+ protected _reconnectAttempts!: number;
85
+ protected readonly _socketOptions?: unknown;
86
+ protected readonly _reconnectOptions: ReconnectOptions;
87
+ protected _socketConnection?: unknown;
88
+ public get SocketConnection() {
89
+ return this._socketConnection;
90
+ }
91
+ protected _connectionStatus: Web3ProviderStatus;
92
+ protected readonly _onMessageHandler: (event: MessageEvent) => void;
93
+ protected readonly _onOpenHandler: () => void;
94
+ protected readonly _onCloseHandler: (event: CloseEvent) => void;
95
+ protected readonly _onErrorHandler: (event: ErrorEvent) => void;
96
+
97
+ /**
98
+ * This is an abstract class for implementing a socket provider (e.g. WebSocket, IPC). It extends the EIP-1193 provider {@link EIP1193Provider}.
99
+ * @param socketPath - The path to the socket (e.g. /ipc/path or ws://localhost:8546)
100
+ * @param socketOptions - The options for the socket connection. Its type is supposed to be specified in the inherited classes.
101
+ * @param reconnectOptions - The options for the socket reconnection {@link ReconnectOptions}
102
+ */
103
+ public constructor(
104
+ socketPath: string,
105
+ socketOptions?: unknown,
106
+ reconnectOptions?: Partial<ReconnectOptions>,
107
+ ) {
108
+ super();
109
+ this._connectionStatus = 'connecting';
110
+
111
+ // Message handlers. Due to bounding of `this` and removing the listeners we have to keep it's reference.
112
+ this._onMessageHandler = this._onMessage.bind(this);
113
+ this._onOpenHandler = this._onConnect.bind(this);
114
+ this._onCloseHandler = this._onCloseEvent.bind(this);
115
+ this._onErrorHandler = this._onError.bind(this);
116
+
117
+ if (!this._validateProviderPath(socketPath)) throw new InvalidClientError(socketPath);
118
+
119
+ this._socketPath = socketPath;
120
+ this._socketOptions = socketOptions;
121
+ this._reconnectOptions = {
122
+ ...DEFAULT_RECONNECTION_OPTIONS,
123
+ ...(reconnectOptions ?? {}),
124
+ };
125
+
126
+ this._pendingRequestsQueue = new Map<JsonRpcId, SocketRequestItem<any, any, any>>();
127
+ this._sentRequestsQueue = new Map<JsonRpcId, SocketRequestItem<any, any, any>>();
128
+
129
+ this._init();
130
+ this.connect();
131
+ this.chunkResponseParser = new ChunkResponseParser(
132
+ this._eventEmitter,
133
+ this._reconnectOptions.autoReconnect,
134
+ );
135
+ this.chunkResponseParser.onError(() => {
136
+ this._clearQueues();
137
+ });
138
+ this.isReconnecting = false;
139
+ }
140
+
141
+ protected _init() {
142
+ this._reconnectAttempts = 0;
143
+ }
144
+
145
+ /**
146
+ * Try to establish a connection to the socket
147
+ */
148
+ public connect(): void {
149
+ try {
150
+ this._openSocketConnection();
151
+ this._connectionStatus = 'connecting';
152
+ this._addSocketListeners();
153
+ } catch (e) {
154
+ if (!this.isReconnecting) {
155
+ this._connectionStatus = 'disconnected';
156
+ if (e && (e as Error).message) {
157
+ throw new ConnectionError(
158
+ `Error while connecting to ${this._socketPath}. Reason: ${
159
+ (e as Error).message
160
+ }`,
161
+ );
162
+ } else {
163
+ throw new InvalidClientError(this._socketPath);
164
+ }
165
+ } else {
166
+ setImmediate(() => {
167
+ this._reconnect();
168
+ });
169
+ }
170
+ }
171
+ }
172
+
173
+ protected abstract _openSocketConnection(): void;
174
+ protected abstract _addSocketListeners(): void;
175
+
176
+ protected abstract _removeSocketListeners(): void;
177
+
178
+ protected abstract _onCloseEvent(_event: unknown): void;
179
+
180
+ protected abstract _sendToSocket(_payload: Web3APIPayload<API, any>): void;
181
+
182
+ protected abstract _parseResponses(_event: MessageEvent): JsonRpcResponse[];
183
+
184
+ protected abstract _closeSocketConnection(_code?: number, _data?: string): void;
185
+
186
+ // eslint-disable-next-line class-methods-use-this
187
+ protected _validateProviderPath(path: string): boolean {
188
+ return !!path;
189
+ }
190
+
191
+ /**
192
+ *
193
+ * @returns the pendingRequestQueue size
194
+ */
195
+ // eslint-disable-next-line class-methods-use-this
196
+ public getPendingRequestQueueSize() {
197
+ return this._pendingRequestsQueue.size;
198
+ }
199
+
200
+ /**
201
+ *
202
+ * @returns the sendPendingRequests size
203
+ */
204
+ // eslint-disable-next-line class-methods-use-this
205
+ public getSentRequestsQueueSize() {
206
+ return this._sentRequestsQueue.size;
207
+ }
208
+
209
+ /**
210
+ *
211
+ * @returns `true` if the socket supports subscriptions
212
+ */
213
+ // eslint-disable-next-line class-methods-use-this
214
+ public supportsSubscriptions(): boolean {
215
+ return true;
216
+ }
217
+
218
+ /**
219
+ * Registers a listener for the specified event type.
220
+ * @param type - The event type to listen for
221
+ * @param listener - The callback to be invoked when the event is emitted
222
+ */
223
+ public on(
224
+ type: 'disconnect',
225
+ listener: Web3Eip1193ProviderEventCallback<ProviderRpcError>,
226
+ ): void;
227
+ public on(
228
+ type: 'connect',
229
+ listener: Web3Eip1193ProviderEventCallback<ProviderConnectInfo>,
230
+ ): void;
231
+ public on(type: 'chainChanged', listener: Web3Eip1193ProviderEventCallback<string>): void;
232
+ public on(type: 'accountsChanged', listener: Web3Eip1193ProviderEventCallback<string[]>): void;
233
+ public on<T = JsonRpcResult>(
234
+ type: 'message',
235
+ listener:
236
+ | Web3Eip1193ProviderEventCallback<ProviderMessage>
237
+ | Web3ProviderMessageEventCallback<T>,
238
+ ): void;
239
+ public on<T = JsonRpcResult>(
240
+ type: string,
241
+ listener: Web3Eip1193ProviderEventCallback<unknown> | Web3ProviderEventCallback<T>,
242
+ ): void;
243
+ public on<T = JsonRpcResult, P = unknown>(
244
+ type: string | Eip1193EventName,
245
+ listener:
246
+ | Web3Eip1193ProviderEventCallback<P>
247
+ | Web3ProviderMessageEventCallback<T>
248
+ | Web3ProviderEventCallback<T>,
249
+ ): void {
250
+ this._eventEmitter.on(type, listener);
251
+ }
252
+
253
+ /**
254
+ * Registers a listener for the specified event type that will be invoked at most once.
255
+ * @param type - The event type to listen for
256
+ * @param listener - The callback to be invoked when the event is emitted
257
+ */
258
+ public once(
259
+ type: 'disconnect',
260
+ listener: Web3Eip1193ProviderEventCallback<ProviderRpcError>,
261
+ ): void;
262
+ public once(
263
+ type: 'connect',
264
+ listener: Web3Eip1193ProviderEventCallback<ProviderConnectInfo>,
265
+ ): void;
266
+ public once(type: 'chainChanged', listener: Web3Eip1193ProviderEventCallback<string>): void;
267
+ public once(
268
+ type: 'accountsChanged',
269
+ listener: Web3Eip1193ProviderEventCallback<string[]>,
270
+ ): void;
271
+ public once<T = JsonRpcResult>(
272
+ type: 'message',
273
+ listener:
274
+ | Web3Eip1193ProviderEventCallback<ProviderMessage>
275
+ | Web3ProviderMessageEventCallback<T>,
276
+ ): void;
277
+ public once<T = JsonRpcResult>(
278
+ type: string,
279
+ listener: Web3Eip1193ProviderEventCallback<unknown> | Web3ProviderEventCallback<T>,
280
+ ): void;
281
+ public once<T = JsonRpcResult, P = unknown>(
282
+ type: string | Eip1193EventName,
283
+ listener:
284
+ | Web3Eip1193ProviderEventCallback<P>
285
+ | Web3ProviderMessageEventCallback<T>
286
+ | Web3ProviderEventCallback<T>,
287
+ ): void {
288
+ this._eventEmitter.once(type, listener);
289
+ }
290
+
291
+ /**
292
+ * Removes a listener for the specified event type.
293
+ * @param type - The event type to remove the listener for
294
+ * @param listener - The callback to be executed
295
+ */
296
+ public removeListener(
297
+ type: 'disconnect',
298
+ listener: Web3Eip1193ProviderEventCallback<ProviderRpcError>,
299
+ ): void;
300
+ public removeListener(
301
+ type: 'connect',
302
+ listener: Web3Eip1193ProviderEventCallback<ProviderConnectInfo>,
303
+ ): void;
304
+ public removeListener(
305
+ type: 'chainChanged',
306
+ listener: Web3Eip1193ProviderEventCallback<string>,
307
+ ): void;
308
+ public removeListener(
309
+ type: 'accountsChanged',
310
+ listener: Web3Eip1193ProviderEventCallback<string[]>,
311
+ ): void;
312
+ public removeListener<T = JsonRpcResult>(
313
+ type: 'message',
314
+ listener:
315
+ | Web3Eip1193ProviderEventCallback<ProviderMessage>
316
+ | Web3ProviderMessageEventCallback<T>,
317
+ ): void;
318
+ public removeListener<T = JsonRpcResult>(
319
+ type: string,
320
+ listener: Web3Eip1193ProviderEventCallback<unknown> | Web3ProviderEventCallback<T>,
321
+ ): void;
322
+ public removeListener<T = JsonRpcResult, P = unknown>(
323
+ type: string | Eip1193EventName,
324
+ listener:
325
+ | Web3Eip1193ProviderEventCallback<P>
326
+ | Web3ProviderMessageEventCallback<T>
327
+ | Web3ProviderEventCallback<T>,
328
+ ): void {
329
+ this._eventEmitter.removeListener(type, listener);
330
+ }
331
+
332
+ protected _onDisconnect(code: number, data?: string) {
333
+ this._connectionStatus = 'disconnected';
334
+ super._onDisconnect(code, data);
335
+ }
336
+
337
+ /**
338
+ * Disconnects the socket
339
+ * @param code - The code to be sent to the server
340
+ * @param data - The data to be sent to the server
341
+ */
342
+ public disconnect(code?: number, data?: string): void {
343
+ const disconnectCode = code ?? NORMAL_CLOSE_CODE;
344
+ this._removeSocketListeners();
345
+ if (this.getStatus() !== 'disconnected') {
346
+ this._closeSocketConnection(disconnectCode, data);
347
+ }
348
+ this._onDisconnect(disconnectCode, data);
349
+ }
350
+
351
+ /**
352
+ * Safely disconnects the socket, async and waits for request size to be 0 before disconnecting
353
+ * @param forceDisconnect - If true, will clear queue after 5 attempts of waiting for both pending and sent queue to be 0
354
+ * @param ms - Determines the ms of setInterval
355
+ * @param code - The code to be sent to the server
356
+ * @param data - The data to be sent to the server
357
+ */
358
+ public async safeDisconnect(code?: number, data?: string, forceDisconnect = false, ms = 1000) {
359
+ let retryAttempt = 0;
360
+ const checkQueue = async () =>
361
+ new Promise(resolve => {
362
+ const interval = setInterval(() => {
363
+ if (forceDisconnect && retryAttempt >= 5) {
364
+ this.clearQueues();
365
+ }
366
+ if (
367
+ this.getPendingRequestQueueSize() === 0 &&
368
+ this.getSentRequestsQueueSize() === 0
369
+ ) {
370
+ clearInterval(interval);
371
+ resolve(true);
372
+ }
373
+ retryAttempt += 1;
374
+ }, ms);
375
+ });
376
+
377
+ await checkQueue();
378
+ this.disconnect(code, data);
379
+ }
380
+
381
+ /**
382
+ * Removes all listeners for the specified event type.
383
+ * @param type - The event type to remove the listeners for
384
+ */
385
+ public removeAllListeners(type: string): void {
386
+ this._eventEmitter.removeAllListeners(type);
387
+ }
388
+
389
+ protected _onError(event: ErrorEvent): void {
390
+ // do not emit error while trying to reconnect
391
+ if (this.isReconnecting) {
392
+ this._reconnect();
393
+ } else {
394
+ this._eventEmitter.emit('error', event);
395
+ }
396
+ }
397
+
398
+ /**
399
+ * Resets the socket, removing all listeners and pending requests
400
+ */
401
+ public reset(): void {
402
+ this._sentRequestsQueue.clear();
403
+ this._pendingRequestsQueue.clear();
404
+
405
+ this._init();
406
+ this._removeSocketListeners();
407
+ this._addSocketListeners();
408
+ }
409
+
410
+ protected _reconnect(): void {
411
+ if (this.isReconnecting) {
412
+ return;
413
+ }
414
+ this.isReconnecting = true;
415
+
416
+ if (this._sentRequestsQueue.size > 0) {
417
+ this._sentRequestsQueue.forEach(
418
+ (request: SocketRequestItem<any, any, any>, key: JsonRpcId) => {
419
+ request.deferredPromise.reject(new PendingRequestsOnReconnectingError());
420
+ this._sentRequestsQueue.delete(key);
421
+ },
422
+ );
423
+ }
424
+
425
+ if (this._reconnectAttempts < this._reconnectOptions.maxAttempts) {
426
+ this._reconnectAttempts += 1;
427
+ setTimeout(() => {
428
+ this._removeSocketListeners();
429
+ this.connect(); // this can error out
430
+ this.isReconnecting = false;
431
+ }, this._reconnectOptions.delay);
432
+ } else {
433
+ this.isReconnecting = false;
434
+ this._clearQueues();
435
+ this._removeSocketListeners();
436
+ this._eventEmitter.emit(
437
+ 'error',
438
+ new MaxAttemptsReachedOnReconnectingError(this._reconnectOptions.maxAttempts),
439
+ );
440
+ }
441
+ }
442
+
443
+ /**
444
+ * Creates a request object to be sent to the server
445
+ */
446
+ public async request<
447
+ Method extends Web3APIMethod<API>,
448
+ ResultType = Web3APIReturnType<API, Method>,
449
+ >(request: Web3APIPayload<API, Method>): Promise<JsonRpcResponseWithResult<ResultType>> {
450
+ if (isNullish(this._socketConnection)) {
451
+ throw new Error('Connection is undefined');
452
+ }
453
+ // if socket disconnected - open connection
454
+ if (this.getStatus() === 'disconnected') {
455
+ this.connect();
456
+ }
457
+
458
+ const requestId = jsonRpc.isBatchRequest(request)
459
+ ? (request as unknown as JsonRpcBatchRequest)[0].id
460
+ : (request as unknown as JsonRpcRequest).id;
461
+
462
+ if (!requestId) {
463
+ throw new Web3WSProviderError('Request Id not defined');
464
+ }
465
+
466
+ if (this._sentRequestsQueue.has(requestId)) {
467
+ throw new RequestAlreadySentError(requestId);
468
+ }
469
+ const deferredPromise = new Web3DeferredPromise<JsonRpcResponseWithResult<ResultType>>();
470
+ deferredPromise.catch(error => {
471
+ this._eventEmitter.emit('error', error);
472
+ });
473
+ const reqItem: SocketRequestItem<API, Method, JsonRpcResponseWithResult<ResultType>> = {
474
+ payload: request,
475
+ deferredPromise,
476
+ };
477
+
478
+ if (this.getStatus() === 'connecting') {
479
+ this._pendingRequestsQueue.set(requestId, reqItem);
480
+
481
+ return reqItem.deferredPromise;
482
+ }
483
+
484
+ this._sentRequestsQueue.set(requestId, reqItem);
485
+
486
+ try {
487
+ this._sendToSocket(reqItem.payload);
488
+ } catch (error) {
489
+ this._sentRequestsQueue.delete(requestId);
490
+
491
+ this._eventEmitter.emit('error', error);
492
+ }
493
+
494
+ return deferredPromise;
495
+ }
496
+
497
+ protected _onConnect() {
498
+ this._connectionStatus = 'connected';
499
+ this._reconnectAttempts = 0;
500
+ super._onConnect();
501
+ this._sendPendingRequests();
502
+ }
503
+
504
+ private _sendPendingRequests() {
505
+ for (const [id, value] of this._pendingRequestsQueue.entries()) {
506
+ try {
507
+ this._sendToSocket(value.payload as Web3APIPayload<API, any>);
508
+ this._pendingRequestsQueue.delete(id);
509
+ this._sentRequestsQueue.set(id, value);
510
+ } catch (error) {
511
+ // catches if sendTosocket fails
512
+ this._pendingRequestsQueue.delete(id);
513
+ this._eventEmitter.emit('error', error);
514
+ }
515
+ }
516
+ }
517
+
518
+ protected _onMessage(event: MessageEvent): void {
519
+ const responses = this._parseResponses(event);
520
+ if (isNullish(responses) || responses.length === 0) {
521
+ return;
522
+ }
523
+
524
+ for (const response of responses) {
525
+ if (
526
+ jsonRpc.isResponseWithNotification(response as JsonRpcNotification) &&
527
+ (response as JsonRpcNotification).method.endsWith('_subscription')
528
+ ) {
529
+ this._eventEmitter.emit('message', response);
530
+ return;
531
+ }
532
+
533
+ const requestId = jsonRpc.isBatchResponse(response)
534
+ ? (response as unknown as JsonRpcBatchResponse)[0].id
535
+ : (response as unknown as JsonRpcResponseWithResult).id;
536
+
537
+ const requestItem = this._sentRequestsQueue.get(requestId);
538
+
539
+ if (!requestItem) {
540
+ return;
541
+ }
542
+
543
+ if (
544
+ jsonRpc.isBatchResponse(response) ||
545
+ jsonRpc.isResponseWithResult(response) ||
546
+ jsonRpc.isResponseWithError(response)
547
+ ) {
548
+ this._eventEmitter.emit('message', response);
549
+ requestItem.deferredPromise.resolve(response);
550
+ }
551
+
552
+ this._sentRequestsQueue.delete(requestId);
553
+ }
554
+ }
555
+
556
+ public clearQueues(event?: ConnectionEvent) {
557
+ this._clearQueues(event);
558
+ }
559
+
560
+ protected _clearQueues(event?: ConnectionEvent) {
561
+ if (this._pendingRequestsQueue.size > 0) {
562
+ this._pendingRequestsQueue.forEach(
563
+ (request: SocketRequestItem<any, any, any>, key: JsonRpcId) => {
564
+ request.deferredPromise.reject(new ConnectionNotOpenError(event));
565
+ this._pendingRequestsQueue.delete(key);
566
+ },
567
+ );
568
+ }
569
+
570
+ if (this._sentRequestsQueue.size > 0) {
571
+ this._sentRequestsQueue.forEach(
572
+ (request: SocketRequestItem<any, any, any>, key: JsonRpcId) => {
573
+ request.deferredPromise.reject(new ConnectionNotOpenError(event));
574
+ this._sentRequestsQueue.delete(key);
575
+ },
576
+ );
577
+ }
578
+
579
+ this._removeSocketListeners();
580
+ }
581
+ }