@metamask-previews/eth-block-tracker 15.0.1-preview-abca9bcea → 16.0.0-preview-0a30e47

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 (48) hide show
  1. package/CHANGELOG.md +10 -2
  2. package/dist/{BlockTracker.d.mts → BlockTracker.d.ts} +2 -2
  3. package/dist/BlockTracker.d.ts.map +1 -0
  4. package/dist/BlockTracker.js +2 -0
  5. package/dist/BlockTracker.js.map +1 -0
  6. package/dist/{PollingBlockTracker.d.cts → PollingBlockTracker.d.ts} +5 -5
  7. package/dist/PollingBlockTracker.d.ts.map +1 -0
  8. package/dist/PollingBlockTracker.js +354 -0
  9. package/dist/PollingBlockTracker.js.map +1 -0
  10. package/dist/index.d.ts +3 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +2 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/logging-utils.d.ts +4 -0
  15. package/dist/logging-utils.d.ts.map +1 -0
  16. package/dist/logging-utils.js +4 -0
  17. package/dist/logging-utils.js.map +1 -0
  18. package/package.json +15 -20
  19. package/dist/BlockTracker.cjs +0 -3
  20. package/dist/BlockTracker.cjs.map +0 -1
  21. package/dist/BlockTracker.d.cts +0 -9
  22. package/dist/BlockTracker.d.cts.map +0 -1
  23. package/dist/BlockTracker.d.mts.map +0 -1
  24. package/dist/BlockTracker.mjs +0 -2
  25. package/dist/BlockTracker.mjs.map +0 -1
  26. package/dist/PollingBlockTracker.cjs +0 -355
  27. package/dist/PollingBlockTracker.cjs.map +0 -1
  28. package/dist/PollingBlockTracker.d.cts.map +0 -1
  29. package/dist/PollingBlockTracker.d.mts +0 -33
  30. package/dist/PollingBlockTracker.d.mts.map +0 -1
  31. package/dist/PollingBlockTracker.mjs +0 -355
  32. package/dist/PollingBlockTracker.mjs.map +0 -1
  33. package/dist/index.cjs +0 -18
  34. package/dist/index.cjs.map +0 -1
  35. package/dist/index.d.cts +0 -3
  36. package/dist/index.d.cts.map +0 -1
  37. package/dist/index.d.mts +0 -3
  38. package/dist/index.d.mts.map +0 -1
  39. package/dist/index.mjs +0 -2
  40. package/dist/index.mjs.map +0 -1
  41. package/dist/logging-utils.cjs +0 -7
  42. package/dist/logging-utils.cjs.map +0 -1
  43. package/dist/logging-utils.d.cts +0 -5
  44. package/dist/logging-utils.d.cts.map +0 -1
  45. package/dist/logging-utils.d.mts +0 -5
  46. package/dist/logging-utils.d.mts.map +0 -1
  47. package/dist/logging-utils.mjs +0 -4
  48. package/dist/logging-utils.mjs.map +0 -1
package/CHANGELOG.md CHANGED
@@ -7,11 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [16.0.0]
11
+
10
12
  ### Changed
11
13
 
