viem 2.9.11 → 2.9.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/_cjs/actions/public/watchContractEvent.js +8 -2
  3. package/_cjs/actions/public/watchContractEvent.js.map +1 -1
  4. package/_cjs/actions/public/watchEvent.js +8 -2
  5. package/_cjs/actions/public/watchEvent.js.map +1 -1
  6. package/_cjs/clients/transports/ipc.js +2 -2
  7. package/_cjs/clients/transports/ipc.js.map +1 -1
  8. package/_cjs/clients/transports/webSocket.js +2 -2
  9. package/_cjs/clients/transports/webSocket.js.map +1 -1
  10. package/_cjs/errors/version.js +1 -1
  11. package/_cjs/utils/abi/decodeEventLog.js +1 -1
  12. package/_cjs/utils/abi/decodeEventLog.js.map +1 -1
  13. package/_cjs/utils/rpc/ipc.js +8 -2
  14. package/_cjs/utils/rpc/ipc.js.map +1 -1
  15. package/_cjs/utils/rpc/socket.js +46 -15
  16. package/_cjs/utils/rpc/socket.js.map +1 -1
  17. package/_cjs/utils/rpc/webSocket.js +8 -2
  18. package/_cjs/utils/rpc/webSocket.js.map +1 -1
  19. package/_esm/actions/public/watchContractEvent.js +8 -2
  20. package/_esm/actions/public/watchContractEvent.js.map +1 -1
  21. package/_esm/actions/public/watchEvent.js +8 -2
  22. package/_esm/actions/public/watchEvent.js.map +1 -1
  23. package/_esm/clients/transports/ipc.js +3 -3
  24. package/_esm/clients/transports/ipc.js.map +1 -1
  25. package/_esm/clients/transports/webSocket.js +3 -3
  26. package/_esm/clients/transports/webSocket.js.map +1 -1
  27. package/_esm/errors/version.js +1 -1
  28. package/_esm/utils/abi/decodeEventLog.js +1 -1
  29. package/_esm/utils/abi/decodeEventLog.js.map +1 -1
  30. package/_esm/utils/rpc/ipc.js +8 -2
  31. package/_esm/utils/rpc/ipc.js.map +1 -1
  32. package/_esm/utils/rpc/socket.js +49 -16
  33. package/_esm/utils/rpc/socket.js.map +1 -1
  34. package/_esm/utils/rpc/webSocket.js +8 -2
  35. package/_esm/utils/rpc/webSocket.js.map +1 -1
  36. package/_types/actions/public/watchContractEvent.d.ts +3 -0
  37. package/_types/actions/public/watchContractEvent.d.ts.map +1 -1
  38. package/_types/actions/public/watchEvent.d.ts +4 -1
  39. package/_types/actions/public/watchEvent.d.ts.map +1 -1
  40. package/_types/clients/transports/ipc.d.ts +6 -1
  41. package/_types/clients/transports/ipc.d.ts.map +1 -1
  42. package/_types/clients/transports/webSocket.d.ts +6 -0
  43. package/_types/clients/transports/webSocket.d.ts.map +1 -1
  44. package/_types/errors/version.d.ts +1 -1
  45. package/_types/utils/rpc/compat.d.ts +8 -2
  46. package/_types/utils/rpc/compat.d.ts.map +1 -1
  47. package/_types/utils/rpc/ipc.d.ts +3 -2
  48. package/_types/utils/rpc/ipc.d.ts.map +1 -1
  49. package/_types/utils/rpc/socket.d.ts +25 -4
  50. package/_types/utils/rpc/socket.d.ts.map +1 -1
  51. package/_types/utils/rpc/webSocket.d.ts +3 -2
  52. package/_types/utils/rpc/webSocket.d.ts.map +1 -1
  53. package/actions/public/watchContractEvent.ts +10 -1
  54. package/actions/public/watchEvent.ts +10 -1
  55. package/clients/transports/ipc.ts +12 -3
  56. package/clients/transports/webSocket.ts +16 -3
  57. package/errors/version.ts +1 -1
  58. package/package.json +1 -1
  59. package/utils/abi/decodeEventLog.ts +1 -1
  60. package/utils/rpc/ipc.ts +18 -2
  61. package/utils/rpc/socket.ts +78 -19
  62. package/utils/rpc/webSocket.ts +15 -1
@@ -12,6 +12,7 @@ import {
12
12
  } from '../../errors/abi.js'
