viem 0.0.0-main.20230810T045448 → 0.0.0-main.20230810T051129
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/cjs/actions/public/watchContractEvent.js +134 -67
- package/dist/cjs/actions/public/watchContractEvent.js.map +1 -1
- package/dist/cjs/actions/public/watchEvent.js +136 -62
- package/dist/cjs/actions/public/watchEvent.js.map +1 -1
- package/dist/cjs/errors/version.js +1 -1
- package/dist/esm/actions/public/watchContractEvent.js +145 -76
- package/dist/esm/actions/public/watchContractEvent.js.map +1 -1
- package/dist/esm/actions/public/watchEvent.js +146 -70
- package/dist/esm/actions/public/watchEvent.js.map +1 -1
- package/dist/esm/errors/version.js +1 -1
- package/dist/types/actions/public/watchContractEvent.d.ts +32 -6
- package/dist/types/actions/public/watchContractEvent.d.ts.map +1 -1
- package/dist/types/actions/public/watchEvent.d.ts +32 -9
- package/dist/types/actions/public/watchEvent.d.ts.map +1 -1
- package/dist/types/errors/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/actions/public/watchContractEvent.ts +201 -87
- package/src/actions/public/watchEvent.ts +230 -109
- package/src/errors/version.ts +1 -1
@@ -1,4 +1,4 @@
|
|
1
|
-
import type { AbiEvent, Address, Narrow } from 'abitype'
|
1
|
+
import type { Abi, AbiEvent, Address, Narrow } from 'abitype'
|
2
2
|
|
3
3
|
import type { Client } from '../../clients/createClient.js'
|
4
4
|
import type { Transport } from '../../clients/transports/createTransport.js'
|
@@ -9,11 +9,23 @@ import type {
|
|
9
9
|
} from '../../types/contract.js'
|
10
10
|
import type { Filter } from '../../types/filter.js'
|
11
11
|
import type { Log } from '../../types/log.js'
|
12
|
+
import type { LogTopic } from '../../types/misc.js'
|
13
|
+
import type { GetTransportConfig } from '../../types/transport.js'
|
14
|
+
import type { EncodeEventTopicsParameters } from '../../utils/index.js'
|
12
15
|
import { observe } from '../../utils/observe.js'
|
13
16
|
import { poll } from '../../utils/poll.js'
|
14
17
|
import { stringify } from '../../utils/stringify.js'
|
15
18
|
|
19
|
+
import {
|
20
|
+
DecodeLogDataMismatch,
|
21
|
+
DecodeLogTopicsMismatch,
|
22
|
+
} from '../../errors/abi.js'
|
16
23
|
import { InvalidInputRpcError } from '../../errors/rpc.js'
|
24
|
+
import {
|
25
|
+
decodeEventLog,
|
26
|
+
encodeEventTopics,
|
27
|
+
formatLog,
|
28
|
+
} from '../../utils/index.js'
|
17
29
|
import {
|
18
30
|
type CreateEventFilterParameters,
|
19
31
|
createEventFilter,
|
@@ -23,6 +35,19 @@ import { getFilterChanges } from './getFilterChanges.js'
|
|
23
35
|
import { type GetLogsParameters, getLogs } from './getLogs.js'
|
24
36
|
import { uninstallFilter } from './uninstallFilter.js'
|
25
37
|
|
38
|
+
type PollOptions = {
|
39
|
+
/**
|
40
|
+
* Whether or not the transaction hashes should be batched on each invocation.
|
41
|
+
* @default true
|
42
|
+
*/
|
43
|
+
batch?: boolean
|
44
|
+
/**
|
45
|
+
* Polling frequency (in ms). Defaults to Client's pollingInterval config.
|
46
|
+
* @default client.pollingInterval
|
47
|
+
*/
|
48
|
+
pollingInterval?: number
|
49
|
+
}
|
50
|
+
|
26
51
|
export type WatchEventOnLogsParameter<
|
27
52
|
TAbiEvent extends AbiEvent | undefined = undefined,
|
28
53
|
TAbiEvents extends
|
@@ -55,45 +80,59 @@ export type WatchEventParameters<
|
|
55
80
|
> = {
|
56
81
|
/** The address of the contract. */
|
57
82
|
address?: Address | Address[]
|
58
|
-
/**
|
59
|
-
* Whether or not the event logs should be batched on each invocation.
|
60
|
-
* @default true
|
61
|
-
*/
|
62
|
-
batch?: boolean
|
63
83
|
/** The callback to call when an error occurred when trying to get for a new block. */
|
64
84
|
onError?: (error: Error) => void
|
65
85
|
/** The callback to call when new event logs are received. */
|
66
86
|
onLogs: WatchEventOnLogsFn<TAbiEvent, TAbiEvents, TStrict, _EventName>
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
87
|
+
} & (GetTransportConfig<Transport>['type'] extends 'webSocket'
|
88
|
+
?
|
89
|
+
| {
|
90
|
+
batch?: never
|
91
|
+
/**
|
92
|
+
* Whether or not the WebSocket Transport should poll the JSON-RPC, rather than using `eth_subscribe`.
|
93
|
+
* @default false
|
94
|
+
*/
|
95
|
+
poll?: false
|
96
|
+
pollingInterval?: never
|
97
|
+
}
|
98
|
+
| (PollOptions & {
|
99
|
+
/**
|
100
|
+
* Whether or not the WebSocket Transport should poll the JSON-RPC, rather than using `eth_subscribe`.
|
101
|
+
* @default true
|
102
|
+
*/
|
103
|
+
poll?: true
|
104
|
+
})
|
105
|
+
: PollOptions & {
|
106
|
+
poll?: true
|
107
|
+
}) &
|
108
|
+
(
|
109
|
+
| {
|
110
|
+
event: Narrow<TAbiEvent>
|
111
|
+
events?: never
|
112
|
+
args?: MaybeExtractEventArgsFromAbi<TAbiEvents, _EventName>
|
113
|
+
/**
|
114
|
+
* Whether or not the logs must match the indexed/non-indexed arguments on `event`.
|
115
|
+
* @default false
|
116
|
+
*/
|
117
|
+
strict?: TStrict
|
118
|
+
}
|
119
|
+
| {
|
120
|
+
event?: never
|
121
|
+
events?: Narrow<TAbiEvents>
|
122
|
+
args?: never
|
123
|
+
/**
|
124
|
+
* Whether or not the logs must match the indexed/non-indexed arguments on `event`.
|
125
|
+
* @default false
|
126
|
+
*/
|
127
|
+
strict?: TStrict
|
128
|
+
}
|
129
|
+
| {
|
130
|
+
event?: never
|
131
|
+
events?: never
|
132
|
+
args?: never
|
133
|
+
strict?: never
|
134
|
+
}
|
135
|
+
)
|
97
136
|
|
98
137
|
export type WatchEventReturnType = () => void
|
99
138
|
|
@@ -148,94 +187,176 @@ export function watchEvent<
|
|
148
187
|
events,
|
149
188
|
onError,
|
150
189
|
onLogs,
|
190
|
+
poll: poll_,
|
151
191
|
pollingInterval = client.pollingInterval,
|
152
192
|
strict: strict_,
|
153
193
|
}: WatchEventParameters<TAbiEvent, TAbiEvents, TStrict>,
|
154
194
|
): WatchEventReturnType {
|
155
|
-
const
|
156
|
-
'
|
157
|
-
address,
|
158
|
-
args,
|
159
|
-
batch,
|
160
|
-
client.uid,
|
161
|
-
event,
|
162
|
-
pollingInterval,
|
163
|
-
])
|
195
|
+
const enablePolling =
|
196
|
+
typeof poll_ !== 'undefined' ? poll_ : client.transport.type !== 'webSocket'
|
164
197
|
const strict = strict_ ?? false
|
165
198
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
199
|
+
const pollEvent = () => {
|
200
|
+
const observerId = stringify([
|
201
|
+
'watchEvent',
|
202
|
+
address,
|
203
|
+
args,
|
204
|
+
batch,
|
205
|
+
client.uid,
|
206
|
+
event,
|
207
|
+
pollingInterval,
|
208
|
+
])
|
170
209
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
filter = (await createEventFilter(client, {
|
176
|
-
address,
|
177
|
-
args,
|
178
|
-
event: event!,
|
179
|
-
events,
|
180
|
-
strict,
|
181
|
-
} as unknown as CreateEventFilterParameters)) as unknown as Filter<
|
182
|
-
'event',
|
183
|
-
TAbiEvents,
|
184
|
-
_EventName
|
185
|
-
>
|
186
|
-
} catch {}
|
187
|
-
initialized = true
|
188
|
-
return
|
189
|
-
}
|
210
|
+
return observe(observerId, { onLogs, onError }, (emit) => {
|
211
|
+
let previousBlockNumber: bigint
|
212
|
+
let filter: Filter<'event', TAbiEvents, _EventName, any>
|
213
|
+
let initialized = false
|
190
214
|
|
191
|
-
|
192
|
-
|
193
|
-
if (
|
194
|
-
|
195
|
-
|
196
|
-
// If the filter doesn't exist, we will fall back to use `getLogs`.
|
197
|
-
// The fall back exists because some RPC Providers do not support filters.
|
198
|
-
|
199
|
-
// Fetch the block number to use for `getLogs`.
|
200
|
-
const blockNumber = await getBlockNumber(client)
|
201
|
-
|
202
|
-
// If the block number has changed, we will need to fetch the logs.
|
203
|
-
// If the block number doesn't exist, we are yet to reach the first poll interval,
|
204
|
-
// so do not emit any logs.
|
205
|
-
if (previousBlockNumber && previousBlockNumber !== blockNumber) {
|
206
|
-
logs = await getLogs(client, {
|
215
|
+
const unwatch = poll(
|
216
|
+
async () => {
|
217
|
+
if (!initialized) {
|
218
|
+
try {
|
219
|
+
filter = (await createEventFilter(client, {
|
207
220
|
address,
|
208
221
|
args,
|
209
222
|
event: event!,
|
210
223
|
events,
|
211
|
-
|
212
|
-
|
213
|
-
|
224
|
+
strict,
|
225
|
+
} as unknown as CreateEventFilterParameters)) as unknown as Filter<
|
226
|
+
'event',
|
227
|
+
TAbiEvents,
|
228
|
+
_EventName
|
229
|
+
>
|
230
|
+
} catch {}
|
231
|
+
initialized = true
|
232
|
+
return
|
233
|
+
}
|
234
|
+
|
235
|
+
try {
|
236
|
+
let logs: Log[]
|
237
|
+
if (filter) {
|
238
|
+
logs = await getFilterChanges(client, { filter })
|
214
239
|
} else {
|
215
|
-
|
240
|
+
// If the filter doesn't exist, we will fall back to use `getLogs`.
|
241
|
+
// The fall back exists because some RPC Providers do not support filters.
|
242
|
+
|
243
|
+
// Fetch the block number to use for `getLogs`.
|
244
|
+
const blockNumber = await getBlockNumber(client)
|
245
|
+
|
246
|
+
// If the block number has changed, we will need to fetch the logs.
|
247
|
+
// If the block number doesn't exist, we are yet to reach the first poll interval,
|
248
|
+
// so do not emit any logs.
|
249
|
+
if (previousBlockNumber && previousBlockNumber !== blockNumber) {
|
250
|
+
logs = await getLogs(client, {
|
251
|
+
address,
|
252
|
+
args,
|
253
|
+
event: event!,
|
254
|
+
events,
|
255
|
+
fromBlock: previousBlockNumber + 1n,
|
256
|
+
toBlock: blockNumber,
|
257
|
+
} as unknown as GetLogsParameters)
|
258
|
+
} else {
|
259
|
+
logs = []
|
260
|
+
}
|
261
|
+
previousBlockNumber = blockNumber
|
216
262
|
}
|
217
|
-
|
263
|
+
|
264
|
+
if (logs.length === 0) return
|
265
|
+
if (batch) emit.onLogs(logs as any)
|
266
|
+
else logs.forEach((log) => emit.onLogs([log] as any))
|
267
|
+
} catch (err) {
|
268
|
+
// If a filter has been set and gets uninstalled, providers will throw an InvalidInput error.
|
269
|
+
// Reinitalize the filter when this occurs
|
270
|
+
if (filter && err instanceof InvalidInputRpcError)
|
271
|
+
initialized = false
|
272
|
+
emit.onError?.(err as Error)
|
218
273
|
}
|
274
|
+
},
|
275
|
+
{
|
276
|
+
emitOnBegin: true,
|
277
|
+
interval: pollingInterval,
|
278
|
+
},
|
279
|
+
)
|
280
|
+
|
281
|
+
return async () => {
|
282
|
+
if (filter) await uninstallFilter(client, { filter })
|
283
|
+
unwatch()
|
284
|
+
}
|
285
|
+
})
|
286
|
+
}
|
219
287
|
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
288
|
+
const subscribeEvent = () => {
|
289
|
+
let active = true
|
290
|
+
let unsubscribe = () => (active = false)
|
291
|
+
;(async () => {
|
292
|
+
try {
|
293
|
+
const events_ = events ?? (event ? [event] : undefined)
|
294
|
+
let topics: LogTopic[] = []
|
295
|
+
if (events_) {
|
296
|
+
topics = [
|
297
|
+
(events_ as AbiEvent[]).flatMap((event) =>
|
298
|
+
encodeEventTopics({
|
299
|
+
abi: [event],
|
300
|
+
eventName: (event as AbiEvent).name,
|
301
|
+
args,
|
302
|
+
} as EncodeEventTopicsParameters),
|
303
|
+
),
|
304
|
+
]
|
305
|
+
if (event) topics = topics[0] as LogTopic[]
|
228
306
|
}
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
307
|
+
|
308
|
+
const { unsubscribe: unsubscribe_ } = await client.transport.subscribe({
|
309
|
+
params: ['logs', { address, topics }],
|
310
|
+
onData(data: any) {
|
311
|
+
if (!active) return
|
312
|
+
const log = data.result
|
313
|
+
try {
|
314
|
+
const { eventName, args } = decodeEventLog({
|
315
|
+
abi: events_ as Abi,
|
316
|
+
data: log.data,
|
317
|
+
topics: log.topics as any,
|
318
|
+
strict,
|
319
|
+
})
|
320
|
+
const formatted = formatLog(log, {
|
321
|
+
args,
|
322
|
+
eventName: eventName as string,
|
323
|
+
})
|
324
|
+
onLogs([formatted] as any)
|
325
|
+
} catch (err) {
|
326
|
+
let eventName
|
327
|
+
let isUnnamed
|
328
|
+
if (
|
329
|
+
err instanceof DecodeLogDataMismatch ||
|
330
|
+
err instanceof DecodeLogTopicsMismatch
|
331
|
+
) {
|
332
|
+
// If strict mode is on, and log data/topics do not match event definition, skip.
|
333
|
+
if (strict_) return
|
334
|
+
eventName = err.abiItem.name
|
335
|
+
isUnnamed = err.abiItem.inputs?.some(
|
336
|
+
(x) => !('name' in x && x.name),
|
337
|
+
)
|
338
|
+
}
|
339
|
+
|
340
|
+
// Set args to empty if there is an error decoding (e.g. indexed/non-indexed params mismatch).
|
341
|
+
const formatted = formatLog(log, {
|
342
|
+
args: isUnnamed ? [] : {},
|
343
|
+
eventName,
|
344
|
+
})
|
345
|
+
onLogs([formatted] as any)
|
346
|
+
}
|
347
|
+
},
|
348
|
+
onError(error: Error) {
|
349
|
+
onError?.(error)
|
350
|
+
},
|
351
|
+
})
|
352
|
+
unsubscribe = unsubscribe_
|
353
|
+
if (!active) unsubscribe()
|
354
|
+
} catch (err) {
|
355
|
+
onError?.(err as Error)
|
356
|
+
}
|
357
|
+
})()
|
358
|
+
return unsubscribe
|
359
|
+
}
|
360
|
+
|
361
|
+
return enablePolling ? pollEvent() : subscribeEvent()
|
241
362
|
}
|
package/src/errors/version.ts
CHANGED
@@ -1 +1 @@
|
|
1
|
-
export const version = '0.0.0-main.
|
1
|
+
export const version = '0.0.0-main.20230810T051129'
|