14
+ - **BREAKING:** Drop CommonJS support ([#9536](https://github.com/MetaMask/core/pull/9536))
15
+ - This package is now ESM-only, but can still be used in CommonJS projects via `require(esm)` in modern Node.js versions (22+), or dynamic imports in older Node.js versions.
16
+ - **BREAKING:** Bump minimum Node.js version to 22 ([#9976](https://github.com/MetaMask/core/pull/9976))
17
+ - **BREAKING:** Bump TypeScript target to ES2022 ([#10019](https://github.com/MetaMask/core/pull/10019))
18
+ - This package now ships ES2022 code, requiring a compatible modern environment or bundler configuration to consume.
12
19
  - Bump `@metamask/utils` from `^11.9.0` to `^11.12.0` ([#9074](https://github.com/MetaMask/core/pull/9074), [#10076](https://github.com/MetaMask/core/pull/10076))
13
20
  - Bump `@metamask/json-rpc-engine` from `^10.2.4` to `^10.3.0` ([#8661](https://github.com/MetaMask/core/pull/8661))
14
- - Bump `@metamask/eth-json-rpc-provider` from `^6.0.0` to `^6.0.1` ([#8317](https://github.com/MetaMask/core/pull/8317))
21
+ - Bump `@metamask/eth-json-rpc-provider` from `^6.0.0` to `^7.0.0` ([#8317](https://github.com/MetaMask/core/pull/8317), [#10160](https://github.com/MetaMask/core/pull/10160))
15
22
 
16
23
  ### Fixed
17
24
 
@@ -47,7 +54,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
47
54
  - See [`MetaMask/eth-block-tracker`](https://github.com/MetaMask/eth-block-tracker/blob/main/CHANGELOG.md)
48
55
  for the original changelog.
49
56
 
50
- [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/eth-block-tracker@15.0.1...HEAD
57
+ [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/eth-block-tracker@16.0.0...HEAD
58
+ [16.0.0]: https://github.com/MetaMask/core/compare/@metamask/eth-block-tracker@15.0.1...@metamask/eth-block-tracker@16.0.0
51
59
  [15.0.1]: https://github.com/MetaMask/core/compare/@metamask/eth-block-tracker@15.0.0...@metamask/eth-block-tracker@15.0.1
52
60
  [15.0.0]: https://github.com/MetaMask/core/compare/@metamask/eth-block-tracker@14.0.0...@metamask/eth-block-tracker@15.0.0
53
61
  [14.0.0]: https://github.com/MetaMask/core/releases/tag/@metamask/eth-block-tracker@14.0.0
@@ -1,4 +1,4 @@
1
- import type SafeEventEmitter from "@metamask/safe-event-emitter";
1
+ import type SafeEventEmitter from '@metamask/safe-event-emitter';
2
2
  export type BlockTracker = SafeEventEmitter & {
3
3
  destroy(): Promise<void>;
4
4
  isRunning(): boolean;
@@ -6,4 +6,4 @@ export type BlockTracker = SafeEventEmitter & {
6
6
  getLatestBlock(): Promise<string>;
7
7
  checkForLatestBlock(): Promise<string>;
8
8
  };
9
- //# sourceMappingURL=BlockTracker.d.mts.map
9
+ //# sourceMappingURL=BlockTracker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BlockTracker.d.ts","sourceRoot":"","sources":["../src/BlockTracker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,gBAAgB,MAAM,8BAA8B,CAAC;AAEjE,MAAM,MAAM,YAAY,GAAG,gBAAgB,GAAG;IAC5C,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEzB,SAAS,IAAI,OAAO,CAAC;IAErB,eAAe,IAAI,MAAM,GAAG,IAAI,CAAC;IAEjC,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAElC,mBAAmB,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CACxC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=BlockTracker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BlockTracker.js","sourceRoot":"","sources":["../src/BlockTracker.ts"],"names":[],"mappings":"","sourcesContent":["import type SafeEventEmitter from '@metamask/safe-event-emitter';\n\nexport type BlockTracker = SafeEventEmitter & {\n destroy(): Promise<void>;\n\n isRunning(): boolean;\n\n getCurrentBlock(): string | null;\n\n getLatestBlock(): Promise<string>;\n\n checkForLatestBlock(): Promise<string>;\n};\n"]}
@@ -1,7 +1,7 @@
1
- import type { InternalProvider } from "@metamask/eth-json-rpc-provider";
2
- import type { ContextConstraint, MiddlewareContext } from "@metamask/json-rpc-engine/v2";
3
- import SafeEventEmitter from "@metamask/safe-event-emitter";
4
- import type { BlockTracker } from "./BlockTracker.cjs";
1
+ import type { InternalProvider } from '@metamask/eth-json-rpc-provider';
2
+ import type { ContextConstraint, MiddlewareContext } from '@metamask/json-rpc-engine/v2';
3
+ import SafeEventEmitter from '@metamask/safe-event-emitter';
4
+ import type { BlockTracker } from './BlockTracker.js';
5
5
  export type PollingBlockTrackerOptions<Context extends ContextConstraint = MiddlewareContext> = {
6
6
  provider?: InternalProvider<Context>;
7
7
  pollingInterval?: number;
@@ -30,4 +30,4 @@ export declare class PollingBlockTracker<Context extends ContextConstraint = Mid
30
30
  */
31
31
  checkForLatestBlock(): Promise<string>;
32
32
  }
33
- //# sourceMappingURL=PollingBlockTracker.d.cts.map
33
+ //# sourceMappingURL=PollingBlockTracker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PollingBlockTracker.d.ts","sourceRoot":"","sources":["../src/PollingBlockTracker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,KAAK,EACV,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,8BAA8B,CAAC;AACtC,OAAO,gBAAgB,MAAM,8BAA8B,CAAC;AAK5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAStD,MAAM,MAAM,0BAA0B,CACpC,OAAO,SAAS,iBAAiB,GAAG,iBAAiB,IACnD;IACF,QAAQ,CAAC,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC;IACrC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAQF,qBAAa,mBAAmB,CAC9B,OAAO,SAAS,iBAAiB,GAAG,iBAAiB,CAErD,SAAQ,gBACR,YAAW,YAAY;;IAsCvB,YAAY,IAAI,GAAE,0BAA0B,CAAC,OAAO,CAAM,EA6BzD;IAEK,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAI7B;IAED,SAAS,IAAI,OAAO,CAEnB;IAED,eAAe,IAAI,MAAM,GAAG,IAAI,CAE/B;IAEK,cAAc,CAAC,EACnB,QAAe,GAChB,GAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAoD/C;IAGD,kBAAkB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAcpD;IAsHD;;;;;;OAMG;IACH,mBAAmB,IAAI,OAAO,CAAC,MAAM,CAAC,CASrC;CA+HF"}
@@ -0,0 +1,354 @@
1
+ import SafeEventEmitter from '@metamask/safe-event-emitter';
2
+ import { createDeferredPromise, getErrorMessage } from '@metamask/utils';
3
+ import getCreateRandomId from 'json-rpc-random-id';
4
+ import { projectLogger, createModuleLogger } from './logging-utils.js';
5
+ const log = createModuleLogger(projectLogger, 'polling-block-tracker');
6
+ const createRandomId = getCreateRandomId();
7
+ const sec = 1000;
8
+ const blockTrackerEvents = ['sync', 'latest'];
9
+ export class PollingBlockTracker extends SafeEventEmitter {
10
+ #isRunning;
11
+ #blockResetDuration;
12
+ #usePastBlocks;
13
+ #currentBlock;
14
+ #blockResetTimeout;
15
+ #pollingTimeout;
16
+ #provider;
17
+ #pollingInterval;
18
+ #retryTimeout;
19
+ #keepEventLoopActive;
20
+ #setSkipCacheFlag;
21
+ #internalEventListeners = [];
22
+ #pendingLatestBlock;
23
+ #pendingFetch;
24
+ #pendingCheckForLatestBlock;
25
+ #onNewListener;
26
+ #onRemoveListener;
27
+ #resetCurrentBlock;
28
+ constructor(opts = {}) {
29
+ // parse + validate args
30
+ if (!opts.provider) {
31
+ throw new Error('PollingBlockTracker - no provider specified.');
32
+ }
33
+ super();
34
+ // config
35
+ this.#blockResetDuration = opts.blockResetDuration ?? 20 * sec;
36
+ this.#usePastBlocks = opts.usePastBlocks ?? false;
37
+ // state
38
+ this.#currentBlock = null;
39
+ this.#isRunning = false;
40
+ // bind functions for internal use
41
+ this.#onNewListener = this.#onNewListenerUnbound.bind(this);
42
+ this.#onRemoveListener = this.#onRemoveListenerUnbound.bind(this);
43
+ this.#resetCurrentBlock = this.#resetCurrentBlockUnbound.bind(this);
44
+ // listen for handler changes
45
+ this.#setupInternalEvents();
46
+ // config
47
+ this.#provider = opts.provider;
48
+ this.#pollingInterval = opts.pollingInterval ?? 20 * sec;
49
+ this.#retryTimeout = opts.retryTimeout ?? this.#pollingInterval / 10;
50
+ this.#keepEventLoopActive = opts.keepEventLoopActive ?? true;
51
+ this.#setSkipCacheFlag = opts.setSkipCacheFlag ?? false;
52
+ }
53
+ async destroy() {
54
+ this.#cancelBlockResetTimeout();
55
+ super.removeAllListeners();
56
+ this.#maybeEnd();
57
+ }
58
+ isRunning() {
59
+ return this.#isRunning;
60
+ }
61
+ getCurrentBlock() {
62
+ return this.#currentBlock;
63
+ }
64
+ async getLatestBlock({ useCache = true, } = {}) {
65
+ // return if available
66
+ if (this.#currentBlock && useCache) {
67
+ return this.#currentBlock;
68
+ }
69
+ if (this.#pendingLatestBlock) {
70
+ return await this.#pendingLatestBlock.promise;
71
+ }
72
+ const { promise, resolve, reject } = createDeferredPromise({
73
+ suppressUnhandledRejection: true,
74
+ });
75
+ this.#pendingLatestBlock = { reject, promise };
76
+ if (this.#isRunning) {
77
+ try {
78
+ // If tracker is running, wait for next block with timeout
79
+ const onLatestBlock = (value) => {
80
+ this.#removeInternalListener(onLatestBlock);
81
+ this.removeListener('latest', onLatestBlock);
82
+ resolve(value);
83
+ };
84
+ this.#addInternalListener(onLatestBlock);
85
+ this.once('latest', onLatestBlock);
86
+ return await promise;
87
+ }
88
+ catch (error) {
89
+ reject(error);
90
+ throw error;
91
+ }
92
+ finally {
93
+ this.#pendingLatestBlock = undefined;
94
+ }
95
+ }
96
+ else {
97
+ // If tracker isn't running, just fetch directly
98
+ try {
99
+ const latestBlock = await this.#updateLatestBlock();
100
+ resolve(latestBlock);
101
+ return latestBlock;
102
+ }
103
+ catch (error) {
104
+ reject(error);
105
+ throw error;
106
+ }
107
+ finally {
108
+ // We want to rate limit calls to this method if we made a direct fetch
109
+ // for the block number because the BlockTracker was not running. We
110
+ // achieve this by delaying the unsetting of the #pendingLatestBlock promise.
111
+ setTimeout(() => {
112
+ this.#pendingLatestBlock = undefined;
113
+ }, this.#pollingInterval);
114
+ }
115
+ }
116
+ }
117
+ // Don't allow module consumer to remove our internal event listeners.
118
+ removeAllListeners(eventName) {
119
+ // perform default behavior, preserve fn arity
120
+ if (eventName) {
121
+ super.removeAllListeners(eventName);
122
+ }
123
+ else {
124
+ super.removeAllListeners();
125
+ }
126
+ // re-add internal events
127
+ this.#setupInternalEvents();
128
+ // trigger stop check just in case
129
+ this.#onRemoveListener();
130
+ return this;
131
+ }
132
+ #setupInternalEvents() {
133
+ // first remove listeners for idempotence
134
+ this.removeListener('newListener', this.#onNewListener);
135
+ this.removeListener('removeListener', this.#onRemoveListener);
136
+ // then add them
137
+ this.on('newListener', this.#onNewListener);
138
+ this.on('removeListener', this.#onRemoveListener);
139
+ }
140
+ #onNewListenerUnbound(eventName) {
141
+ // `newListener` is called *before* the listener is added
142
+ if (blockTrackerEvents.includes(eventName)) {
143
+ // TODO: Handle dangling promise
144
+ this.#maybeStart();
145
+ }
146
+ }
147
+ #onRemoveListenerUnbound() {
148
+ // `removeListener` is called *after* the listener is removed
149
+ if (this.#getBlockTrackerEventCount() > 0) {
150
+ return;
151
+ }
152
+ this.#maybeEnd();
153
+ }
154
+ #maybeStart() {
155
+ if (this.#isRunning) {
156
+ return;
157
+ }
158
+ this.#isRunning = true;
159
+ // cancel setting latest block to stale
160
+ this.#cancelBlockResetTimeout();
161
+ this.#start();
162
+ this.emit('_started');
163
+ }
164
+ #maybeEnd() {
165
+ if (!this.#isRunning) {
166
+ return;
167
+ }
168
+ this.#isRunning = false;
169
+ this.#setupBlockResetTimeout();
170
+ this.#end();
171
+ this.#rejectPendingLatestBlock(new Error('Block tracker destroyed'));
172
+ this.emit('_ended');
173
+ }
174
+ #getBlockTrackerEventCount() {
175
+ return (blockTrackerEvents
176
+ .map((eventName) => this.listeners(eventName))
177
+ .flat()
178
+ // internal listeners are not included in the count
179
+ .filter((listener) => this.#internalEventListeners.every((internalListener) => !Object.is(internalListener, listener))).length);
180
+ }
181
+ #shouldUseNewBlock(newBlock) {
182
+ const currentBlock = this.#currentBlock;
183
+ if (!currentBlock) {
184
+ return true;
185
+ }
186
+ const newBlockInt = hexToInt(newBlock);
187
+ const currentBlockInt = hexToInt(currentBlock);
188
+ return ((this.#usePastBlocks && newBlockInt < currentBlockInt) ||
189
+ newBlockInt > currentBlockInt);
190
+ }
191
+ #newPotentialLatest(newBlock) {
192
+ if (!this.#shouldUseNewBlock(newBlock)) {
193
+ return;
194
+ }
195
+ this.#setCurrentBlock(newBlock);
196
+ }
197
+ #setCurrentBlock(newBlock) {
198
+ const oldBlock = this.#currentBlock;
199
+ this.#currentBlock = newBlock;
200
+ this.emit('latest', newBlock);
201
+ this.emit('sync', { oldBlock, newBlock });
202
+ }
203
+ #setupBlockResetTimeout() {
204
+ // clear any existing timeout
205
+ this.#cancelBlockResetTimeout();
206
+ // clear latest block when stale
207
+ this.#blockResetTimeout = setTimeout(this.#resetCurrentBlock, this.#blockResetDuration);
208
+ // nodejs - dont hold process open
209
+ if (this.#blockResetTimeout.unref) {
210
+ this.#blockResetTimeout.unref();
211
+ }
212
+ }
213
+ #cancelBlockResetTimeout() {
214
+ if (this.#blockResetTimeout) {
215
+ clearTimeout(this.#blockResetTimeout);
216
+ }
217
+ }
218
+ #resetCurrentBlockUnbound() {
219
+ this.#currentBlock = null;
220
+ }
221
+ /**
222
+ * Checks for the latest block, updates the internal state, and returns the
223
+ * value immediately rather than waiting for the next polling interval.
224
+ *
225
+ * @deprecated Use {@link getLatestBlock} instead.
226
+ * @returns A promise that resolves to the latest block number.
227
+ */
228
+ checkForLatestBlock() {
229
+ if (!this.#pendingCheckForLatestBlock) {
230
+ this.#pendingCheckForLatestBlock = this.#updateLatestBlock()
231
+ .then(() => this.getLatestBlock())
232
+ .finally(() => {
233
+ this.#pendingCheckForLatestBlock = undefined;
234
+ });
235
+ }
236
+ return this.#pendingCheckForLatestBlock;
237
+ }
238
+ #start() {
239
+ // Intentionally not awaited as this starts the polling via a timeout chain.
240
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
241
+ this.#updateAndQueue();
242
+ }
243
+ #end() {
244
+ this.#clearPollingTimeout();
245
+ }
246
+ async #updateLatestBlock() {
247
+ // fetch + set latest block
248
+ const latestBlock = await this.#fetchLatestBlock();
249
+ this.#newPotentialLatest(latestBlock);
250
+ if (!this.#isRunning) {
251
+ // Ensure the one-time update is eventually reset once it's stale
252
+ this.#setupBlockResetTimeout();
253
+ }
254
+ // _newPotentialLatest() ensures that this._currentBlock is not null
255
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
256
+ return this.#currentBlock;
257
+ }
258
+ async #fetchLatestBlock() {
259
+ // If there's already a pending fetch, reuse it
260
+ if (this.#pendingFetch) {
261
+ return await this.#pendingFetch.promise;
262
+ }
263
+ // Create a new deferred promise for this request
264
+ const { promise, resolve, reject } = createDeferredPromise({
265
+ suppressUnhandledRejection: true,
266
+ });
267
+ this.#pendingFetch = { reject, promise };
268
+ try {
269
+ const req = {
270
+ jsonrpc: '2.0',
271
+ id: createRandomId(),
272
+ method: 'eth_blockNumber',
273
+ params: [],
274
+ };
275
+ if (this.#setSkipCacheFlag) {
276
+ req.skipCache = true;
277
+ }
278
+ log('Making request', req);
279
+ const result = await this.#provider.request(req);
280
+ log('Got result', result);
281
+ resolve(result);
282
+ return result;
283
+ }
284
+ catch (error) {
285
+ log('Encountered error fetching block', getErrorMessage(error));
286
+ reject(error);
287
+ this.#rejectPendingLatestBlock(error);
288
+ throw error;
289
+ }
290
+ finally {
291
+ this.#pendingFetch = undefined;
292
+ }
293
+ }
294
+ /**
295
+ * The core polling function that runs after each interval.
296
+ * Updates the latest block and then queues the next update.
297
+ */
298
+ async #updateAndQueue() {
299
+ let interval = this.#pollingInterval;
300
+ try {
301
+ await this.#updateLatestBlock();
302
+ }
303
+ catch (error) {
304
+ try {
305
+ this.emit('error', error);
306
+ }
307
+ catch {
308
+ console.error(`Error updating latest block: ${getErrorMessage(error)}`);
309
+ }
310
+ interval = this.#retryTimeout;
311
+ }
312
+ if (!this.#isRunning) {
313
+ return;
314
+ }
315
+ this.#clearPollingTimeout();
316
+ const timeoutRef = setTimeout(() => {
317
+ // Intentionally not awaited as this just continues the polling loop.
318
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
319
+ this.#updateAndQueue();
320
+ }, interval);
321
+ if (timeoutRef.unref && !this.#keepEventLoopActive) {
322
+ timeoutRef.unref();
323
+ }
324
+ this.#pollingTimeout = timeoutRef;
325
+ this.emit('_waitingForNextIteration');
326
+ }
327
+ #clearPollingTimeout() {
328
+ if (this.#pollingTimeout) {
329
+ clearTimeout(this.#pollingTimeout);
330
+ this.#pollingTimeout = undefined;
331
+ }
332
+ }
333
+ #addInternalListener(listener) {
334
+ this.#internalEventListeners.push(listener);
335
+ }
336
+ #removeInternalListener(listener) {
337
+ this.#internalEventListeners.splice(this.#internalEventListeners.indexOf(listener), 1);
338
+ }
339
+ #rejectPendingLatestBlock(error) {
340
+ this.#pendingLatestBlock?.reject(error);
341
+ this.#pendingLatestBlock = undefined;
342
+ }
343
+ }
344
+ /**
345
+ * Converts a number represented as a string in hexadecimal format into a native
346
+ * number.
347
+ *
348
+ * @param hexInt - The hex string.
349
+ * @returns The number.
350
+ */
351
+ function hexToInt(hexInt) {
352
+ return Number.parseInt(hexInt, 16);
353
+ }
354
+ //# sourceMappingURL=PollingBlockTracker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PollingBlockTracker.js","sourceRoot":"","sources":["../src/PollingBlockTracker.ts"],"names":[],"mappings":"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","sourcesContent":["import type { InternalProvider } from '@metamask/eth-json-rpc-provider';\nimport type {\n ContextConstraint,\n MiddlewareContext,\n} from '@metamask/json-rpc-engine/v2';\nimport SafeEventEmitter from '@metamask/safe-event-emitter';\nimport { createDeferredPromise, getErrorMessage } from '@metamask/utils';\nimport type { DeferredPromise, JsonRpcRequest } from '@metamask/utils';\nimport getCreateRandomId from 'json-rpc-random-id';\n\nimport type { BlockTracker } from './BlockTracker.js';\nimport { projectLogger, createModuleLogger } from './logging-utils.js';\n\nconst log = createModuleLogger(projectLogger, 'polling-block-tracker');\nconst createRandomId = getCreateRandomId();\nconst sec = 1000;\n\nconst blockTrackerEvents: (string | symbol)[] = ['sync', 'latest'];\n\nexport type PollingBlockTrackerOptions<\n Context extends ContextConstraint = MiddlewareContext,\n> = {\n provider?: InternalProvider<Context>;\n pollingInterval?: number;\n retryTimeout?: number;\n keepEventLoopActive?: boolean;\n setSkipCacheFlag?: boolean;\n blockResetDuration?: number;\n usePastBlocks?: boolean;\n};\n\ntype ExtendedJsonRpcRequest = {\n skipCache?: boolean;\n} & JsonRpcRequest<[]>;\n\ntype InternalListener = (value: string) => void;\n\nexport class PollingBlockTracker<\n Context extends ContextConstraint = MiddlewareContext,\n>\n extends SafeEventEmitter\n implements BlockTracker\n{\n #isRunning: boolean;\n\n readonly #blockResetDuration: number;\n\n readonly #usePastBlocks: boolean;\n\n #currentBlock: string | null;\n\n #blockResetTimeout?: ReturnType<typeof setTimeout>;\n\n #pollingTimeout?: ReturnType<typeof setTimeout>;\n\n readonly #provider: InternalProvider<Context>;\n\n readonly #pollingInterval: number;\n\n readonly #retryTimeout: number;\n\n readonly #keepEventLoopActive: boolean;\n\n readonly #setSkipCacheFlag: boolean;\n\n readonly #internalEventListeners: InternalListener[] = [];\n\n #pendingLatestBlock?: Omit<DeferredPromise<string>, 'resolve'>;\n\n #pendingFetch?: Omit<DeferredPromise<string>, 'resolve'>;\n\n #pendingCheckForLatestBlock?: Promise<string>;\n\n readonly #onNewListener: (eventName: string | symbol) => void;\n\n readonly #onRemoveListener: () => void;\n\n readonly #resetCurrentBlock: () => void;\n\n constructor(opts: PollingBlockTrackerOptions<Context> = {}) {\n // parse + validate args\n if (!opts.provider) {\n throw new Error('PollingBlockTracker - no provider specified.');\n }\n\n super();\n\n // config\n this.#blockResetDuration = opts.blockResetDuration ?? 20 * sec;\n this.#usePastBlocks = opts.usePastBlocks ?? false;\n // state\n this.#currentBlock = null;\n this.#isRunning = false;\n\n // bind functions for internal use\n this.#onNewListener = this.#onNewListenerUnbound.bind(this);\n this.#onRemoveListener = this.#onRemoveListenerUnbound.bind(this);\n this.#resetCurrentBlock = this.#resetCurrentBlockUnbound.bind(this);\n\n // listen for handler changes\n this.#setupInternalEvents();\n\n // config\n this.#provider = opts.provider;\n this.#pollingInterval = opts.pollingInterval ?? 20 * sec;\n this.#retryTimeout = opts.retryTimeout ?? this.#pollingInterval / 10;\n this.#keepEventLoopActive = opts.keepEventLoopActive ?? true;\n this.#setSkipCacheFlag = opts.setSkipCacheFlag ?? false;\n }\n\n async destroy(): Promise<void> {\n this.#cancelBlockResetTimeout();\n super.removeAllListeners();\n this.#maybeEnd();\n }\n\n isRunning(): boolean {\n return this.#isRunning;\n }\n\n getCurrentBlock(): string | null {\n return this.#currentBlock;\n }\n\n async getLatestBlock({\n useCache = true,\n }: { useCache?: boolean } = {}): Promise<string> {\n // return if available\n if (this.#currentBlock && useCache) {\n return this.#currentBlock;\n }\n\n if (this.#pendingLatestBlock) {\n return await this.#pendingLatestBlock.promise;\n }\n\n const { promise, resolve, reject } = createDeferredPromise<string>({\n suppressUnhandledRejection: true,\n });\n this.#pendingLatestBlock = { reject, promise };\n\n if (this.#isRunning) {\n try {\n // If tracker is running, wait for next block with timeout\n const onLatestBlock = (value: string): void => {\n this.#removeInternalListener(onLatestBlock);\n this.removeListener('latest', onLatestBlock);\n resolve(value);\n };\n\n this.#addInternalListener(onLatestBlock);\n this.once('latest', onLatestBlock);\n\n return await promise;\n } catch (error) {\n reject(error);\n throw error;\n } finally {\n this.#pendingLatestBlock = undefined;\n }\n } else {\n // If tracker isn't running, just fetch directly\n try {\n const latestBlock = await this.#updateLatestBlock();\n resolve(latestBlock);\n return latestBlock;\n } catch (error) {\n reject(error);\n throw error;\n } finally {\n // We want to rate limit calls to this method if we made a direct fetch\n // for the block number because the BlockTracker was not running. We\n // achieve this by delaying the unsetting of the #pendingLatestBlock promise.\n setTimeout(() => {\n this.#pendingLatestBlock = undefined;\n }, this.#pollingInterval);\n }\n }\n }\n\n // Don't allow module consumer to remove our internal event listeners.\n removeAllListeners(eventName?: string | symbol): this {\n // perform default behavior, preserve fn arity\n if (eventName) {\n super.removeAllListeners(eventName);\n } else {\n super.removeAllListeners();\n }\n\n // re-add internal events\n this.#setupInternalEvents();\n // trigger stop check just in case\n this.#onRemoveListener();\n\n return this;\n }\n\n #setupInternalEvents(): void {\n // first remove listeners for idempotence\n this.removeListener('newListener', this.#onNewListener);\n this.removeListener('removeListener', this.#onRemoveListener);\n // then add them\n this.on('newListener', this.#onNewListener);\n this.on('removeListener', this.#onRemoveListener);\n }\n\n #onNewListenerUnbound(eventName: string | symbol): void {\n // `newListener` is called *before* the listener is added\n if (blockTrackerEvents.includes(eventName)) {\n // TODO: Handle dangling promise\n this.#maybeStart();\n }\n }\n\n #onRemoveListenerUnbound(): void {\n // `removeListener` is called *after* the listener is removed\n if (this.#getBlockTrackerEventCount() > 0) {\n return;\n }\n this.#maybeEnd();\n }\n\n #maybeStart(): void {\n if (this.#isRunning) {\n return;\n }\n\n this.#isRunning = true;\n // cancel setting latest block to stale\n this.#cancelBlockResetTimeout();\n this.#start();\n this.emit('_started');\n }\n\n #maybeEnd(): void {\n if (!this.#isRunning) {\n return;\n }\n\n this.#isRunning = false;\n this.#setupBlockResetTimeout();\n this.#end();\n this.#rejectPendingLatestBlock(new Error('Block tracker destroyed'));\n this.emit('_ended');\n }\n\n #getBlockTrackerEventCount(): number {\n return (\n blockTrackerEvents\n .map((eventName) => this.listeners(eventName))\n .flat()\n // internal listeners are not included in the count\n .filter((listener) =>\n this.#internalEventListeners.every(\n (internalListener) => !Object.is(internalListener, listener),\n ),\n ).length\n );\n }\n\n #shouldUseNewBlock(newBlock: string): boolean {\n const currentBlock = this.#currentBlock;\n if (!currentBlock) {\n return true;\n }\n const newBlockInt = hexToInt(newBlock);\n const currentBlockInt = hexToInt(currentBlock);\n\n return (\n (this.#usePastBlocks && newBlockInt < currentBlockInt) ||\n newBlockInt > currentBlockInt\n );\n }\n\n #newPotentialLatest(newBlock: string): void {\n if (!this.#shouldUseNewBlock(newBlock)) {\n return;\n }\n this.#setCurrentBlock(newBlock);\n }\n\n #setCurrentBlock(newBlock: string): void {\n const oldBlock = this.#currentBlock;\n this.#currentBlock = newBlock;\n this.emit('latest', newBlock);\n this.emit('sync', { oldBlock, newBlock });\n }\n\n #setupBlockResetTimeout(): void {\n // clear any existing timeout\n this.#cancelBlockResetTimeout();\n // clear latest block when stale\n this.#blockResetTimeout = setTimeout(\n this.#resetCurrentBlock,\n this.#blockResetDuration,\n );\n\n // nodejs - dont hold process open\n if (this.#blockResetTimeout.unref) {\n this.#blockResetTimeout.unref();\n }\n }\n\n #cancelBlockResetTimeout(): void {\n if (this.#blockResetTimeout) {\n clearTimeout(this.#blockResetTimeout);\n }\n }\n\n #resetCurrentBlockUnbound(): void {\n this.#currentBlock = null;\n }\n\n /**\n * Checks for the latest block, updates the internal state, and returns the\n * value immediately rather than waiting for the next polling interval.\n *\n * @deprecated Use {@link getLatestBlock} instead.\n * @returns A promise that resolves to the latest block number.\n */\n checkForLatestBlock(): Promise<string> {\n if (!this.#pendingCheckForLatestBlock) {\n this.#pendingCheckForLatestBlock = this.#updateLatestBlock()\n .then(() => this.getLatestBlock())\n .finally(() => {\n this.#pendingCheckForLatestBlock = undefined;\n });\n }\n return this.#pendingCheckForLatestBlock;\n }\n\n #start(): void {\n // Intentionally not awaited as this starts the polling via a timeout chain.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.#updateAndQueue();\n }\n\n #end(): void {\n this.#clearPollingTimeout();\n }\n\n async #updateLatestBlock(): Promise<string> {\n // fetch + set latest block\n const latestBlock = await this.#fetchLatestBlock();\n this.#newPotentialLatest(latestBlock);\n\n if (!this.#isRunning) {\n // Ensure the one-time update is eventually reset once it's stale\n this.#setupBlockResetTimeout();\n }\n\n // _newPotentialLatest() ensures that this._currentBlock is not null\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n return this.#currentBlock!;\n }\n\n async #fetchLatestBlock(): Promise<string> {\n // If there's already a pending fetch, reuse it\n if (this.#pendingFetch) {\n return await this.#pendingFetch.promise;\n }\n\n // Create a new deferred promise for this request\n const { promise, resolve, reject } = createDeferredPromise<string>({\n suppressUnhandledRejection: true,\n });\n this.#pendingFetch = { reject, promise };\n\n try {\n const req: ExtendedJsonRpcRequest = {\n jsonrpc: '2.0',\n id: createRandomId(),\n method: 'eth_blockNumber',\n params: [] as [],\n };\n if (this.#setSkipCacheFlag) {\n req.skipCache = true;\n }\n\n log('Making request', req);\n const result = await this.#provider.request<[], string>(req);\n log('Got result', result);\n resolve(result);\n return result;\n } catch (error) {\n log('Encountered error fetching block', getErrorMessage(error));\n reject(error);\n this.#rejectPendingLatestBlock(error);\n throw error;\n } finally {\n this.#pendingFetch = undefined;\n }\n }\n\n /**\n * The core polling function that runs after each interval.\n * Updates the latest block and then queues the next update.\n */\n async #updateAndQueue(): Promise<void> {\n let interval = this.#pollingInterval;\n\n try {\n await this.#updateLatestBlock();\n } catch (error: unknown) {\n try {\n this.emit('error', error);\n } catch {\n console.error(`Error updating latest block: ${getErrorMessage(error)}`);\n }\n\n interval = this.#retryTimeout;\n }\n\n if (!this.#isRunning) {\n return;\n }\n\n this.#clearPollingTimeout();\n\n const timeoutRef = setTimeout(() => {\n // Intentionally not awaited as this just continues the polling loop.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.#updateAndQueue();\n }, interval);\n\n if (timeoutRef.unref && !this.#keepEventLoopActive) {\n timeoutRef.unref();\n }\n\n this.#pollingTimeout = timeoutRef;\n\n this.emit('_waitingForNextIteration');\n }\n\n #clearPollingTimeout(): void {\n if (this.#pollingTimeout) {\n clearTimeout(this.#pollingTimeout);\n this.#pollingTimeout = undefined;\n }\n }\n\n #addInternalListener(listener: InternalListener): void {\n this.#internalEventListeners.push(listener);\n }\n\n #removeInternalListener(listener: InternalListener): void {\n this.#internalEventListeners.splice(\n this.#internalEventListeners.indexOf(listener),\n 1,\n );\n }\n\n #rejectPendingLatestBlock(error: unknown): void {\n this.#pendingLatestBlock?.reject(error);\n this.#pendingLatestBlock = undefined;\n }\n}\n\n/**\n * Converts a number represented as a string in hexadecimal format into a native\n * number.\n *\n * @param hexInt - The hex string.\n * @returns The number.\n */\nfunction hexToInt(hexInt: string): number {\n return Number.parseInt(hexInt, 16);\n}\n"]}
@@ -0,0 +1,3 @@
1
+ export * from './PollingBlockTracker.js';
2
+ export type * from './BlockTracker.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,mBAAmB,mBAAmB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export * from './PollingBlockTracker.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC","sourcesContent":["export * from './PollingBlockTracker.js';\nexport type * from './BlockTracker.js';\n"]}
@@ -0,0 +1,4 @@
1
+ import { createModuleLogger } from '@metamask/utils';
2
+ export declare const projectLogger: import("debug").Debugger;
3
+ export { createModuleLogger };
4
+ //# sourceMappingURL=logging-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logging-utils.d.ts","sourceRoot":"","sources":["../src/logging-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAE1E,eAAO,MAAM,aAAa,0BAA2C,CAAC;AAEtE,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { createProjectLogger, createModuleLogger } from '@metamask/utils';
2
+ export const projectLogger = createProjectLogger('eth-block-tracker');
3
+ export { createModuleLogger };
4
+ //# sourceMappingURL=logging-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logging-utils.js","sourceRoot":"","sources":["../src/logging-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAE1E,MAAM,CAAC,MAAM,aAAa,GAAG,mBAAmB,CAAC,mBAAmB,CAAC,CAAC;AAEtE,OAAO,EAAE,kBAAkB,EAAE,CAAC","sourcesContent":["import { createProjectLogger, createModuleLogger } from '@metamask/utils';\n\nexport const projectLogger = createProjectLogger('eth-block-tracker');\n\nexport { createModuleLogger };\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metamask-previews/eth-block-tracker",
3
- "version": "15.0.1-preview-abca9bcea",
3
+ "version": "16.0.0-preview-0a30e47",
4
4
  "description": "A block tracker for the Ethereum blockchain. Keeps track of the latest block",
5
5
  "keywords": [
6
6
  "Ethereum",
@@ -19,20 +19,12 @@
19
19
  "dist",
20
20
  "dist/"
21
21
  ],
22
+ "type": "module",
22
23
  "sideEffects": false,
23
- "main": "./dist/index.cjs",
24
- "module": "./dist/index.mjs",
25
- "types": "./dist/index.d.cts",
26
24
  "exports": {
27
25
  ".": {
28
- "import": {
29
- "types": "./dist/index.d.mts",
30
- "default": "./dist/index.mjs"
31
- },
32
- "require": {
33
- "types": "./dist/index.d.cts",
34
- "default": "./dist/index.cjs"
35
- }
26
+ "types": "./dist/index.d.ts",
27
+ "default": "./dist/index.js"
36
28
  },
37
29
  "./package.json": "./package.json"
38
30
  },
@@ -41,9 +33,11 @@
41
33
  "registry": "https://registry.npmjs.org/"
42
34
  },
43
35
  "scripts": {
44
- "build": "ts-bridge --project tsconfig.build.json --verbose --clean --no-references",
45
- "build:all": "ts-bridge --project tsconfig.build.json --verbose --clean",
36
+ "build": "tsc --project tsconfig.build.json",
37
+ "build:all": "tsc --build tsconfig.build.json --verbose",
38
+ "build:clean": "yarn build:only-clean && yarn build",
46
39
  "build:docs": "typedoc",
40
+ "build:only-clean": "rimraf './dist' './tsconfig.build.tsbuildinfo'",
47
41
  "changelog:update": "../../scripts/update-changelog.sh @metamask/eth-block-tracker",
48
42
  "changelog:validate": "../../scripts/validate-changelog.sh @metamask/eth-block-tracker",
49
43
  "lint:tsconfigs": "tsx ../../scripts/lint-tsconfigs/lint-tsconfigs.mts",
@@ -55,26 +49,27 @@
55
49
  "test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch"
56
50
  },
57
51
  "dependencies": {
58
- "@metamask/eth-json-rpc-provider": "^6.0.1",
59
- "@metamask/safe-event-emitter": "^3.0.0",
52
+ "@metamask/eth-json-rpc-provider": "^7.0.0",
53
+ "@metamask/safe-event-emitter": "^3.1.3",
60
54
  "@metamask/utils": "^11.12.0",
61
55
  "json-rpc-random-id": "^1.0.1"
62
56
  },
63
57
  "devDependencies": {
64
58
  "@metamask/auto-changelog": "^6.1.0",
65
- "@metamask/json-rpc-engine": "^10.5.0",
66
- "@ts-bridge/cli": "^0.6.4",
59
+ "@metamask/json-rpc-engine": "^11.0.0",
67
60
  "@types/jest": "^30.0.0",
68
61
  "@types/json-rpc-random-id": "^1.0.1",
62
+ "@typescript/native": "npm:typescript@^7.0.2",
69
63
  "deepmerge": "^4.2.2",
70
64
  "jest": "^30.4.2",
65
+ "rimraf": "^5.0.5",
71
66
  "ts-jest": "^29.4.11",
72
67
  "tsx": "^4.20.5",
73
68
  "typedoc": "^0.25.13",
74
69
  "typedoc-plugin-missing-exports": "^2.0.0",
75
- "typescript": "~5.3.3"
70
+ "typescript": "npm:@typescript/typescript6@^6.0.2"
76
71
  },
77
72
  "engines": {
78
- "node": "^18.18 || >=20"
73
+ "node": "^22.14.0 || ^24"
79
74
  }
80
75
  }
@@ -1,3 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- //# sourceMappingURL=BlockTracker.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"BlockTracker.cjs","sourceRoot":"","sources":["../src/BlockTracker.ts"],"names":[],"mappings":"","sourcesContent":["import type SafeEventEmitter from '@metamask/safe-event-emitter';\n\nexport type BlockTracker = SafeEventEmitter & {\n destroy(): Promise<void>;\n\n isRunning(): boolean;\n\n getCurrentBlock(): string | null;\n\n getLatestBlock(): Promise<string>;\n\n checkForLatestBlock(): Promise<string>;\n};\n"]}
@@ -1,9 +0,0 @@
1
- import type SafeEventEmitter from "@metamask/safe-event-emitter";
2
- export type BlockTracker = SafeEventEmitter & {
3
- destroy(): Promise<void>;
4
- isRunning(): boolean;
5
- getCurrentBlock(): string | null;
6
- getLatestBlock(): Promise<string>;
7
- checkForLatestBlock(): Promise<string>;
8
- };
9
- //# sourceMappingURL=BlockTracker.d.cts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"BlockTracker.d.cts","sourceRoot":"","sources":["../src/BlockTracker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,gBAAgB,qCAAqC;AAEjE,MAAM,MAAM,YAAY,GAAG,gBAAgB,GAAG;IAC5C,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEzB,SAAS,IAAI,OAAO,CAAC;IAErB,eAAe,IAAI,MAAM,GAAG,IAAI,CAAC;IAEjC,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAElC,mBAAmB,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CACxC,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"BlockTracker.d.mts","sourceRoot":"","sources":["../src/BlockTracker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,gBAAgB,qCAAqC;AAEjE,MAAM,MAAM,YAAY,GAAG,gBAAgB,GAAG;IAC5C,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEzB,SAAS,IAAI,OAAO,CAAC;IAErB,eAAe,IAAI,MAAM,GAAG,IAAI,CAAC;IAEjC,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAElC,mBAAmB,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CACxC,CAAC"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=BlockTracker.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"BlockTracker.mjs","sourceRoot":"","sources":["../src/BlockTracker.ts"],"names":[],"mappings":"","sourcesContent":["import type SafeEventEmitter from '@metamask/safe-event-emitter';\n\nexport type BlockTracker = SafeEventEmitter & {\n destroy(): Promise<void>;\n\n isRunning(): boolean;\n\n getCurrentBlock(): string | null;\n\n getLatestBlock(): Promise<string>;\n\n checkForLatestBlock(): Promise<string>;\n};\n"]}