13
13
  import { InvalidInputRpcError } from '../../errors/rpc.js'
14
14
  import type { ErrorType } from '../../errors/utils.js'
15
+ import type { BlockNumber } from '../../types/block.js'
15
16
  import type {
16
17
  ContractEventArgs,
17
18
  ContractEventName,
@@ -73,6 +74,8 @@ export type WatchContractEventParameters<
73
74
  | undefined
74
75
  /** Contract event. */
75
76
  eventName?: eventName | ContractEventName<abi> | undefined
77
+ /** Block to start listening from. */
78
+ fromBlock?: BlockNumber<bigint> | undefined
76
79
  /** The callback to call when an error occurred when trying to get for a new block. */
77
80
  onError?: ((error: Error) => void) | undefined
78
81
  /** The callback to call when new event logs are received. */
@@ -143,6 +146,7 @@ export function watchContractEvent<
143
146
  args,
144
147
  batch = true,
145
148
  eventName,
149
+ fromBlock,
146
150
  onError,
147
151
  onLogs,
148
152
  poll: poll_,
@@ -151,7 +155,9 @@ export function watchContractEvent<
151
155
  } = parameters
152
156
 
153
157
  const enablePolling =
154
- typeof poll_ !== 'undefined' ? poll_ : client.transport.type !== 'webSocket'
158
+ typeof poll_ !== 'undefined'
159
+ ? poll_
160
+ : client.transport.type !== 'webSocket' || typeof fromBlock === 'number'
155
161
 
156
162
  const pollContractEvent = () => {
157
163
  const strict = strict_ ?? false
@@ -164,10 +170,12 @@ export function watchContractEvent<
164
170
  eventName,
165
171
  pollingInterval,
166
172
  strict,
173
+ fromBlock,
167
174
  ])
168
175
 
169
176
  return observe(observerId, { onLogs, onError }, (emit) => {
170
177
  let previousBlockNumber: bigint
178
+ if (fromBlock !== undefined) previousBlockNumber = fromBlock - 1n
171
179
  let filter: Filter<'event', abi, eventName> | undefined
172
180
  let initialized = false
173
181
 
@@ -185,6 +193,7 @@ export function watchContractEvent<
185
193
  args: args as any,
186
194
  eventName: eventName as any,
187
195
  strict: strict as any,
196
+ fromBlock,
188
197
  })) as Filter<'event', abi, eventName>
189
198
  } catch {}
190
199
  initialized = true
@@ -22,6 +22,7 @@ import {
22
22
  } from '../../errors/abi.js'
23
23
  import { InvalidInputRpcError } from '../../errors/rpc.js'
24
24
  import type { ErrorType } from '../../errors/utils.js'
25
+ import type { BlockNumber } from '../../types/block.js'
25
26
  import { getAction } from '../../utils/getAction.js'
26
27
  import {
27
28
  decodeEventLog,
@@ -70,6 +71,8 @@ export type WatchEventParameters<
70
71
  > = {
71
72
  /** The address of the contract. */
72
73
  address?: Address | Address[] | undefined
74
+ /** Block to start listening from. */
75
+ fromBlock?: BlockNumber<bigint> | undefined
73
76
  /** The callback to call when an error occurred when trying to get for a new block. */
74
77
  onError?: ((error: Error) => void) | undefined
75
78
  /** The callback to call when new event logs are received. */
@@ -161,6 +164,7 @@ export function watchEvent<
161
164
  batch = true,
162
165
  event,
163
166
  events,
167
+ fromBlock,
164
168
  onError,
165
169
  onLogs,
166
170
  poll: poll_,
@@ -169,7 +173,9 @@ export function watchEvent<
169
173
  }: WatchEventParameters<TAbiEvent, TAbiEvents, TStrict, TTransport>,
170
174
  ): WatchEventReturnType {
171
175
  const enablePolling =
172
- typeof poll_ !== 'undefined' ? poll_ : client.transport.type !== 'webSocket'
176
+ typeof poll_ !== 'undefined'
177
+ ? poll_
178
+ : client.transport.type !== 'webSocket' || typeof fromBlock === 'bigint'
173
179
  const strict = strict_ ?? false
174
180
 
175
181
  const pollEvent = () => {
@@ -181,10 +187,12 @@ export function watchEvent<
181
187
  client.uid,
182
188
  event,
183
189
  pollingInterval,
190
+ fromBlock,
184
191
  ])
