@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 +3 -4
- package/dist/PollingBlockTracker.js +56 -27
- package/dist/PollingBlockTracker.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/package.json +10 -9
- package/dist/SubscribeBlockTracker.d.ts +0 -41
- package/dist/SubscribeBlockTracker.js +0 -253
- package/dist/SubscribeBlockTracker.js.map +0 -1
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
|
|
96
|
-
- Run `yarn
|
|
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
|
-
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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
|
-
|
|
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 (
|
|
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',
|
|
275
|
+
this.emit('error', error);
|
|
247
276
|
}
|
|
248
|
-
catch (
|
|
249
|
-
console.error(
|
|
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
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,
|
|
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": "
|
|
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
|
-
"
|
|
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": "^
|
|
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": "^
|
|
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@
|
|
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"]}
|