@metamask/eth-block-tracker 11.0.3 → 12.0.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/README.md CHANGED
@@ -91,10 +91,9 @@ blockTracker.on('error', (err) => console.error(err));
91
91
  ### Setup
92
92
 
93
93
  - Install the current LTS version of [Node.js](https://nodejs.org)
94
- - If you are using [nvm](https://github.com/creationix/nvm#installation) (recommended) running `nvm use` will automatically choose the right node version for you.
95
- - Install [Yarn v1](https://yarnpkg.com/en/docs/install)
96
- - Run `yarn setup` to install dependencies and run any requried post-install scripts
97
- - **Warning:** Do not use the `yarn` / `yarn install` command directly. Use `yarn setup` instead. The normal install command will skip required post-install scripts, leaving your development environment in an invalid state.
94
+ - If you are using [nvm](https://github.com/creationix/nvm#installation) (recommended) running `nvm install` will install the latest version and running `nvm use` will automatically choose the right node version for you.
95
+ - Install [Yarn](https://yarnpkg.com) v4 via [Corepack](https://github.com/nodejs/corepack?tab=readme-ov-file#how-to-install)
96
+ - Run `yarn install` to install dependencies and run any required post-install scripts
98
97
 
99
98
  ### Testing and Linting
100
99
 
@@ -13,7 +13,7 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
13
13
  var __importDefault = (this && this.__importDefault) || function (mod) {
14
14
  return (mod && mod.__esModule) ? mod : { "default": mod };
15
15
  };
16
- var _PollingBlockTracker_instances, _PollingBlockTracker_internalEventListeners, _PollingBlockTracker_pendingLatestBlock, _PollingBlockTracker_addInternalListener, _PollingBlockTracker_removeInternalListener, _PollingBlockTracker_rejectPendingLatestBlock;
16
+ var _PollingBlockTracker_instances, _PollingBlockTracker_internalEventListeners, _PollingBlockTracker_pendingLatestBlock, _PollingBlockTracker_pendingFetch, _PollingBlockTracker_addInternalListener, _PollingBlockTracker_removeInternalListener, _PollingBlockTracker_rejectPendingLatestBlock;
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
18
  exports.PollingBlockTracker = void 0;
19
19
  const safe_event_emitter_1 = __importDefault(require("@metamask/safe-event-emitter"));
@@ -34,6 +34,7 @@ class PollingBlockTracker extends safe_event_emitter_1.default {
34
34
  _PollingBlockTracker_instances.add(this);
35
35
  _PollingBlockTracker_internalEventListeners.set(this, []);
36
36
  _PollingBlockTracker_pendingLatestBlock.set(this, void 0);
37
+ _PollingBlockTracker_pendingFetch.set(this, void 0);
37
38
  // config
38
39
  this._blockResetDuration = opts.blockResetDuration || 20 * sec;
39
40
  this._usePastBlocks = opts.usePastBlocks || false;
@@ -71,22 +72,38 @@ class PollingBlockTracker extends safe_event_emitter_1.default {
71
72
  if (this._currentBlock) {
72
73
  return this._currentBlock;
73
74
  }
74
- else if (__classPrivateFieldGet(this, _PollingBlockTracker_pendingLatestBlock, "f")) {
75
+ if (__classPrivateFieldGet(this, _PollingBlockTracker_pendingLatestBlock, "f")) {
75
76
  return await __classPrivateFieldGet(this, _PollingBlockTracker_pendingLatestBlock, "f").promise;
76
77
  }
77
78
  const { promise, resolve, reject } = (0, utils_1.createDeferredPromise)({
78
79
  suppressUnhandledRejection: true,
79
80
  });
80
81
  __classPrivateFieldSet(this, _PollingBlockTracker_pendingLatestBlock, { reject, promise }, "f");
81
- // wait for a new latest block
82
- const onLatestBlock = (value) => {
83
- __classPrivateFieldGet(this, _PollingBlockTracker_instances, "m", _PollingBlockTracker_removeInternalListener).call(this, onLatestBlock);
84
- resolve(value);
82
+ try {
83
+ // If tracker isn't running, just fetch directly
84
+ if (!this._isRunning) {
85
+ const latestBlock = await this._fetchLatestBlock();
86
+ this._newPotentialLatest(latestBlock);
87
+ resolve(latestBlock);
88
+ return latestBlock;
89
+ }
90
+ // If tracker is running, wait for next block with timeout
91
+ const onLatestBlock = (value) => {
92
+ __classPrivateFieldGet(this, _PollingBlockTracker_instances, "m", _PollingBlockTracker_removeInternalListener).call(this, onLatestBlock);
93
+ this.removeListener('latest', onLatestBlock);
94
+ resolve(value);
95
+ };
96
+ __classPrivateFieldGet(this, _PollingBlockTracker_instances, "m", _PollingBlockTracker_addInternalListener).call(this, onLatestBlock);
97
+ this.once('latest', onLatestBlock);
98
+ return await promise;
99
+ }
100
+ catch (error) {
101
+ reject(error);
102
+ throw error;
103
+ }
104
+ finally {
85
105
  __classPrivateFieldSet(this, _PollingBlockTracker_pendingLatestBlock, undefined, "f");
86
- };
87
- __classPrivateFieldGet(this, _PollingBlockTracker_instances, "m", _PollingBlockTracker_addInternalListener).call(this, onLatestBlock);
88
- this.once('latest', onLatestBlock);
89
- return await promise;
106
+ }
90
107
  }
91
108
  // dont allow module consumer to remove our internal event listeners
92
109
  removeAllListeners(eventName) {
@@ -191,7 +208,6 @@ class PollingBlockTracker extends safe_event_emitter_1.default {
191
208
  _resetCurrentBlock() {
192
209
  this._currentBlock = null;
193
210
  }
194
- // trigger block polling
195
211
  async checkForLatestBlock() {
196
212
  await this._updateLatestBlock();
197
213
  return await this.getLatestBlock();
@@ -210,43 +226,56 @@ class PollingBlockTracker extends safe_event_emitter_1.default {
210
226
  this._newPotentialLatest(latestBlock);
211
227
  }
212
228
  async _fetchLatestBlock() {
213
- const req = {
214
- jsonrpc: '2.0',
215
- id: createRandomId(),
216
- method: 'eth_blockNumber',
217
- params: [],
218
- };
219
- if (this._setSkipCacheFlag) {
220
- req.skipCache = true;
229
+ // If there's already a pending fetch, reuse it
230
+ if (__classPrivateFieldGet(this, _PollingBlockTracker_pendingFetch, "f")) {
231
+ return await __classPrivateFieldGet(this, _PollingBlockTracker_pendingFetch, "f").promise;
221
232
  }
222
- log('Making request', req);
233
+ // Create a new deferred promise for this request
234
+ const { promise, resolve, reject } = (0, utils_1.createDeferredPromise)({
235
+ suppressUnhandledRejection: true,
236
+ });
237
+ __classPrivateFieldSet(this, _PollingBlockTracker_pendingFetch, { reject, promise }, "f");
223
238
  try {
239
+ const req = {
240
+ jsonrpc: '2.0',
241
+ id: createRandomId(),
242
+ method: 'eth_blockNumber',
243
+ params: [],
244
+ };
245
+ if (this._setSkipCacheFlag) {
246
+ req.skipCache = true;
247
+ }
248
+ log('Making request', req);
224
249
  const result = await this._provider.request(req);
225
250
  log('Got result', result);
251
+ resolve(result);
226
252
  return result;
227
253
  }
228
254
  catch (error) {
229
255
  log('Encountered error fetching block', (0, utils_1.getErrorMessage)(error));
256
+ reject(error);
257
+ __classPrivateFieldGet(this, _PollingBlockTracker_instances, "m", _PollingBlockTracker_rejectPendingLatestBlock).call(this, error);
230
258
  throw error;
231
259
  }
260
+ finally {
261
+ __classPrivateFieldSet(this, _PollingBlockTracker_pendingFetch, undefined, "f");
262
+ }
232
263
  }
233
264
  /**
234
265
  * The core polling function that runs after each interval.
235
266
  * Updates the latest block and then queues the next update.
236
267
  */
237
268
  async _updateAndQueue() {
238
- var _a;
239
269
  let interval = this._pollingInterval;
240
270
  try {
241
271
  await this._updateLatestBlock();
242
272
  }
243
- catch (err) {
244
- const newErr = new Error(`PollingBlockTracker - encountered an error while attempting to update latest block:\n${(_a = err.stack) !== null && _a !== void 0 ? _a : err}`);
273
+ catch (error) {
245
274
  try {
246
- this.emit('error', newErr);
275
+ this.emit('error', error);
247
276
  }
248
- catch (emitErr) {
249
- console.error(newErr);
277
+ catch (_a) {
278
+ console.error(`Error updating latest block: ${(0, utils_1.getErrorMessage)(error)}`);
250
279
  }
251
280
  interval = this._retryTimeout;
252
281
  }
@@ -273,7 +302,7 @@ class PollingBlockTracker extends safe_event_emitter_1.default {
273
302
  }
274
303
  }
275
304
  exports.PollingBlockTracker = PollingBlockTracker;
276
- _PollingBlockTracker_internalEventListeners = new WeakMap(), _PollingBlockTracker_pendingLatestBlock = new WeakMap(), _PollingBlockTracker_instances = new WeakSet(), _PollingBlockTracker_addInternalListener = function _PollingBlockTracker_addInternalListener(listener) {
305
+ _PollingBlockTracker_internalEventListeners = new WeakMap(), _PollingBlockTracker_pendingLatestBlock = new WeakMap(), _PollingBlockTracker_pendingFetch = new WeakMap(), _PollingBlockTracker_instances = new WeakSet(), _PollingBlockTracker_addInternalListener = function _PollingBlockTracker_addInternalListener(listener) {
277
306
  __classPrivateFieldGet(this, _PollingBlockTracker_internalEventListeners, "f").push(listener);
278
307
  }, _PollingBlockTracker_removeInternalListener = function _PollingBlockTracker_removeInternalListener(listener) {
279
308
  __classPrivateFieldGet(this, _PollingBlockTracker_internalEventListeners, "f").splice(__classPrivateFieldGet(this, _PollingBlockTracker_internalEventListeners, "f").indexOf(listener), 1);
@@ -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 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 this._maybeEnd();\n super.removeAllListeners();\n this.#rejectPendingLatestBlock(new Error('Block tracker destroyed'));\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 } else 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 // wait for a new latest block\n const onLatestBlock = (value: string) => {\n this.#removeInternalListener(onLatestBlock);\n resolve(value);\n this.#pendingLatestBlock = undefined;\n };\n this.#addInternalListener(onLatestBlock);\n this.once('latest', onLatestBlock);\n return await promise;\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.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 // trigger block polling\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 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 try {\n const result = await this._provider.request<[], string>(req);\n log('Got result', result);\n return result;\n } catch (error) {\n log('Encountered error fetching block', getErrorMessage(error));\n throw error;\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 (err: any) {\n const newErr = new Error(\n `PollingBlockTracker - encountered an error while attempting to update latest block:\\n${\n err.stack ?? err\n }`,\n );\n\n try {\n this.emit('error', newErr);\n } catch (emitErr) {\n console.error(newErr);\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 this._maybeEnd();\n super.removeAllListeners();\n this.#rejectPendingLatestBlock(new Error('Block tracker destroyed'));\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.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"]}
package/dist/index.d.ts CHANGED
@@ -1,3 +1,2 @@
1
1
  export * from './PollingBlockTracker';
2
- export * from './SubscribeBlockTracker';
3
2
  export * from './BlockTracker';
package/dist/index.js CHANGED
@@ -15,6 +15,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./PollingBlockTracker"), exports);
18
- __exportStar(require("./SubscribeBlockTracker"), exports);
19
18
  __exportStar(require("./BlockTracker"), exports);
20
19
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,wDAAsC;AACtC,0DAAwC;AACxC,iDAA+B","sourcesContent":["export * from './PollingBlockTracker';\nexport * from './SubscribeBlockTracker';\nexport * from './BlockTracker';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,wDAAsC;AACtC,iDAA+B","sourcesContent":["export * from './PollingBlockTracker';\nexport * from './BlockTracker';\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@metamask/eth-block-tracker",
3
- "version": "11.0.3",
4
- "description": "A block tracker for the Ethereum blockchain. Keeps track of the latest block.",
3
+ "version": "12.0.0",
4
+ "description": "A block tracker for the Ethereum blockchain. Keeps track of the latest block",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/MetaMask/eth-block-tracker.git"
@@ -15,19 +15,19 @@
15
15
  "scripts": {
16
16
  "build": "tsc --project tsconfig.build.json",
17
17
  "build:clean": "rimraf dist && yarn build",
18
- "lint": "yarn lint:eslint && yarn lint:misc --check",
18
+ "lint": "yarn lint:eslint && yarn lint:constraints && yarn lint:misc --check",
19
+ "lint:constraints": "yarn constraints",
19
20
  "lint:eslint": "eslint . --cache --ext js,ts",
20
- "lint:fix": "yarn lint:eslint --fix && yarn lint:misc --write",
21
+ "lint:fix": "yarn lint:eslint --fix && yarn lint:constraints --fix && yarn lint:misc --write",
21
22
  "lint:misc": "prettier '**/*.json' '**/*.md' '!CHANGELOG.md' '**/*.yml' --ignore-path .gitignore --no-error-on-unmatched-pattern",
22
- "prepublishOnly": "yarn build:clean && yarn lint && yarn test",
23
- "setup": "yarn install && yarn allow-scripts",
23
+ "prepack": "./scripts/prepack.sh",
24
24
  "test": "jest",
25
25
  "test:watch": "jest --watch"
26
26
  },
27
27
  "dependencies": {
28
28
  "@metamask/eth-json-rpc-provider": "^4.1.5",
29
29
  "@metamask/safe-event-emitter": "^3.1.1",
30
- "@metamask/utils": "^9.1.0",
30
+ "@metamask/utils": "^11.0.1",
31
31
  "json-rpc-random-id": "^1.0.1",
32
32
  "pify": "^5.0.0"
33
33
  },
@@ -45,12 +45,13 @@
45
45
  "@types/pify": "^5.0.1",
46
46
  "@typescript-eslint/eslint-plugin": "^5.61.0",
47
47
  "@typescript-eslint/parser": "^5.61.0",
48
+ "@yarnpkg/types": "^4.0.0",
48
49
  "eslint": "^8.21.0",
49
50
  "eslint-config-prettier": "^8.1.0",
50
51
  "eslint-import-resolver-typescript": "^2.7.1",
51
52
  "eslint-plugin-import": "^2.22.1",
52
53
  "eslint-plugin-jest": "^27.1.5",
53
- "eslint-plugin-jsdoc": "^39.9.1",
54
+ "eslint-plugin-jsdoc": "^41.0.0",
54
55
  "eslint-plugin-n": "^15.7.0",
55
56
  "eslint-plugin-prettier": "^4.2.1",
56
57
  "eslint-plugin-promise": "^6.1.1",
@@ -62,7 +63,7 @@
62
63
  "ts-node": "^10.7.0",
63
64
  "typescript": "~4.8.4"
64
65
  },
65
- "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e",
66
+ "packageManager": "yarn@4.5.3",
66
67
  "engines": {
67
68
  "node": "^18.16 || ^20 || >=22"
68
69
  },
@@ -1,41 +0,0 @@
1
- import type { SafeEventEmitterProvider } from '@metamask/eth-json-rpc-provider';
2
- import SafeEventEmitter from '@metamask/safe-event-emitter';
3
- import type { BlockTracker } from './BlockTracker';
4
- export interface SubscribeBlockTrackerOptions {
5
- provider?: SafeEventEmitterProvider;
6
- blockResetDuration?: number;
7
- usePastBlocks?: boolean;
8
- }
9
- export declare class SubscribeBlockTracker extends SafeEventEmitter implements BlockTracker {
10
- #private;
11
- private _isRunning;
12
- private readonly _blockResetDuration;
13
- private readonly _usePastBlocks;
14
- private _currentBlock;
15
- private _blockResetTimeout?;
16
- private readonly _provider;
17
- private _subscriptionId;
18
- constructor(opts?: SubscribeBlockTrackerOptions);
19
- destroy(): Promise<void>;
20
- isRunning(): boolean;
21
- getCurrentBlock(): string | null;
22
- getLatestBlock(): Promise<string>;
23
- removeAllListeners(eventName?: string | symbol): this;
24
- private _setupInternalEvents;
25
- private _onNewListener;
26
- private _onRemoveListener;
27
- private _maybeStart;
28
- private _maybeEnd;
29
- private _getBlockTrackerEventCount;
30
- private _shouldUseNewBlock;
31
- private _newPotentialLatest;
32
- private _setCurrentBlock;
33
- private _setupBlockResetTimeout;
34
- private _cancelBlockResetTimeout;
35
- private _resetCurrentBlock;
36
- checkForLatestBlock(): Promise<string>;
37
- private _start;
38
- private _end;
39
- private _call;
40
- private _handleSubData;
41
- }
@@ -1,253 +0,0 @@
1
- "use strict";
2
- var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
3
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
4
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
5
- return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
6
- };
7
- var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
8
- if (kind === "m") throw new TypeError("Private method is not writable");
9
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
10
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
11
- return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
12
- };
13
- var __importDefault = (this && this.__importDefault) || function (mod) {
14
- return (mod && mod.__esModule) ? mod : { "default": mod };
15
- };
16
- var _SubscribeBlockTracker_instances, _SubscribeBlockTracker_internalEventListeners, _SubscribeBlockTracker_pendingLatestBlock, _SubscribeBlockTracker_addInternalListener, _SubscribeBlockTracker_removeInternalListener, _SubscribeBlockTracker_rejectPendingLatestBlock;
17
- Object.defineProperty(exports, "__esModule", { value: true });
18
- exports.SubscribeBlockTracker = void 0;
19
- const safe_event_emitter_1 = __importDefault(require("@metamask/safe-event-emitter"));
20
- const utils_1 = require("@metamask/utils");
21
- const json_rpc_random_id_1 = __importDefault(require("json-rpc-random-id"));
22
- const createRandomId = (0, json_rpc_random_id_1.default)();
23
- const sec = 1000;
24
- const blockTrackerEvents = ['sync', 'latest'];
25
- class SubscribeBlockTracker extends safe_event_emitter_1.default {
26
- constructor(opts = {}) {
27
- // parse + validate args
28
- if (!opts.provider) {
29
- throw new Error('SubscribeBlockTracker - no provider specified.');
30
- }
31
- super();
32
- _SubscribeBlockTracker_instances.add(this);
33
- _SubscribeBlockTracker_internalEventListeners.set(this, []);
34
- _SubscribeBlockTracker_pendingLatestBlock.set(this, void 0);
35
- // config
36
- this._blockResetDuration = opts.blockResetDuration || 20 * sec;
37
- this._usePastBlocks = opts.usePastBlocks || false;
38
- // state
39
- this._currentBlock = null;
40
- this._isRunning = false;
41
- // bind functions for internal use
42
- this._onNewListener = this._onNewListener.bind(this);
43
- this._onRemoveListener = this._onRemoveListener.bind(this);
44
- this._resetCurrentBlock = this._resetCurrentBlock.bind(this);
45
- // listen for handler changes
46
- this._setupInternalEvents();
47
- // config
48
- this._provider = opts.provider;
49
- this._subscriptionId = null;
50
- }
51
- async destroy() {
52
- this._cancelBlockResetTimeout();
53
- await this._maybeEnd();
54
- super.removeAllListeners();
55
- __classPrivateFieldGet(this, _SubscribeBlockTracker_instances, "m", _SubscribeBlockTracker_rejectPendingLatestBlock).call(this, new Error('Block tracker destroyed'));
56
- }
57
- isRunning() {
58
- return this._isRunning;
59
- }
60
- getCurrentBlock() {
61
- return this._currentBlock;
62
- }
63
- async getLatestBlock() {
64
- // return if available
65
- if (this._currentBlock) {
66
- return this._currentBlock;
67
- }
68
- else if (__classPrivateFieldGet(this, _SubscribeBlockTracker_pendingLatestBlock, "f")) {
69
- return await __classPrivateFieldGet(this, _SubscribeBlockTracker_pendingLatestBlock, "f").promise;
70
- }
71
- const { resolve, reject, promise } = (0, utils_1.createDeferredPromise)({
72
- suppressUnhandledRejection: true,
73
- });
74
- __classPrivateFieldSet(this, _SubscribeBlockTracker_pendingLatestBlock, { reject, promise }, "f");
75
- // wait for a new latest block
76
- const onLatestBlock = (value) => {
77
- __classPrivateFieldGet(this, _SubscribeBlockTracker_instances, "m", _SubscribeBlockTracker_removeInternalListener).call(this, onLatestBlock);
78
- resolve(value);
79
- __classPrivateFieldSet(this, _SubscribeBlockTracker_pendingLatestBlock, undefined, "f");
80
- };
81
- __classPrivateFieldGet(this, _SubscribeBlockTracker_instances, "m", _SubscribeBlockTracker_addInternalListener).call(this, onLatestBlock);
82
- this.once('latest', onLatestBlock);
83
- return await promise;
84
- }
85
- // dont allow module consumer to remove our internal event listeners
86
- removeAllListeners(eventName) {
87
- // perform default behavior, preserve fn arity
88
- if (eventName) {
89
- super.removeAllListeners(eventName);
90
- }
91
- else {
92
- super.removeAllListeners();
93
- }
94
- // re-add internal events
95
- this._setupInternalEvents();
96
- // trigger stop check just in case
97
- this._onRemoveListener();
98
- return this;
99
- }
100
- _setupInternalEvents() {
101
- // first remove listeners for idempotence
102
- this.removeListener('newListener', this._onNewListener);
103
- this.removeListener('removeListener', this._onRemoveListener);
104
- // then add them
105
- this.on('newListener', this._onNewListener);
106
- this.on('removeListener', this._onRemoveListener);
107
- }
108
- _onNewListener(eventName) {
109
- // `newListener` is called *before* the listener is added
110
- if (blockTrackerEvents.includes(eventName)) {
111
- // TODO: Handle dangling promise
112
- this._maybeStart();
113
- }
114
- }
115
- _onRemoveListener() {
116
- // `removeListener` is called *after* the listener is removed
117
- if (this._getBlockTrackerEventCount() > 0) {
118
- return;
119
- }
120
- this._maybeEnd();
121
- }
122
- async _maybeStart() {
123
- if (this._isRunning) {
124
- return;
125
- }
126
- this._isRunning = true;
127
- // cancel setting latest block to stale
128
- this._cancelBlockResetTimeout();
129
- await this._start();
130
- this.emit('_started');
131
- }
132
- async _maybeEnd() {
133
- if (!this._isRunning) {
134
- return;
135
- }
136
- this._isRunning = false;
137
- this._setupBlockResetTimeout();
138
- await this._end();
139
- this.emit('_ended');
140
- }
141
- _getBlockTrackerEventCount() {
142
- return (blockTrackerEvents
143
- .map((eventName) => this.listeners(eventName))
144
- .flat()
145
- // internal listeners are not included in the count
146
- .filter((listener) => __classPrivateFieldGet(this, _SubscribeBlockTracker_internalEventListeners, "f").every((internalListener) => !Object.is(internalListener, listener))).length);
147
- }
148
- _shouldUseNewBlock(newBlock) {
149
- const currentBlock = this._currentBlock;
150
- if (!currentBlock) {
151
- return true;
152
- }
153
- const newBlockInt = hexToInt(newBlock);
154
- const currentBlockInt = hexToInt(currentBlock);
155
- return ((this._usePastBlocks && newBlockInt < currentBlockInt) ||
156
- newBlockInt > currentBlockInt);
157
- }
158
- _newPotentialLatest(newBlock) {
159
- if (!this._shouldUseNewBlock(newBlock)) {
160
- return;
161
- }
162
- this._setCurrentBlock(newBlock);
163
- }
164
- _setCurrentBlock(newBlock) {
165
- const oldBlock = this._currentBlock;
166
- this._currentBlock = newBlock;
167
- this.emit('latest', newBlock);
168
- this.emit('sync', { oldBlock, newBlock });
169
- }
170
- _setupBlockResetTimeout() {
171
- // clear any existing timeout
172
- this._cancelBlockResetTimeout();
173
- // clear latest block when stale
174
- this._blockResetTimeout = setTimeout(this._resetCurrentBlock, this._blockResetDuration);
175
- // nodejs - dont hold process open
176
- if (this._blockResetTimeout.unref) {
177
- this._blockResetTimeout.unref();
178
- }
179
- }
180
- _cancelBlockResetTimeout() {
181
- if (this._blockResetTimeout) {
182
- clearTimeout(this._blockResetTimeout);
183
- }
184
- }
185
- _resetCurrentBlock() {
186
- this._currentBlock = null;
187
- }
188
- async checkForLatestBlock() {
189
- return await this.getLatestBlock();
190
- }
191
- async _start() {
192
- if (this._subscriptionId === undefined || this._subscriptionId === null) {
193
- try {
194
- const blockNumber = (await this._call('eth_blockNumber'));
195
- this._subscriptionId = (await this._call('eth_subscribe', 'newHeads'));
196
- this._provider.on('data', this._handleSubData.bind(this));
197
- this._newPotentialLatest(blockNumber);
198
- }
199
- catch (e) {
200
- this.emit('error', e);
201
- __classPrivateFieldGet(this, _SubscribeBlockTracker_instances, "m", _SubscribeBlockTracker_rejectPendingLatestBlock).call(this, e);
202
- }
203
- }
204
- }
205
- async _end() {
206
- if (this._subscriptionId !== null && this._subscriptionId !== undefined) {
207
- try {
208
- await this._call('eth_unsubscribe', this._subscriptionId);
209
- this._subscriptionId = null;
210
- }
211
- catch (e) {
212
- this.emit('error', e);
213
- __classPrivateFieldGet(this, _SubscribeBlockTracker_instances, "m", _SubscribeBlockTracker_rejectPendingLatestBlock).call(this, e);
214
- }
215
- }
216
- }
217
- async _call(method, ...params) {
218
- return this._provider.request({
219
- id: createRandomId(),
220
- method,
221
- params,
222
- jsonrpc: '2.0',
223
- });
224
- }
225
- _handleSubData(_, response) {
226
- var _a;
227
- if (response.method === 'eth_subscription' &&
228
- ((_a = response.params) === null || _a === void 0 ? void 0 : _a.subscription) === this._subscriptionId) {
229
- this._newPotentialLatest(response.params.result.number);
230
- }
231
- }
232
- }
233
- exports.SubscribeBlockTracker = SubscribeBlockTracker;
234
- _SubscribeBlockTracker_internalEventListeners = new WeakMap(), _SubscribeBlockTracker_pendingLatestBlock = new WeakMap(), _SubscribeBlockTracker_instances = new WeakSet(), _SubscribeBlockTracker_addInternalListener = function _SubscribeBlockTracker_addInternalListener(listener) {
235
- __classPrivateFieldGet(this, _SubscribeBlockTracker_internalEventListeners, "f").push(listener);
236
- }, _SubscribeBlockTracker_removeInternalListener = function _SubscribeBlockTracker_removeInternalListener(listener) {
237
- __classPrivateFieldGet(this, _SubscribeBlockTracker_internalEventListeners, "f").splice(__classPrivateFieldGet(this, _SubscribeBlockTracker_internalEventListeners, "f").indexOf(listener), 1);
238
- }, _SubscribeBlockTracker_rejectPendingLatestBlock = function _SubscribeBlockTracker_rejectPendingLatestBlock(error) {
239
- var _a;
240
- (_a = __classPrivateFieldGet(this, _SubscribeBlockTracker_pendingLatestBlock, "f")) === null || _a === void 0 ? void 0 : _a.reject(error);
241
- __classPrivateFieldSet(this, _SubscribeBlockTracker_pendingLatestBlock, undefined, "f");
242
- };
243
- /**
244
- * Converts a number represented as a string in hexadecimal format into a native
245
- * number.
246
- *
247
- * @param hexInt - The hex string.
248
- * @returns The number.
249
- */
250
- function hexToInt(hexInt) {
251
- return Number.parseInt(hexInt, 16);
252
- }
253
- //# sourceMappingURL=SubscribeBlockTracker.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"SubscribeBlockTracker.js","sourceRoot":"","sources":["../src/SubscribeBlockTracker.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 type Json,\n type JsonRpcNotification,\n} from '@metamask/utils';\nimport getCreateRandomId from 'json-rpc-random-id';\n\nimport type { BlockTracker } from './BlockTracker';\n\nconst createRandomId = getCreateRandomId();\n\nconst sec = 1000;\n\nconst blockTrackerEvents: (string | symbol)[] = ['sync', 'latest'];\n\nexport interface SubscribeBlockTrackerOptions {\n provider?: SafeEventEmitterProvider;\n blockResetDuration?: number;\n usePastBlocks?: boolean;\n}\n\ninterface SubscriptionNotificationParams {\n [key: string]: Json;\n subscription: string;\n result: { number: string };\n}\n\ntype InternalListener = (value: string) => void;\n\nexport class SubscribeBlockTracker\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 readonly _provider: SafeEventEmitterProvider;\n\n private _subscriptionId: string | null;\n\n readonly #internalEventListeners: InternalListener[] = [];\n\n #pendingLatestBlock?: Omit<DeferredPromise<string>, 'resolve'>;\n\n constructor(opts: SubscribeBlockTrackerOptions = {}) {\n // parse + validate args\n if (!opts.provider) {\n throw new Error('SubscribeBlockTracker - 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._subscriptionId = null;\n }\n\n async destroy() {\n this._cancelBlockResetTimeout();\n await this._maybeEnd();\n super.removeAllListeners();\n this.#rejectPendingLatestBlock(new Error('Block tracker destroyed'));\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 } else if (this.#pendingLatestBlock) {\n return await this.#pendingLatestBlock.promise;\n }\n\n const { resolve, reject, promise } = createDeferredPromise<string>({\n suppressUnhandledRejection: true,\n });\n this.#pendingLatestBlock = { reject, promise };\n\n // wait for a new latest block\n const onLatestBlock = (value: string) => {\n this.#removeInternalListener(onLatestBlock);\n resolve(value);\n this.#pendingLatestBlock = undefined;\n };\n this.#addInternalListener(onLatestBlock);\n this.once('latest', onLatestBlock);\n return await promise;\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 async _maybeStart(): Promise<void> {\n if (this._isRunning) {\n return;\n }\n this._isRunning = true;\n // cancel setting latest block to stale\n this._cancelBlockResetTimeout();\n await this._start();\n this.emit('_started');\n }\n\n private async _maybeEnd(): Promise<void> {\n if (!this._isRunning) {\n return;\n }\n this._isRunning = false;\n this._setupBlockResetTimeout();\n await this._end();\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(): Promise<string> {\n return await this.getLatestBlock();\n }\n\n private async _start(): Promise<void> {\n if (this._subscriptionId === undefined || this._subscriptionId === null) {\n try {\n const blockNumber = (await this._call('eth_blockNumber')) as string;\n this._subscriptionId = (await this._call(\n 'eth_subscribe',\n 'newHeads',\n )) as string;\n this._provider.on('data', this._handleSubData.bind(this));\n this._newPotentialLatest(blockNumber);\n } catch (e) {\n this.emit('error', e);\n this.#rejectPendingLatestBlock(e);\n }\n }\n }\n\n private async _end() {\n if (this._subscriptionId !== null && this._subscriptionId !== undefined) {\n try {\n await this._call('eth_unsubscribe', this._subscriptionId);\n this._subscriptionId = null;\n } catch (e) {\n this.emit('error', e);\n this.#rejectPendingLatestBlock(e);\n }\n }\n }\n\n private async _call(method: string, ...params: Json[]): Promise<unknown> {\n return this._provider.request({\n id: createRandomId(),\n method,\n params,\n jsonrpc: '2.0',\n });\n }\n\n private _handleSubData(\n _: unknown,\n response: JsonRpcNotification<SubscriptionNotificationParams>,\n ): void {\n if (\n response.method === 'eth_subscription' &&\n response.params?.subscription === this._subscriptionId\n ) {\n this._newPotentialLatest(response.params.result.number);\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"]}