185
192
 
186
193
  return observe(observerId, { onLogs, onError }, (emit) => {
187
194
  let previousBlockNumber: bigint
195
+ if (fromBlock !== undefined) previousBlockNumber = fromBlock - 1n
188
196
  let filter: Filter<'event', TAbiEvents, _EventName, any>
189
197
  let initialized = false
190
198
 
@@ -202,6 +210,7 @@ export function watchEvent<
202
210
  event: event!,
203
211
  events,
204
212
  strict,
213
+ fromBlock,
205
214
  } as unknown as CreateEventFilterParameters)) as unknown as Filter<
206
215
  'event',
207
216
  TAbiEvents,
@@ -3,7 +3,11 @@ import { type UrlRequiredErrorType } from '../../errors/transport.js'
3
3
  import type { ErrorType } from '../../errors/utils.js'
4
4
  import type { Hash } from '../../types/misc.js'
5
5
  import type { RpcResponse } from '../../types/rpc.js'
6
- import { type IpcRpcClient, getIpcRpcClient } from '../../utils/rpc/ipc.js'
6
+ import {
7
+ type GetIpcRpcClientOptions,
8
+ type IpcRpcClient,
9
+ getIpcRpcClient,
10
+ } from '../../utils/rpc/ipc.js'
7
11
  import {
8
12
  type CreateTransportErrorType,
9
13
  type Transport,
@@ -38,6 +42,11 @@ export type IpcTransportConfig = {
38
42
  key?: TransportConfig['key'] | undefined
39
43
  /** The name of the Ipc transport. */
40
44
  name?: TransportConfig['name'] | undefined
45
+ /**
46
+ * Whether or not to attempt to reconnect on socket failure.
47
+ * @default true
48
+ */
49
+ reconnect?: GetIpcRpcClientOptions['reconnect'] | undefined
41
50
  /** The max number of times to retry. */
42
51
  retryCount?: TransportConfig['retryCount'] | undefined
43
52
  /** The base delay (in ms) between retries. */
@@ -66,7 +75,7 @@ export function ipc(
66
75
  path: string,
67
76
  config: IpcTransportConfig = {},
68
77
  ): IpcTransport {
69
- const { key = 'ipc', name = 'IPC JSON-RPC', retryDelay } = config
78
+ const { key = 'ipc', name = 'IPC JSON-RPC', reconnect, retryDelay } = config
70
79
  return ({ retryCount: retryCount_, timeout: timeout_ }) => {
71
80
  const retryCount = config.retryCount ?? retryCount_
72
81
  const timeout = timeout_ ?? config.timeout ?? 10_000
@@ -76,7 +85,7 @@ export function ipc(
76
85
  name,
77
86
  async request({ method, params }) {
78
87
  const body = { method, params }
79
- const rpcClient = await getIpcRpcClient(path)
88
+ const rpcClient = await getIpcRpcClient(path, { reconnect })
80
89
  const { error, result } = await rpcClient.requestAsync({
81
90
  body,
82
91
  timeout,
@@ -8,7 +8,10 @@ import type { Hash } from '../../types/misc.js'
8
8
  import type { RpcResponse } from '../../types/rpc.js'
9
9
  import { getSocket } from '../../utils/rpc/compat.js'
10
10
  import type { SocketRpcClient } from '../../utils/rpc/socket.js'
11
- import { getWebSocketRpcClient } from '../../utils/rpc/webSocket.js'
11
+ import {
12
+ type GetWebSocketRpcClientOptions,
13
+ getWebSocketRpcClient,
14
+ } from '../../utils/rpc/webSocket.js'
12
15
  import {
13
16
  type CreateTransportErrorType,
14
17
  type Transport,
@@ -43,6 +46,11 @@ export type WebSocketTransportConfig = {
43
46
  key?: TransportConfig['key'] | undefined
44
47
  /** The name of the WebSocket transport. */
45
48
  name?: TransportConfig['name'] | undefined
49
+ /**
50
+ * Whether or not to attempt to reconnect on socket failure.
51
+ * @default true
52
+ */
53
+ reconnect?: GetWebSocketRpcClientOptions['reconnect'] | undefined
46
54
  /** The max number of times to retry. */
47
55
  retryCount?: TransportConfig['retryCount'] | undefined
48
56
  /** The base delay (in ms) between retries. */
@@ -76,7 +84,12 @@ export function webSocket(
76
84
  url?: string,
77
85
  config: WebSocketTransportConfig = {},
78
86
  ): WebSocketTransport {
79
- const { key = 'webSocket', name = 'WebSocket JSON-RPC', retryDelay } = config
87
+ const {
88
+ key = 'webSocket',
89
+ name = 'WebSocket JSON-RPC',
90
+ reconnect,
91
+ retryDelay,
92
+ } = config
80
93
  return ({ chain, retryCount: retryCount_, timeout: timeout_ }) => {
81
94
  const retryCount = config.retryCount ?? retryCount_
82
95
  const timeout = timeout_ ?? config.timeout ?? 10_000
@@ -88,7 +101,7 @@ export function webSocket(
88
101
  name,
89
102
  async request({ method, params }) {
90
103
  const body = { method, params }
91
- const rpcClient = await getWebSocketRpcClient(url_)
104
+ const rpcClient = await getWebSocketRpcClient(url_, { reconnect })
92
105
  const { error, result } = await rpcClient.requestAsync({
93
106
  body,
94
107
  timeout,
package/errors/version.ts CHANGED
@@ -1 +1 @@
1
- export const version = '2.9.11'
1
+ export const version = '2.9.13'
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "viem",
3
3
  "description": "TypeScript Interface for Ethereum",
4
- "version": "2.9.11",
4
+ "version": "2.9.13",
5
5
  "main": "./_cjs/index.js",
6
6
  "module": "./_esm/index.js",
7
7
  "types": "./_types/index.d.ts",
@@ -139,7 +139,7 @@ export function decodeEventLog<
139
139
  abiItem,
140
140
  param: param as AbiParameter & { indexed: boolean },
141
141
  })
142
- args[param.name || i] = decodeTopic({ param, value: topic })
142
+ args[isUnnamed ? i : param.name || i] = decodeTopic({ param, value: topic })
143
143
  }
144
144
 
145
145
  // Decode data (non-indexed args).
package/utils/rpc/ipc.ts CHANGED
@@ -1,11 +1,17 @@
1
1
  import { type Socket as NetSocket, connect } from 'node:net'
2
2
  import { WebSocketRequestError } from '../../index.js'
3
3
  import {
4
+ type GetSocketRpcClientParameters,
4
5
  type Socket,
5
6
  type SocketRpcClient,
6
7
  getSocketRpcClient,
7
8
  } from './socket.js'
8
9
 
10
+ export type GetIpcRpcClientOptions = Pick<
11
+ GetSocketRpcClientParameters,
12
+ 'reconnect'
13
+ >
14
+
9
15
  const openingBrace = '{'.charCodeAt(0)
10
16
  const closingBrace = '}'.charCodeAt(0)
11
17
 
@@ -33,14 +39,21 @@ export function extractMessages(buffer: Buffer): [Buffer[], Buffer] {
33
39
 
34
40
  export type IpcRpcClient = SocketRpcClient<NetSocket>
35
41
 
36
- export async function getIpcRpcClient(path: string): Promise<IpcRpcClient> {
42
+ export async function getIpcRpcClient(
43
+ path: string,
44
+ options: GetIpcRpcClientOptions = {},
45
+ ): Promise<IpcRpcClient> {
46
+ const { reconnect } = options
47
+
37
48
  return getSocketRpcClient({
38
- async getSocket({ onResponse }) {
49
+ async getSocket({ onError, onOpen, onResponse }) {
39
50
  const socket = connect(path)
40
51
 
41
52
  function onClose() {
42
53
  socket.off('close', onClose)
43
54
  socket.off('message', onData)
55
+ socket.off('error', onError)
56
+ socket.off('connect', onOpen)
44
57
  }
45
58
 
46
59
  let lastRemaining = Buffer.alloc(0)
@@ -57,6 +70,8 @@ export async function getIpcRpcClient(path: string): Promise<IpcRpcClient> {
57
70
 
58
71
  socket.on('close', onClose)
59
72
  socket.on('data', onData)
73
+ socket.on('error', onError)
74
+ socket.on('connect', onOpen)
60
75
 
61
76
  // Wait for the socket to open.
62
77
  await new Promise<void>((resolve, reject) => {
@@ -84,6 +99,7 @@ export async function getIpcRpcClient(path: string): Promise<IpcRpcClient> {
84
99
  },
85
100
  } as Socket<{}>)
86
101
  },
102
+ reconnect,
87
103
  url: path,
88
104
  })
89
105
  }
@@ -9,10 +9,15 @@ import { withTimeout } from '../promise/withTimeout.js'
9
9
  import { idCache } from './id.js'
10
10
 
11
11
  type Id = string | number
12
- type CallbackFn = (message: any) => void
12
+ type CallbackFn = {
13
+ onResponse: (message: any) => void
14
+ onError?: ((error?: Error | Event | undefined) => void) | undefined
15
+ }
13
16
  type CallbackMap = Map<Id, CallbackFn>
14
17
 
15
18
  export type GetSocketParameters = {
19
+ onError: (error?: Error | Event | undefined) => void
20
+ onOpen: () => void
16
21
  onResponse: (data: RpcResponse) => void
17
22
  }
18
23
 
@@ -28,7 +33,7 @@ export type SocketRpcClient<socket extends {}> = {
28
33
  socket: Socket<socket>
29
34
  request(params: {
30
35
  body: RpcRequest
31
- onError?: ((error: Error) => void) | undefined
36
+ onError?: ((error?: Error | Event | undefined) => void) | undefined
32
37
  onResponse: (message: RpcResponse) => void
33
38
  }): void
34
39
  requestAsync(params: {
@@ -40,9 +45,28 @@ export type SocketRpcClient<socket extends {}> = {
40
45
  url: string
41
46
  }
42
47
 
43
- export type GetSocketRpcClientParameters<socket extends {}> = {
44
- url: string
48
+ export type GetSocketRpcClientParameters<socket extends {} = {}> = {
45
49
  getSocket(params: GetSocketParameters): Promise<Socket<socket>>
50
+ /**
51
+ * Whether or not to attempt to reconnect on socket failure.
52
+ * @default true
53
+ */
54
+ reconnect?:
55
+ | boolean
56
+ | {
57
+ /**
58
+ * The maximum number of reconnection attempts.
59
+ * @default 5
60
+ */
61
+ attempts?: number | undefined
62
+ /**
63
+ * The delay (in ms) between reconnection attempts.
64
+ * @default 2_000
65
+ */
66
+ delay?: number | undefined
67
+ }
68
+ | undefined
69
+ url: string
46
70
  }
47
71
 
48
72
  export type GetSocketRpcClientErrorType =
@@ -57,13 +81,16 @@ export const socketClientCache = /*#__PURE__*/ new Map<
57
81
  export async function getSocketRpcClient<socket extends {}>(
58
82
  params: GetSocketRpcClientParameters<socket>,
59
83
  ): Promise<SocketRpcClient<socket>> {
60
- const { getSocket, url } = params
84
+ const { getSocket, reconnect = true, url } = params
85
+ const { attempts = 5, delay = 2_000 } =
86
+ typeof reconnect === 'object' ? reconnect : {}
61
87
 
62
88
  let socketClient = socketClientCache.get(url)
63
89
 
64
90
  // If the socket already exists, return it.
65
91
  if (socketClient) return socketClient as {} as SocketRpcClient<socket>
66
92
 
93
+ let reconnectCount = 0
67
94
  const { schedule } = createBatchScheduler<
68
95
  undefined,
69
96
  [SocketRpcClient<socket>]
@@ -76,17 +103,46 @@ export async function getSocketRpcClient<socket extends {}>(
76
103
  // Set up a cache for subscriptions (eth_subscribe).
77
104
  const subscriptions = new Map<Id, CallbackFn>()
78
105
 
106
+ let error: Error | Event | undefined
107
+ let socket: Socket<any>
79
108
  // Set up socket implementation.
80
- const socket = await getSocket({
81
- onResponse(data) {
82
- const isSubscription = data.method === 'eth_subscription'
83
- const id = isSubscription ? data.params.subscription : data.id
84
- const cache = isSubscription ? subscriptions : requests
85
- const callback = cache.get(id)
86
- if (callback) callback(data)
87
- if (!isSubscription) cache.delete(id)
88
- },
89
- })
109
+ async function setup() {
110
+ return getSocket({
111
+ onError(error_) {
112
+ error = error_
113
+
114
+ // Notify all requests and subscriptions of the error.
115
+ for (const request of requests.values()) request.onError?.(error)
116
+ for (const subscription of subscriptions.values())
117
+ subscription.onError?.(error)
118
+
119
+ // Clear all requests and subscriptions.
120
+ requests.clear()
121
+ subscriptions.clear()
122
+
123
+ // Attempt to reconnect.
124
+ if (reconnect && reconnectCount < attempts)
125
+ setTimeout(async () => {
126
+ reconnectCount++
127
+ socket = await setup().catch(console.error)
128
+ }, delay)
129
+ },
130
+ onOpen() {
131
+ error = undefined
132
+ reconnectCount = 0
133
+ },
134
+ onResponse(data) {
135
+ const isSubscription = data.method === 'eth_subscription'
136
+ const id = isSubscription ? data.params.subscription : data.id
137
+ const cache = isSubscription ? subscriptions : requests
138
+ const callback = cache.get(id)
139
+ if (callback) callback.onResponse(data)
140
+ if (!isSubscription) cache.delete(id)
141
+ },
142
+ })
143
+ }
144
+ socket = await setup()
145
+ error = undefined
90
146
 
91
147
  // Create a new socket instance.
92
148
  socketClient = {
@@ -96,6 +152,8 @@ export async function getSocketRpcClient<socket extends {}>(
96
152
  },
97
153
  socket,
98
154
  request({ body, onError, onResponse }) {
155
+ if (error && onError) onError(error)
156
+
99
157
  const id = body.id ?? idCache.take()
100
158
 
101
159
  const callback = (response: RpcResponse) => {
@@ -107,18 +165,19 @@ export async function getSocketRpcClient<socket extends {}>(
107
165
  body.method === 'eth_subscribe' &&
108
166
  typeof response.result === 'string'
109
167
  )
110
- subscriptions.set(response.result, callback)
168
+ subscriptions.set(response.result, {
169
+ onResponse: callback,
170
+ onError,
171
+ })
111
172
 
112
173
  // If we are unsubscribing from a topic, we want to remove the listener.
113
174
  if (body.method === 'eth_unsubscribe')
114
175
  subscriptions.delete(body.params?.[0])
115
176
 
116
177
  onResponse(response)
117
-
118
- // TODO: delete request?
119
178
  }
120
179
 
121
- requests.set(id, callback)
180
+ requests.set(id, { onResponse: callback, onError })
122
181
  try {
123
182
  socket.request({
124
183
  body: {
@@ -2,22 +2,33 @@ import type { MessageEvent } from 'isows'
2
2
 
3
3
  import { WebSocketRequestError } from '../../errors/request.js'
4
4
  import {
5
+ type GetSocketRpcClientParameters,
5
6
  type Socket,
6
7
  type SocketRpcClient,
7
8
  getSocketRpcClient,
8
9
  } from './socket.js'
9
10
 
11
+ export type GetWebSocketRpcClientOptions = Pick<
12
+ GetSocketRpcClientParameters,
13
+ 'reconnect'
14
+ >
15
+
10
16
  export async function getWebSocketRpcClient(
11
17
  url: string,
18
+ options: GetWebSocketRpcClientOptions | undefined = {},
12
19
  ): Promise<SocketRpcClient<WebSocket>> {
20
+ const { reconnect } = options
21
+
13
22
  return getSocketRpcClient({
14
- async getSocket({ onResponse }) {
23
+ async getSocket({ onError, onOpen, onResponse }) {
15
24
  const WebSocket = await import('isows').then((module) => module.WebSocket)
16
25
  const socket = new WebSocket(url)
17
26
 
18
27
  function onClose() {
19
28
  socket.removeEventListener('close', onClose)
20
29
  socket.removeEventListener('message', onMessage)
30
+ socket.removeEventListener('error', onError)
31
+ socket.removeEventListener('open', onOpen)
21
32
  }
22
33
  function onMessage({ data }: MessageEvent) {
23
34
  onResponse(JSON.parse(data))
@@ -26,6 +37,8 @@ export async function getWebSocketRpcClient(
26
37
  // Setup event listeners for RPC & subscription responses.
27
38
  socket.addEventListener('close', onClose)
28
39
  socket.addEventListener('message', onMessage)
40
+ socket.addEventListener('error', onError)
41
+ socket.addEventListener('open', onOpen)
29
42
 
30
43
  // Wait for the socket to open.
31
44
  if (socket.readyState === WebSocket.CONNECTING) {
@@ -58,6 +71,7 @@ export async function getWebSocketRpcClient(
58
71
  },
59
72
  } as Socket<WebSocket>)
60
73
  },
74
+ reconnect,
61
75
  url,
62
76
  })
63
77
  }