@metamask/eth-block-tracker 12.0.1 → 12.2.0

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 ADDED
@@ -0,0 +1,333 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [12.2.0]
11
+
12
+ ### Changed
13
+
14
+ - `PollingBlockTracker.getLatestBlock()` now accepts an optional parameter `useCache` ([#340](https://github.com/MetaMask/eth-block-tracker/pull/340))
15
+ - This option defaults to `true`, but when `false`, it ignores the cached block number and instead updates and returns a new block number, ensuring that the frequency of requests is limited to the `pollingInterval` period
16
+
17
+ ## [12.1.0]
18
+
19
+ ### Changed
20
+
21
+ - Bump `@metamask/eth-json-rpc-provider` from `^4.1.5` to `^5.0.0` ([#334](https://github.com/MetaMask/eth-block-tracker/pull/334))
22
+
23
+ ## [12.0.1]
24
+
25
+ ### Fixed
26
+
27
+ - Fixed hanging `getLatestBlock()` promises when block tracker is stopped before request completion ([#320](https://github.com/MetaMask/eth-block-tracker/pull/320))
28
+ - Pending `getLatestBlock()` requests are now properly rejected with "Block tracker destroyed" error when the tracker is stopped
29
+
30
+ ## [12.0.0]
31
+
32
+ ### Changed
33
+
34
+ - Errors that occur while polling are no longer wrapped ([#310](https://github.com/MetaMask/eth-block-tracker/pull/310))
35
+
36
+ ### Removed
37
+
38
+ - **BREAKING:** Remove `SubscribeBlockTracker` ([#309](https://github.com/MetaMask/eth-block-tracker/pull/309))
39
+ - Although we continue to maintain this, we have not used it internally for quite some time. In general we have found a polling-based approval to be reliable than a subscription-based approach. We recommend using `PollingBlockTracker` instead.
40
+
41
+ ### Fixed
42
+
43
+ - Fix `PollingBlockTracker.getLatestBlock` so that it throws an error encountered while making the request instead of hanging, regardless of whether the request occurs inside or outside of a polling loop ([#313](https://github.com/MetaMask/eth-block-tracker/pull/313))
44
+ - Fix `PollingBlockTracker.getLatestBlock` so that if invoked while a previous invocation is pending, it will throw if that invocation also throws ([#313](https://github.com/MetaMask/eth-block-tracker/pull/313))
45
+
46
+ ## [11.0.4]
47
+
48
+ ### Changed
49
+
50
+ - Bump `@metamask/utils` from `^9.1.0` to `^11.0.1` ([#297](https://github.com/MetaMask/eth-block-tracker/pull/297))
51
+
52
+ ## [11.0.3]
53
+
54
+ ### Fixed
55
+
56
+ - Avoid risk of infinite retry loops when fetching new blocks ([#284](https://github.com/MetaMask/eth-block-tracker/pull/284))
57
+ - When the provider returns an error and `PollingBlockTracker` or `SubscribeBlockTracker` is destroyed, the promise returned by the `getLatestBlock` method will be rejected.
58
+
59
+ ## [11.0.2]
60
+
61
+ ### Fixed
62
+
63
+ - Bump `@metamask/eth-json-rpc-provider` from `^4.1.1` to `^4.1.5` ([#273](https://github.com/MetaMask/eth-block-tracker/pull/273))
64
+
65
+ ## [11.0.1]
66
+
67
+ ### Changed
68
+
69
+ - Bump `@metamask/eth-json-rpc-provider` from `^4.1.0` to `^4.1.1` ([#261](https://github.com/MetaMask/eth-block-tracker/pull/261))
70
+ - Bump `@metamask/safe-event-emitter` from `^3.0.0` to `^3.1.1` ([#261](https://github.com/MetaMask/eth-block-tracker/pull/261))
71
+ - Bump `@metamask/utils` from `^8.1.0` to `^9.1.0` ([#261](https://github.com/MetaMask/eth-block-tracker/pull/261))
72
+
73
+ ## [11.0.0]
74
+
75
+ ### Changed
76
+
77
+ - **BREAKING**: Adapt to EIP-1193 provider changes by using `request` method of provider instead of `sendAsync` ([#252](https://github.com/MetaMask/eth-block-tracker/pull/252))
78
+ - This change included in `10.1.0` was mistakenly categorised as a non-breaking change.
79
+ - This requires provider object to have the `request` method. You need to upgrade to minimum `4.1.0` of `@metamask/eth-json-rpc-provider` so that the `request` method is available.
80
+
81
+ ## [10.1.0] [DEPRECATED]
82
+
83
+ ### Changed
84
+
85
+ - Adapt to EIP-1193 provider changes ([#252](https://github.com/MetaMask/eth-block-tracker/pull/252))
86
+ - Deprecated `sendAsync` method is replaced with `request` method
87
+ - Bump `@metamask/eth-json-rpc-provider` from `^4.0.0` to `^4.1.0` ([#252](https://github.com/MetaMask/eth-block-tracker/pull/252))
88
+
89
+ ## [10.0.0]
90
+
91
+ ### Changed
92
+
93
+ - BREAKING: Drop support for Node.js v16; add support for Node.js v20, v22 ([#245](https://github.com/MetaMask/eth-block-tracker/pull/245))
94
+ - Update `@metamask/eth-json-rpc-provider` from `^3.0.2` to `^4.0.0` ([#247](https://github.com/MetaMask/eth-block-tracker/pull/247))
95
+
96
+ ## [9.0.3]
97
+
98
+ ### Fixed
99
+
100
+ - Update dependency `@metamask/eth-json-rpc-engine` from `^7.1.1` to `^8.0.2` ([#235](https://github.com/MetaMask/eth-block-tracker/pull/235))
101
+ - Update dependency `@metamask/eth-json-rpc-provider` from `^2.3.1` to `^3.0.2` ([#235](https://github.com/MetaMask/eth-block-tracker/pull/235))
102
+
103
+ ## [9.0.2]
104
+
105
+ ### Fixed
106
+
107
+ - Put back missing empty `params` to `eth_blockNumber` call removed in 9.0.1 ([#198](https://github.com/MetaMask/eth-block-tracker/pull/198))
108
+
109
+ ## [9.0.1]
110
+
111
+ ### Fixed
112
+
113
+ - Concurrency fix: Prevent multiple simultaneous polling loops ([#208](https://github.com/MetaMask/eth-block-tracker/pull/208))
114
+ - Bump `@metamask/eth-json-rpc-provider` from `^2.1.0` to `^2.3.1` ([#198](https://github.com/MetaMask/eth-block-tracker/pull/198)) ([#217](https://github.com/MetaMask/eth-block-tracker/pull/217))
115
+
116
+ ## [9.0.0]
117
+
118
+ ### Changed
119
+
120
+ - Rename package from `eth-block-tracker` to `@metamask/eth-block-tracker` ([#183](https://github.com/MetaMask/eth-block-tracker/pull/183))
121
+
122
+ ## [8.1.0]
123
+
124
+ ### Changed
125
+
126
+ - Typescript: Add `checkForLatestBlock` function to `BlockTracker` interface ([#184](https://github.com/MetaMask/eth-block-tracker/pull/184))
127
+
128
+ ## [8.0.0]
129
+
130
+ ### Changed
131
+
132
+ - Bump @metamask/utils from ^5.0.2 to ^8.1.0 ([#178](https://github.com/MetaMask/eth-block-tracker/pull/178))
133
+ - **BREAKING**: Bump @metamask/eth-json-rpc-provider from ^1.0.0 to ^2.1.0 ([#174](https://github.com/MetaMask/eth-block-tracker/pull/174))
134
+ - **BREAKING**: Increase minimum Node.js version to 16 ([#173](https://github.com/MetaMask/eth-block-tracker/pull/173))
135
+
136
+ ## [7.2.0]
137
+
138
+ ### Added
139
+
140
+ - Typescript: Export `BlockTracker` interface ([#168](https://github.com/MetaMask/eth-block-tracker/pull/168))
141
+
142
+ ### Changed
143
+
144
+ - Dependency Updates: ([#165](https://github.com/MetaMask/eth-block-tracker/pull/165))
145
+ - Bump pify from ^3.0.0 to ^5.0.0
146
+ - Bump @metamask/utils from ^5.0.1 to ^5.0.2
147
+
148
+ ## [7.1.0]
149
+
150
+ ### Added
151
+
152
+ - Add `usePastBlocks` to constructor ([#151](https://github.com/MetaMask/eth-block-tracker/pull/151))
153
+ - Optional flag. When set to true, it allows blocks less than the current block number to be cached and returned.
154
+
155
+ ## [7.0.1]
156
+
157
+ ### Changed
158
+
159
+ - Dependency updates:
160
+ - Bump @metamask/utils from 5.0.1 to 5.0.2
161
+ - [#141](https://github.com/MetaMask/eth-block-tracker/pull/141)
162
+ - [#144](https://github.com/MetaMask/eth-block-tracker/pull/144)
163
+ - Bump @metamask/safe-event-emitter from 2.0.0 to 3.0.0 ([#143](https://github.com/MetaMask/eth-block-tracker/pull/143))
164
+
165
+ ## [7.0.0]
166
+
167
+ ### Changed
168
+
169
+ - **BREAKING:** The type of the `provider` option for `PollingBlockTracker` and `SubscribeBlockTracker` has changed ([#130](https://github.com/MetaMask/eth-block-tracker/pull/130))
170
+ - The `provider` option must be compatible with the `SafeEventEmitterProvider` type from `@metamask/eth-json-rpc-middleware`.
171
+ - The new provider type should be mostly equivalent, except that it's now expected to have a `send` method. We don't use that `send` method in this package though.
172
+
173
+ ### Removed
174
+
175
+ - **BREAKING:** Remove the `Provider` exported type ([#130](https://github.com/MetaMask/eth-block-tracker/pull/130))
176
+ - We now use `@metamask/eth-json-rpc-provider` for this instead, so there was no need to re-export it.
177
+
178
+ ## [6.1.0]
179
+
180
+ ### Added
181
+
182
+ - Add back Provider type that was accidentally removed in 6.0.0 ([#117](https://github.com/MetaMask/eth-block-tracker/pull/117))
183
+
184
+ ### Fixed
185
+
186
+ - Align Provider type with `eth-json-rpc-middleware` to prevent typecasting ([#117](https://github.com/MetaMask/eth-block-tracker/pull/117))
187
+
188
+ ## [6.0.0]
189
+
190
+ ### Added
191
+
192
+ - Add logging ([#112](https://github.com/MetaMask/eth-block-tracker/pull/112))
193
+ - You will not be able to see log messages by default, but you can turn them on for this library by setting the `DEBUG` environment variable to `metamask:eth-block-tracker:*` or `metamask:*`.
194
+ - Add `destroy` method to block tracker classes ([#106](https://github.com/MetaMask/eth-block-tracker/pull/106))
195
+ - Update PollingBlockTracker to support new `blockResetDuration` option ([#103](https://github.com/MetaMask/eth-block-tracker/pull/103))
196
+ - Expose types that represent options to PollingBlockTracker and SubscribeBlockTracker constructors ([#103](https://github.com/MetaMask/eth-block-tracker/pull/103))
197
+
198
+ ### Changed
199
+
200
+ - **BREAKING:** Require Node >= 14 ([#113](https://github.com/MetaMask/eth-block-tracker/pull/113))
201
+ - **BREAKING:** Make BaseBlockTracker abstract ([#103](https://github.com/MetaMask/eth-block-tracker/pull/103))
202
+ - If you are using this class directly, you must only use PollingBlockTracker or SubscribeBlockTracker.
203
+ - **BREAKING:** Make options for BaseBlockTracker required ([#103](https://github.com/MetaMask/eth-block-tracker/pull/103))
204
+ - Subclasses must pass a set of options to `super` in their constructors.
205
+ - Make argument to `removeAllListeners` in BaseBlockTracker optional ([#103](https://github.com/MetaMask/eth-block-tracker/pull/103))
206
+ - **BREAKING:** Update signatures for `_start` and `_end` in BaseBlockTracker ([#103](https://github.com/MetaMask/eth-block-tracker/pull/103))
207
+ - Subclasses must provide an implementation for both of these methods; they are no longer no-ops.
208
+ - Both methods must return a promise.
209
+ - Update SubscribeBlockTracker to not pass empty `newHeads` parameter to `eth_subscribe` call ([#108](https://github.com/MetaMask/eth-block-tracker/pull/108))
210
+ - This change was made because OpenEthereum does not support this parameter. While we've done our best to confirm that this will not be a breaking change for other Ethereum implementations, you will want to confirm no breakages for yours.
211
+
212
+ ### Security
213
+
214
+ - Add `@lavamoat/allow-scripts` to ensure that install scripts are opt-in for dependencies ([#97](https://github.com/MetaMask/eth-block-tracker/pull/97))
215
+
216
+ ## [5.0.1] - 2021-03-25
217
+
218
+ ### Fixed
219
+
220
+ - Add missing `types` field to `package.json` ([#75](https://github.com/MetaMask/eth-block-tracker/pull/75))
221
+
222
+ ## [5.0.0] - 2021-03-25
223
+
224
+ ### Changed
225
+
226
+ - **(BREAKING)** Refactor exports ([#71](https://github.com/MetaMask/eth-block-tracker/pull/71))
227
+ - **(BREAKING)** Target ES2017, remove ES5 builds ([#71](https://github.com/MetaMask/eth-block-tracker/pull/71))
228
+ - Migrate to TypeScript ([#71](https://github.com/MetaMask/eth-block-tracker/pull/71))
229
+ - Update various dependencies ([#44](https://github.com/MetaMask/eth-block-tracker/pull/44), [#49](https://github.com/MetaMask/eth-block-tracker/pull/49), [#54](https://github.com/MetaMask/eth-block-tracker/pull/54), [#59](https://github.com/MetaMask/eth-block-tracker/pull/59), [#61](https://github.com/MetaMask/eth-block-tracker/pull/61), [#62](https://github.com/MetaMask/eth-block-tracker/pull/62), [#63](https://github.com/MetaMask/eth-block-tracker/pull/63), [#70](https://github.com/MetaMask/eth-block-tracker/pull/70), [#72](https://github.com/MetaMask/eth-block-tracker/pull/72))
230
+
231
+ ### Removed
232
+
233
+ - Remove unused production dependencies ([#60](https://github.com/MetaMask/eth-block-tracker/pull/60), [#68](https://github.com/MetaMask/eth-block-tracker/pull/68))
234
+
235
+ ## [4.4.3] - 2019-08-30
236
+
237
+ ### Added
238
+
239
+ - Add SubscribeBlockTracker
240
+
241
+ ### Changed
242
+
243
+ - Change events so that they now only return the block number (internal polling is done via `eth_blockNumber`)
244
+ - Add `retryTimeout` and `keepEventLoopActive` to constructor
245
+ - Update block trackers to inherit from `safe-event-emitter` rather than EventEmitter
246
+
247
+ ### Removed
248
+
249
+ - Remove `block` event
250
+ - Please use `latest` or `sync`.
251
+
252
+ ## [4.0.0] - 2018-04-26
253
+
254
+ ### Added
255
+
256
+ - Add isRunning method
257
+ - Add `error` event
258
+
259
+ ### Changed
260
+
261
+ - Significantly rewrite `eth-block-tracker` (primarily due to optimizing network IO)
262
+ - Rename `awaitCurrentBlock` to `getLatestBlock`
263
+
264
+ ### Removed
265
+
266
+ - Remove `stop`/`start` methods from BlockTrackers
267
+ - BlockTrackers now automatically start and stop based on listener count for the `latest` and `sync` events. You can force a stop by calling the `EventEmitter` method `removeAllListeners`.
268
+ - Remove tx body from block
269
+ - Remove getTrackingBlock
270
+ - Remove start/stop
271
+ - Remove test/util/testBlockMiddleware
272
+
273
+ ## [3.0.0] - 2018-04-16
274
+
275
+ ### Changed
276
+
277
+ - Update published version so main module now exports unprocessed source
278
+ - Module includes dist:
279
+ - Bundle: `dist/EthBlockTracker.js`
280
+ - ES5 source: `dist/es5/`
281
+ - Rename `lib` to `src`
282
+ - Update RpcBlockTracker to be a normal `EventEmitter`
283
+ - It no longer provides a callback to event handlers.
284
+
285
+ ### Fixed
286
+
287
+ - Fix `awaitCurrentBlock` return value
288
+
289
+ ## [2.0.0] - 2017-06-14
290
+
291
+ ### Added
292
+
293
+ - Expose EventEmitter interface (via `async-eventemitter`)
294
+ - Add `getTrackingBlock`, `getCurrentBlock`, `start`, and `stop`
295
+ - Add events: `block`, `latest`, `sync`
296
+
297
+ ## [1.0.0] - 2017-02-03
298
+
299
+ ### Added
300
+
301
+ - Add RpcBlockTracker
302
+
303
+ [Unreleased]: https://github.com/MetaMask/eth-block-tracker/compare/v12.2.0...HEAD
304
+ [12.2.0]: https://github.com/MetaMask/eth-block-tracker/compare/v12.1.0...v12.2.0
305
+ [12.1.0]: https://github.com/MetaMask/eth-block-tracker/compare/v12.0.1...v12.1.0
306
+ [12.0.1]: https://github.com/MetaMask/eth-block-tracker/compare/v12.0.0...v12.0.1
307
+ [12.0.0]: https://github.com/MetaMask/eth-block-tracker/compare/v11.0.4...v12.0.0
308
+ [11.0.4]: https://github.com/MetaMask/eth-block-tracker/compare/v11.0.3...v11.0.4
309
+ [11.0.3]: https://github.com/MetaMask/eth-block-tracker/compare/v11.0.2...v11.0.3
310
+ [11.0.2]: https://github.com/MetaMask/eth-block-tracker/compare/v11.0.1...v11.0.2
311
+ [11.0.1]: https://github.com/MetaMask/eth-block-tracker/compare/v11.0.0...v11.0.1
312
+ [11.0.0]: https://github.com/MetaMask/eth-block-tracker/compare/v10.1.0...v11.0.0
313
+ [10.1.0]: https://github.com/MetaMask/eth-block-tracker/compare/v10.0.0...v10.1.0
314
+ [10.0.0]: https://github.com/MetaMask/eth-block-tracker/compare/v9.0.3...v10.0.0
315
+ [9.0.3]: https://github.com/MetaMask/eth-block-tracker/compare/v9.0.2...v9.0.3
316
+ [9.0.2]: https://github.com/MetaMask/eth-block-tracker/compare/v9.0.1...v9.0.2
317
+ [9.0.1]: https://github.com/MetaMask/eth-block-tracker/compare/v9.0.0...v9.0.1
318
+ [9.0.0]: https://github.com/MetaMask/eth-block-tracker/compare/v8.1.0...v9.0.0
319
+ [8.1.0]: https://github.com/MetaMask/eth-block-tracker/compare/v8.0.0...v8.1.0
320
+ [8.0.0]: https://github.com/MetaMask/eth-block-tracker/compare/v7.2.0...v8.0.0
321
+ [7.2.0]: https://github.com/MetaMask/eth-block-tracker/compare/v7.1.0...v7.2.0
322
+ [7.1.0]: https://github.com/MetaMask/eth-block-tracker/compare/v7.0.1...v7.1.0
323
+ [7.0.1]: https://github.com/MetaMask/eth-block-tracker/compare/v7.0.0...v7.0.1
324
+ [7.0.0]: https://github.com/MetaMask/eth-block-tracker/compare/v6.1.0...v7.0.0
325
+ [6.1.0]: https://github.com/MetaMask/eth-block-tracker/compare/v6.0.0...v6.1.0
326
+ [6.0.0]: https://github.com/MetaMask/eth-block-tracker/compare/v5.0.1...v6.0.0
327
+ [5.0.1]: https://github.com/MetaMask/eth-block-tracker/compare/v5.0.0...v5.0.1
328
+ [5.0.0]: https://github.com/MetaMask/eth-block-tracker/compare/v4.4.3...v5.0.0
329
+ [4.4.3]: https://github.com/MetaMask/eth-block-tracker/compare/v4.0.0...v4.4.3
330
+ [4.0.0]: https://github.com/MetaMask/eth-block-tracker/compare/v3.0.0...v4.0.0
331
+ [3.0.0]: https://github.com/MetaMask/eth-block-tracker/compare/v2.0.0...v3.0.0
332
+ [2.0.0]: https://github.com/MetaMask/eth-block-tracker/compare/v1.0.0...v2.0.0
333
+ [1.0.0]: https://github.com/MetaMask/eth-block-tracker/releases/tag/v1.0.0
@@ -27,7 +27,9 @@ export declare class PollingBlockTracker extends SafeEventEmitter implements Blo
27
27
  destroy(): Promise<void>;
28
28
  isRunning(): boolean;
29
29
  getCurrentBlock(): string | null;
30
- getLatestBlock(): Promise<string>;
30
+ getLatestBlock({ useCache, }?: {
31
+ useCache?: boolean;
32
+ }): Promise<string>;
31
33
  removeAllListeners(eventName?: string | symbol): this;
32
34
  private _setupInternalEvents;
33
35
  private _onNewListener;
@@ -41,6 +43,13 @@ export declare class PollingBlockTracker extends SafeEventEmitter implements Blo
41
43
  private _setupBlockResetTimeout;
42
44
  private _cancelBlockResetTimeout;
43
45
  private _resetCurrentBlock;
46
+ /**
47
+ * Checks for the latest block, updates the internal state, and returns the
48
+ * value immediately rather than waiting for the next polling interval.
49
+ *
50
+ * @deprecated Use {@link getLatestBlock} instead.
51
+ * @returns A promise that resolves to the latest block number.
52
+ */
44
53
  checkForLatestBlock(): Promise<string>;
45
54
  private _start;
46
55
  private _end;
@@ -66,9 +66,9 @@ class PollingBlockTracker extends safe_event_emitter_1.default {
66
66
  getCurrentBlock() {
67
67
  return this._currentBlock;
68
68
  }
69
- async getLatestBlock() {
69
+ async getLatestBlock({ useCache = true, } = {}) {
70
70
  // return if available
71
- if (this._currentBlock) {
71
+ if (this._currentBlock && useCache) {
72
72
  return this._currentBlock;
73
73
  }
74
74
  if (__classPrivateFieldGet(this, _PollingBlockTracker_pendingLatestBlock, "f")) {
@@ -78,30 +78,45 @@ class PollingBlockTracker extends safe_event_emitter_1.default {
78
78
  suppressUnhandledRejection: true,
79
79
  });
80
80
  __classPrivateFieldSet(this, _PollingBlockTracker_pendingLatestBlock, { reject, promise }, "f");
81
- try {
81
+ if (this._isRunning) {
82
+ try {
83
+ // If tracker is running, wait for next block with timeout
84
+ const onLatestBlock = (value) => {
85
+ __classPrivateFieldGet(this, _PollingBlockTracker_instances, "m", _PollingBlockTracker_removeInternalListener).call(this, onLatestBlock);
86
+ this.removeListener('latest', onLatestBlock);
87
+ resolve(value);
88
+ };
89
+ __classPrivateFieldGet(this, _PollingBlockTracker_instances, "m", _PollingBlockTracker_addInternalListener).call(this, onLatestBlock);
90
+ this.once('latest', onLatestBlock);
91
+ return await promise;
92
+ }
93
+ catch (error) {
94
+ reject(error);
95
+ throw error;
96
+ }
97
+ finally {
98
+ __classPrivateFieldSet(this, _PollingBlockTracker_pendingLatestBlock, undefined, "f");
99
+ }
100
+ }
101
+ else {
82
102
  // If tracker isn't running, just fetch directly
83
- if (!this._isRunning) {
84
- const latestBlock = await this._fetchLatestBlock();
85
- this._newPotentialLatest(latestBlock);
103
+ try {
104
+ const latestBlock = await this._updateLatestBlock();
86
105
  resolve(latestBlock);
87
106
  return latestBlock;
88
107
  }
89
- // If tracker is running, wait for next block with timeout
90
- const onLatestBlock = (value) => {
91
- __classPrivateFieldGet(this, _PollingBlockTracker_instances, "m", _PollingBlockTracker_removeInternalListener).call(this, onLatestBlock);
92
- this.removeListener('latest', onLatestBlock);
93
- resolve(value);
94
- };
95
- __classPrivateFieldGet(this, _PollingBlockTracker_instances, "m", _PollingBlockTracker_addInternalListener).call(this, onLatestBlock);
96
- this.once('latest', onLatestBlock);
97
- return await promise;
98
- }
99
- catch (error) {
100
- reject(error);
101
- throw error;
102
- }
103
- finally {
104
- __classPrivateFieldSet(this, _PollingBlockTracker_pendingLatestBlock, undefined, "f");
108
+ catch (error) {
109
+ reject(error);
110
+ throw error;
111
+ }
112
+ finally {
113
+ // We want to rate limit calls to this method if we made a direct fetch
114
+ // for the block number because the BlockTracker was not running. We
115
+ // achieve this by delaying the unsetting of the #pendingLatestBlock promise.
116
+ setTimeout(() => {
117
+ __classPrivateFieldSet(this, _PollingBlockTracker_pendingLatestBlock, undefined, "f");
118
+ }, this._pollingInterval);
119
+ }
105
120
  }
106
121
  }
107
122
  // dont allow module consumer to remove our internal event listeners
@@ -208,6 +223,13 @@ class PollingBlockTracker extends safe_event_emitter_1.default {
208
223
  _resetCurrentBlock() {
209
224
  this._currentBlock = null;
210
225
  }
226
+ /**
227
+ * Checks for the latest block, updates the internal state, and returns the
228
+ * value immediately rather than waiting for the next polling interval.
229
+ *
230
+ * @deprecated Use {@link getLatestBlock} instead.
231
+ * @returns A promise that resolves to the latest block number.
232
+ */
211
233
  async checkForLatestBlock() {
212
234
  await this._updateLatestBlock();
213
235
  return await this.getLatestBlock();
@@ -224,6 +246,9 @@ class PollingBlockTracker extends safe_event_emitter_1.default {
224
246
  // fetch + set latest block
225
247
  const latestBlock = await this._fetchLatestBlock();
226
248
  this._newPotentialLatest(latestBlock);
249
+ // _newPotentialLatest() ensures that this._currentBlock is not null
250
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
251
+ return this._currentBlock;
227
252
  }
228
253
  async _fetchLatestBlock() {
229
254
  // If there's already a pending fetch, reuse it
@@ -1 +1 @@
1
- {"version":3,"file":"PollingBlockTracker.js","sourceRoot":"","sources":["../src/PollingBlockTracker.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SafeEventEmitterProvider } from '@metamask/eth-json-rpc-provider';\nimport SafeEventEmitter from '@metamask/safe-event-emitter';\nimport {\n createDeferredPromise,\n type DeferredPromise,\n getErrorMessage,\n type JsonRpcRequest,\n} from '@metamask/utils';\nimport getCreateRandomId from 'json-rpc-random-id';\n\nimport type { BlockTracker } from './BlockTracker';\nimport { projectLogger, createModuleLogger } from './logging-utils';\n\nconst log = createModuleLogger(projectLogger, 'polling-block-tracker');\nconst createRandomId = getCreateRandomId();\nconst sec = 1000;\n\nconst blockTrackerEvents: (string | symbol)[] = ['sync', 'latest'];\n\nexport interface PollingBlockTrackerOptions {\n provider?: SafeEventEmitterProvider;\n pollingInterval?: number;\n retryTimeout?: number;\n keepEventLoopActive?: boolean;\n setSkipCacheFlag?: boolean;\n blockResetDuration?: number;\n usePastBlocks?: boolean;\n}\n\ninterface ExtendedJsonRpcRequest extends JsonRpcRequest<[]> {\n skipCache?: boolean;\n}\n\ntype InternalListener = (value: string) => void;\n\nexport class PollingBlockTracker\n extends SafeEventEmitter\n implements BlockTracker\n{\n private _isRunning: boolean;\n\n private readonly _blockResetDuration: number;\n\n private readonly _usePastBlocks: boolean;\n\n private _currentBlock: string | null;\n\n private _blockResetTimeout?: ReturnType<typeof setTimeout>;\n\n private _pollingTimeout?: ReturnType<typeof setTimeout>;\n\n private readonly _provider: SafeEventEmitterProvider;\n\n private readonly _pollingInterval: number;\n\n private readonly _retryTimeout: number;\n\n private readonly _keepEventLoopActive: boolean;\n\n private readonly _setSkipCacheFlag: boolean;\n\n readonly #internalEventListeners: InternalListener[] = [];\n\n #pendingLatestBlock?: Omit<DeferredPromise<string>, 'resolve'>;\n\n #pendingFetch?: Omit<DeferredPromise<string>, 'resolve'>;\n\n constructor(opts: PollingBlockTrackerOptions = {}) {\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._onNewListener.bind(this);\n this._onRemoveListener = this._onRemoveListener.bind(this);\n this._resetCurrentBlock = this._resetCurrentBlock.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 =\n opts.keepEventLoopActive === undefined ? true : opts.keepEventLoopActive;\n this._setSkipCacheFlag = opts.setSkipCacheFlag || false;\n }\n\n async destroy() {\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(): Promise<string> {\n // return if available\n if (this._currentBlock) {\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 try {\n // If tracker isn't running, just fetch directly\n if (!this._isRunning) {\n const latestBlock = await this._fetchLatestBlock();\n this._newPotentialLatest(latestBlock);\n resolve(latestBlock);\n return latestBlock;\n }\n\n // If tracker is running, wait for next block with timeout\n const onLatestBlock = (value: string) => {\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 }\n\n // dont allow module consumer to remove our internal event listeners\n removeAllListeners(eventName?: string | symbol) {\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 private _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 private _onNewListener(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 private _onRemoveListener(): void {\n // `removeListener` is called *after* the listener is removed\n if (this._getBlockTrackerEventCount() > 0) {\n return;\n }\n this._maybeEnd();\n }\n\n private _maybeStart() {\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 private _maybeEnd() {\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 private _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 private _shouldUseNewBlock(newBlock: string) {\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 private _newPotentialLatest(newBlock: string): void {\n if (!this._shouldUseNewBlock(newBlock)) {\n return;\n }\n this._setCurrentBlock(newBlock);\n }\n\n private _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 private _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 private _cancelBlockResetTimeout(): void {\n if (this._blockResetTimeout) {\n clearTimeout(this._blockResetTimeout);\n }\n }\n\n private _resetCurrentBlock(): void {\n this._currentBlock = null;\n }\n\n async checkForLatestBlock() {\n await this._updateLatestBlock();\n return await this.getLatestBlock();\n }\n\n private _start() {\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 private _end() {\n this._clearPollingTimeout();\n }\n\n private async _updateLatestBlock(): Promise<void> {\n // fetch + set latest block\n const latestBlock = await this._fetchLatestBlock();\n this._newPotentialLatest(latestBlock);\n }\n\n private 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 private async _updateAndQueue() {\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() {\n if (this._pollingTimeout) {\n clearTimeout(this._pollingTimeout);\n this._pollingTimeout = undefined;\n }\n }\n\n #addInternalListener(listener: InternalListener) {\n this.#internalEventListeners.push(listener);\n }\n\n #removeInternalListener(listener: InternalListener) {\n this.#internalEventListeners.splice(\n this.#internalEventListeners.indexOf(listener),\n 1,\n );\n }\n\n #rejectPendingLatestBlock(error: unknown) {\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"]}
1
+ {"version":3,"file":"PollingBlockTracker.js","sourceRoot":"","sources":["../src/PollingBlockTracker.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SafeEventEmitterProvider } from '@metamask/eth-json-rpc-provider';\nimport SafeEventEmitter from '@metamask/safe-event-emitter';\nimport {\n createDeferredPromise,\n type DeferredPromise,\n getErrorMessage,\n type JsonRpcRequest,\n} from '@metamask/utils';\nimport getCreateRandomId from 'json-rpc-random-id';\n\nimport type { BlockTracker } from './BlockTracker';\nimport { projectLogger, createModuleLogger } from './logging-utils';\n\nconst log = createModuleLogger(projectLogger, 'polling-block-tracker');\nconst createRandomId = getCreateRandomId();\nconst sec = 1000;\n\nconst blockTrackerEvents: (string | symbol)[] = ['sync', 'latest'];\n\nexport interface PollingBlockTrackerOptions {\n provider?: SafeEventEmitterProvider;\n pollingInterval?: number;\n retryTimeout?: number;\n keepEventLoopActive?: boolean;\n setSkipCacheFlag?: boolean;\n blockResetDuration?: number;\n usePastBlocks?: boolean;\n}\n\ninterface ExtendedJsonRpcRequest extends JsonRpcRequest<[]> {\n skipCache?: boolean;\n}\n\ntype InternalListener = (value: string) => void;\n\nexport class PollingBlockTracker\n extends SafeEventEmitter\n implements BlockTracker\n{\n private _isRunning: boolean;\n\n private readonly _blockResetDuration: number;\n\n private readonly _usePastBlocks: boolean;\n\n private _currentBlock: string | null;\n\n private _blockResetTimeout?: ReturnType<typeof setTimeout>;\n\n private _pollingTimeout?: ReturnType<typeof setTimeout>;\n\n private readonly _provider: SafeEventEmitterProvider;\n\n private readonly _pollingInterval: number;\n\n private readonly _retryTimeout: number;\n\n private readonly _keepEventLoopActive: boolean;\n\n private readonly _setSkipCacheFlag: boolean;\n\n readonly #internalEventListeners: InternalListener[] = [];\n\n #pendingLatestBlock?: Omit<DeferredPromise<string>, 'resolve'>;\n\n #pendingFetch?: Omit<DeferredPromise<string>, 'resolve'>;\n\n constructor(opts: PollingBlockTrackerOptions = {}) {\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._onNewListener.bind(this);\n this._onRemoveListener = this._onRemoveListener.bind(this);\n this._resetCurrentBlock = this._resetCurrentBlock.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 =\n opts.keepEventLoopActive === undefined ? true : opts.keepEventLoopActive;\n this._setSkipCacheFlag = opts.setSkipCacheFlag || false;\n }\n\n async destroy() {\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) => {\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 // dont allow module consumer to remove our internal event listeners\n removeAllListeners(eventName?: string | symbol) {\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 private _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 private _onNewListener(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 private _onRemoveListener(): void {\n // `removeListener` is called *after* the listener is removed\n if (this._getBlockTrackerEventCount() > 0) {\n return;\n }\n this._maybeEnd();\n }\n\n private _maybeStart() {\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 private _maybeEnd() {\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 private _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 private _shouldUseNewBlock(newBlock: string) {\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 private _newPotentialLatest(newBlock: string): void {\n if (!this._shouldUseNewBlock(newBlock)) {\n return;\n }\n this._setCurrentBlock(newBlock);\n }\n\n private _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 private _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 private _cancelBlockResetTimeout(): void {\n if (this._blockResetTimeout) {\n clearTimeout(this._blockResetTimeout);\n }\n }\n\n private _resetCurrentBlock(): 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 async checkForLatestBlock() {\n await this._updateLatestBlock();\n return await this.getLatestBlock();\n }\n\n private _start() {\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 private _end() {\n this._clearPollingTimeout();\n }\n\n private async _updateLatestBlock(): Promise<string> {\n // fetch + set latest block\n const latestBlock = await this._fetchLatestBlock();\n this._newPotentialLatest(latestBlock);\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 private 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 private async _updateAndQueue() {\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() {\n if (this._pollingTimeout) {\n clearTimeout(this._pollingTimeout);\n this._pollingTimeout = undefined;\n }\n }\n\n #addInternalListener(listener: InternalListener) {\n this.#internalEventListeners.push(listener);\n }\n\n #removeInternalListener(listener: InternalListener) {\n this.#internalEventListeners.splice(\n this.#internalEventListeners.indexOf(listener),\n 1,\n );\n }\n\n #rejectPendingLatestBlock(error: unknown) {\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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metamask/eth-block-tracker",
3
- "version": "12.0.1",
3
+ "version": "12.2.0",
4
4
  "description": "A block tracker for the Ethereum blockchain. Keeps track of the latest block",
5
5
  "repository": {
6
6
  "type": "git",
@@ -25,7 +25,7 @@
25
25
  "test:watch": "jest --watch"
26
26
  },
27
27
  "dependencies": {
28
- "@metamask/eth-json-rpc-provider": "^4.1.5",
28
+ "@metamask/eth-json-rpc-provider": "^5.0.0",
29
29
  "@metamask/safe-event-emitter": "^3.1.1",
30
30
  "@metamask/utils": "^11.0.1",
31
31
  "json-rpc-random-id": "^1.0.1",
@@ -76,4 +76,4 @@
76
76
  "@lavamoat/preinstall-always-fail": false
77
77
  }
78
78
  }
79
- }
79
+ }