Skip to content
This repository was archived by the owner on Oct 16, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 13 additions & 34 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,45 +1,24 @@
module.exports = {
extends: [
'@metamask/eslint-config',
'@metamask/eslint-config/config/nodejs',
],
plugins: [
'json',
],
parserOptions: {
ecmaVersion: 2018,
},
root: true,

extends: ['@metamask/eslint-config'],

overrides: [
{
files: ['*.ts'],
extends: [
'@metamask/eslint-config/config/typescript',
],
extends: ['@metamask/eslint-config-typescript'],
},

{
files: [
'*.js',
'*.json',
],
parserOptions: {
sourceType: 'script',
},
rules: {
'@typescript-eslint/no-require-imports': 'off',
'@typescript-eslint/no-var-requires': 'off',
},
files: ['*.js'],
extends: ['@metamask/eslint-config-nodejs'],
},

{
files: ['test/*'],
rules: {
// This happens before a build is created. It doesn't matter.
'import/no-unresolved': 'off',
},
files: ['*.json'],
extends: ['plugin:json/recommended'],
Comment thread
Gudahtt marked this conversation as resolved.
},
],
ignorePatterns: [
'!.eslintrc.js',
'dist/',
'node_modules/',
],

ignorePatterns: ['!.eslintrc.js', '!.prettierrc.js', 'dist/'],
};
8 changes: 8 additions & 0 deletions .prettierrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// All of these are defaults except singleQuote, but we specify them
// for explicitness
module.exports = {
quoteProps: 'as-needed',
singleQuote: true,
tabWidth: 2,
trailingComma: 'all',
};
26 changes: 16 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,25 @@
"pify": "^3.0.0"
},
"devDependencies": {
"@metamask/eslint-config": "^5.0.0",
"@types/json-rpc-random-id": "^1.0.0",
"@types/pify": "^5.0.0",
"@typescript-eslint/eslint-plugin": "^4.14.1",
"@typescript-eslint/parser": "^4.14.1",
"eslint": "^7.18.0",
"@metamask/eslint-config": "^9.0.0",
"@metamask/eslint-config-nodejs": "^9.0.0",
"@metamask/eslint-config-typescript": "^9.0.1",
"@types/json-rpc-random-id": "^1.0.1",
"@types/pify": "^5.0.1",
"@typescript-eslint/eslint-plugin": "^4.20.0",
"@typescript-eslint/parser": "^4.20.0",
"eslint": "^7.23.0",
"eslint-config-prettier": "^8.1.0",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-json": "^2.1.2",
"eslint-plugin-jsdoc": "^36.1.0",
"eslint-plugin-json": "^3.1.0",
"eslint-plugin-node": "^11.1.0",
"ganache-core": "^2.7.0",
"eslint-plugin-prettier": "^3.3.1",
"ganache-core": "^2.13.2",
"json-rpc-engine": "^6.1.0",
"tape": "^4.9.0",
"typescript": "^4.1.3"
"prettier": "^2.2.1",
"tape": "^5.5.3",
"typescript": "~4.4.0"
},
"directories": {
"test": "test"
Expand Down
28 changes: 21 additions & 7 deletions src/BaseBlockTracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,22 @@ import { JsonRpcRequest, JsonRpcResponse } from 'json-rpc-engine';

const sec = 1000;

const calculateSum = (accumulator: number, currentValue: number) => accumulator + currentValue;
const calculateSum = (accumulator: number, currentValue: number) =>
accumulator + currentValue;
const blockTrackerEvents: (string | symbol)[] = ['sync', 'latest'];

export interface Provider extends SafeEventEmitter {
sendAsync: <T, U>(req: JsonRpcRequest<T>, cb: (err: Error, response: JsonRpcResponse<U>) => void) => void;
sendAsync: <T, U>(
req: JsonRpcRequest<T>,
cb: (err: Error, response: JsonRpcResponse<U>) => void,
) => void;
}

interface BaseBlockTrackerArgs {
blockResetDuration?: number;
}

export class BaseBlockTracker extends SafeEventEmitter {

protected _isRunning: boolean;

private _blockResetDuration: number;
Expand Down Expand Up @@ -56,7 +59,9 @@ export class BaseBlockTracker extends SafeEventEmitter {
return this._currentBlock;
}
// wait for a new latest block
const latestBlock: string = await new Promise((resolve) => this.once('latest', resolve));
const latestBlock: string = await new Promise((resolve) =>
this.once('latest', resolve),
);
// return newly set current block
return latestBlock;
}
Expand Down Expand Up @@ -144,7 +149,7 @@ export class BaseBlockTracker extends SafeEventEmitter {
protected _newPotentialLatest(newBlock: string): void {
const currentBlock = this._currentBlock;
// only update if blok number is higher
if (currentBlock && (hexToInt(newBlock) <= hexToInt(currentBlock))) {
if (currentBlock && hexToInt(newBlock) <= hexToInt(currentBlock)) {
return;
}
this._setCurrentBlock(newBlock);
Expand All @@ -161,7 +166,10 @@ export class BaseBlockTracker extends SafeEventEmitter {
// clear any existing timeout
this._cancelBlockResetTimeout();
// clear latest block when stale
this._blockResetTimeout = setTimeout(this._resetCurrentBlock, this._blockResetDuration);
this._blockResetTimeout = setTimeout(
this._resetCurrentBlock,
this._blockResetDuration,
);

// nodejs - dont hold process open
if (this._blockResetTimeout.unref) {
Expand All @@ -178,9 +186,15 @@ export class BaseBlockTracker extends SafeEventEmitter {
private _resetCurrentBlock(): void {
this._currentBlock = null;
}

}

/**
* Converts a number represented as a string in hexadecimal format into a native
* number.
*
* @param hexInt - The hex string.
* @returns The number.
*/
function hexToInt(hexInt: string): number {
return Number.parseInt(hexInt, 16);
}
25 changes: 20 additions & 5 deletions src/PollingBlockTracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ interface ExtendedJsonRpcRequest<T> extends JsonRpcRequest<T> {
}

export class PollingBlockTracker extends BaseBlockTracker {

private _provider: Provider;

private _pollingInterval: number;
Expand All @@ -44,7 +43,8 @@ export class PollingBlockTracker extends BaseBlockTracker {
this._provider = opts.provider;
this._pollingInterval = opts.pollingInterval || 20 * sec;
this._retryTimeout = opts.retryTimeout || this._pollingInterval / 10;
this._keepEventLoopActive = opts.keepEventLoopActive === undefined ? true : opts.keepEventLoopActive;
this._keepEventLoopActive =
opts.keepEventLoopActive === undefined ? true : opts.keepEventLoopActive;
this._setSkipCacheFlag = opts.setSkipCacheFlag || false;
}

Expand All @@ -63,8 +63,12 @@ export class PollingBlockTracker extends BaseBlockTracker {
try {
await this._updateLatestBlock();
await timeout(this._pollingInterval, !this._keepEventLoopActive);
} catch (err) {
const newErr = new Error(`PollingBlockTracker - encountered an error while attempting to update latest block:\n${err.stack}`);
} catch (err: any) {
const newErr = new Error(
`PollingBlockTracker - encountered an error while attempting to update latest block:\n${
err.stack ?? err
}`,
);
try {
this.emit('error', newErr);
} catch (emitErr) {
Expand Down Expand Up @@ -94,12 +98,23 @@ export class PollingBlockTracker extends BaseBlockTracker {

const res = await pify((cb) => this._provider.sendAsync(req, cb))();
if (res.error) {
throw new Error(`PollingBlockTracker - encountered error fetching block:\n${res.error}`);
throw new Error(
`PollingBlockTracker - encountered error fetching block:\n${res.error}`,
);
}
return res.result;
}
}

/**
* Waits for the specified amount of time.
*
* @param duration - The amount of time in milliseconds.
* @param unref - Assuming this function is run in a Node context, governs
* whether Node should wait before the `setTimeout` has completed before ending
* the process (true for no, false for yes). Defaults to false.
* @returns A promise that can be used to wait.
*/
function timeout(duration: number, unref: boolean) {
return new Promise((resolve) => {
const timeoutRef = setTimeout(resolve, duration);
Expand Down
43 changes: 29 additions & 14 deletions src/SubscribeBlockTracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ interface SubscriptionNotificationParams {
}

export class SubscribeBlockTracker extends BaseBlockTracker {

private _provider: Provider;

private _subscriptionId: string | null;
Expand All @@ -40,8 +39,12 @@ export class SubscribeBlockTracker extends BaseBlockTracker {
protected async _start(): Promise<void> {
if (this._subscriptionId === undefined || this._subscriptionId === null) {
try {
const blockNumber = await this._call('eth_blockNumber') as string;
this._subscriptionId = await this._call('eth_subscribe', 'newHeads', {}) as string;
const blockNumber = (await this._call('eth_blockNumber')) as string;
this._subscriptionId = (await this._call(
'eth_subscribe',
'newHeads',
{},
)) as string;
this._provider.on('data', this._handleSubData.bind(this));
this._newPotentialLatest(blockNumber);
} catch (e) {
Expand All @@ -63,20 +66,32 @@ export class SubscribeBlockTracker extends BaseBlockTracker {

private _call(method: string, ...params: unknown[]): Promise<unknown> {
return new Promise((resolve, reject) => {
this._provider.sendAsync({
id: createRandomId(), method, params, jsonrpc: '2.0',
}, (err, res) => {
if (err) {
reject(err);
} else {
resolve((res as JsonRpcSuccess<unknown>).result);
}
});
this._provider.sendAsync(
{
id: createRandomId(),
method,
params,
jsonrpc: '2.0',
},
(err, res) => {
if (err) {
reject(err);
} else {
resolve((res as JsonRpcSuccess<unknown>).result);
}
},
);
});
}

private _handleSubData(_: unknown, response: JsonRpcNotification<SubscriptionNotificationParams>): void {
if (response.method === 'eth_subscription' && response.params?.subscription === this._subscriptionId) {
private _handleSubData(
_: unknown,
response: JsonRpcNotification<SubscriptionNotificationParams>,
): void {
if (
response.method === 'eth_subscription' &&
response.params?.subscription === this._subscriptionId
) {
this._newPotentialLatest(response.params.result.number);
}
}
Expand Down
Loading