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.
- package/CHANGELOG.md +14 -0
- package/_cjs/actions/public/watchContractEvent.js +8 -2
- package/_cjs/actions/public/watchContractEvent.js.map +1 -1
- package/_cjs/actions/public/watchEvent.js +8 -2
- package/_cjs/actions/public/watchEvent.js.map +1 -1
- package/_cjs/clients/transports/ipc.js +2 -2
- package/_cjs/clients/transports/ipc.js.map +1 -1
- package/_cjs/clients/transports/webSocket.js +2 -2
- package/_cjs/clients/transports/webSocket.js.map +1 -1
- package/_cjs/errors/version.js +1 -1
- package/_cjs/utils/abi/decodeEventLog.js +1 -1
- package/_cjs/utils/abi/decodeEventLog.js.map +1 -1
- package/_cjs/utils/rpc/ipc.js +8 -2
- package/_cjs/utils/rpc/ipc.js.map +1 -1
- package/_cjs/utils/rpc/socket.js +46 -15
- package/_cjs/utils/rpc/socket.js.map +1 -1
- package/_cjs/utils/rpc/webSocket.js +8 -2
- package/_cjs/utils/rpc/webSocket.js.map +1 -1
- package/_esm/actions/public/watchContractEvent.js +8 -2
- package/_esm/actions/public/watchContractEvent.js.map +1 -1
- package/_esm/actions/public/watchEvent.js +8 -2
- package/_esm/actions/public/watchEvent.js.map +1 -1
- package/_esm/clients/transports/ipc.js +3 -3
- package/_esm/clients/transports/ipc.js.map +1 -1
- package/_esm/clients/transports/webSocket.js +3 -3
- package/_esm/clients/transports/webSocket.js.map +1 -1
- package/_esm/errors/version.js +1 -1
- package/_esm/utils/abi/decodeEventLog.js +1 -1
- package/_esm/utils/abi/decodeEventLog.js.map +1 -1
- package/_esm/utils/rpc/ipc.js +8 -2
- package/_esm/utils/rpc/ipc.js.map +1 -1
- package/_esm/utils/rpc/socket.js +49 -16
- package/_esm/utils/rpc/socket.js.map +1 -1
- package/_esm/utils/rpc/webSocket.js +8 -2
- package/_esm/utils/rpc/webSocket.js.map +1 -1
- package/_types/actions/public/watchContractEvent.d.ts +3 -0
- package/_types/actions/public/watchContractEvent.d.ts.map +1 -1
- package/_types/actions/public/watchEvent.d.ts +4 -1
- package/_types/actions/public/watchEvent.d.ts.map +1 -1
- package/_types/clients/transports/ipc.d.ts +6 -1
- package/_types/clients/transports/ipc.d.ts.map +1 -1
- package/_types/clients/transports/webSocket.d.ts +6 -0
- package/_types/clients/transports/webSocket.d.ts.map +1 -1
- package/_types/errors/version.d.ts +1 -1
- package/_types/utils/rpc/compat.d.ts +8 -2
- package/_types/utils/rpc/compat.d.ts.map +1 -1
- package/_types/utils/rpc/ipc.d.ts +3 -2
- package/_types/utils/rpc/ipc.d.ts.map +1 -1
- package/_types/utils/rpc/socket.d.ts +25 -4
- package/_types/utils/rpc/socket.d.ts.map +1 -1
- package/_types/utils/rpc/webSocket.d.ts +3 -2
- package/_types/utils/rpc/webSocket.d.ts.map +1 -1
- package/actions/public/watchContractEvent.ts +10 -1
- package/actions/public/watchEvent.ts +10 -1
- package/clients/transports/ipc.ts +12 -3
- package/clients/transports/webSocket.ts +16 -3
- package/errors/version.ts +1 -1
- package/package.json +1 -1
- package/utils/abi/decodeEventLog.ts +1 -1
- package/utils/rpc/ipc.ts +18 -2
- package/utils/rpc/socket.ts +78 -19
- 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'
|
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'
|
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 {
|
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 {
|
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 {
|
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.
|
1
|
+
export const version = '2.9.13'
|
package/package.json
CHANGED
@@ -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(
|
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
|
}
|
package/utils/rpc/socket.ts
CHANGED
@@ -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 =
|
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
|
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
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
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,
|
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: {
|
package/utils/rpc/webSocket.ts
CHANGED
@@ -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
|
}
|