From ec3e88e967164a8a0f47a8f86a4da295021ed61b Mon Sep 17 00:00:00 2001 From: Elliot Winkler Date: Mon, 2 May 2022 09:43:23 -0600 Subject: [PATCH] Use TypeScript; update deps; refactor exports (#58) * **(BREAKING)** Change `index` so that it exports all public modules as named exports * **(BREAKING)** Update all dependencies so that we get TypeScript versions * Convert implementation code to TypeScript * Convert tests to TypeScript, and add tests to ensure that the JavaScript-only checks still work, for as much backward compatibility as possible --- .eslintrc.js | 7 +- jest.config.js => jest.config.ts | 17 +- package.json | 29 ++- src/create-infura-middleware.test.ts | 51 +++++ src/create-infura-middleware.ts | 218 ++++++++++++++++++ src/create-provider.ts | 25 +++ src/createProvider.js | 21 -- src/fetch-config-from-req.test.ts | 181 +++++++++++++++ src/fetch-config-from-req.ts | 83 +++++++ src/index.js | 274 ----------------------- src/index.test.js | 225 ------------------- src/index.ts | 3 + src/types.ts | 28 +++ tsconfig.build.json | 13 ++ tsconfig.json | 12 + yarn.lock | 316 ++++++++++++++++++++------- 16 files changed, 887 insertions(+), 616 deletions(-) rename jest.config.js => jest.config.ts (95%) create mode 100644 src/create-infura-middleware.test.ts create mode 100644 src/create-infura-middleware.ts create mode 100644 src/create-provider.ts delete mode 100644 src/createProvider.js create mode 100644 src/fetch-config-from-req.test.ts create mode 100644 src/fetch-config-from-req.ts delete mode 100644 src/index.js delete mode 100644 src/index.test.js create mode 100644 src/index.ts create mode 100644 src/types.ts create mode 100644 tsconfig.build.json create mode 100644 tsconfig.json diff --git a/.eslintrc.js b/.eslintrc.js index 8a632f5..3043e90 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -10,7 +10,12 @@ module.exports = { }, { - files: ['*.test.js'], + files: ['*.ts'], + extends: ['@metamask/eslint-config-typescript'], + }, + + { + files: ['*.test.ts'], extends: ['@metamask/eslint-config-jest'], }, ], diff --git a/jest.config.js b/jest.config.ts similarity index 95% rename from jest.config.js rename to jest.config.ts index 5d9efab..3869581 100644 --- a/jest.config.js +++ b/jest.config.ts @@ -3,7 +3,9 @@ * https://jestjs.io/docs/configuration */ -module.exports = { +import type { Config } from '@jest/types'; + +const config: Config.InitialOptions = { // All imported modules in your tests should be mocked automatically // automock: false, @@ -22,15 +24,13 @@ module.exports = { collectCoverage: true, // An array of glob patterns indicating a set of files for which coverage information should be collected - collectCoverageFrom: ['./src/**/*.js'], + collectCoverageFrom: ['./src/**/*.ts'], // The directory where Jest should output its coverage files coverageDirectory: 'coverage', // An array of regexp pattern strings used to skip coverage collection - // coveragePathIgnorePatterns: [ - // "/node_modules/" - // ], + coveragePathIgnorePatterns: ['/src/types\\.ts$'], // Indicates which provider should be used to instrument code for coverage coverageProvider: 'v8', @@ -39,10 +39,11 @@ module.exports = { coverageReporters: ['text', 'html'], // An object that configures minimum threshold enforcement for coverage results + // TODO: Fix lack of coverage and restore to 100% coverageThreshold: { global: { branches: 80, - functions: 30, + functions: 27.27, lines: 55, statements: 55, }, @@ -97,7 +98,7 @@ module.exports = { // notifyMode: "failure-change", // A preset that is used as a base for Jest's configuration - // preset: undefined, + preset: 'ts-jest', // Run tests from one or more projects // projects: undefined, @@ -202,3 +203,5 @@ module.exports = { // Whether to use watchman for file crawling // watchman: true, }; + +export default config; diff --git a/package.json b/package.json index 2018408..8bd1b3b 100644 --- a/package.json +++ b/package.json @@ -7,12 +7,13 @@ "url": "https://github.com/MetaMask/eth-json-rpc-infura.git" }, "license": "ISC", - "main": "src/index.js", + "main": "dist/index.js", + "types": "dist/index.d.ts", "files": [ - "src/*.js" + "dist/" ], "scripts": { - "build": "echo 'nothing to build yet'", + "build": "tsc --project tsconfig.build.json", "build:clean": "rimraf dist && yarn build", "lint": "yarn lint:eslint && yarn lint:misc --check", "lint:eslint": "eslint . --cache --ext js,ts", @@ -24,9 +25,9 @@ "test:watch": "jest --watch" }, "dependencies": { - "eth-json-rpc-middleware": "^6.0.0", - "eth-rpc-errors": "^3.0.0", - "json-rpc-engine": "^5.3.0", + "eth-json-rpc-middleware": "^8.1.0", + "eth-rpc-errors": "^4.0.3", + "json-rpc-engine": "^6.1.0", "node-fetch": "^2.6.7" }, "devDependencies": { @@ -35,8 +36,15 @@ "@metamask/eslint-config": "^9.0.0", "@metamask/eslint-config-jest": "^9.0.0", "@metamask/eslint-config-nodejs": "^9.0.0", + "@metamask/eslint-config-typescript": "^9.0.1", + "@types/jest": "^26.0.13", + "@types/node": "^17.0.23", + "@types/node-fetch": "^2.6.1", + "@typescript-eslint/eslint-plugin": "^4.21.0", + "@typescript-eslint/parser": "^4.21.0", "eslint": "^7.23.0", "eslint-config-prettier": "^8.5.0", + "eslint-import-resolver-typescript": "^2.7.0", "eslint-plugin-import": "^2.25.4", "eslint-plugin-jest": "^24.3.4", "eslint-plugin-jsdoc": "^36.1.0", @@ -45,7 +53,10 @@ "jest": "^26.4.2", "prettier": "^2.6.1", "prettier-plugin-packagejson": "^2.2.17", - "rimraf": "^3.0.2" + "rimraf": "^3.0.2", + "ts-jest": "^26.3.0", + "ts-node": "^10.7.0", + "typescript": "~4.4.0" }, "engines": { "node": ">=12.0.0" @@ -57,8 +68,8 @@ "lavamoat": { "allowScripts": { "@lavamoat/preinstall-always-fail": false, - "eth-json-rpc-middleware>ethereumjs-util>ethereum-cryptography>keccak": true, - "eth-json-rpc-middleware>ethereumjs-util>ethereum-cryptography>secp256k1": true + "eth-json-rpc-middleware>eth-sig-util>ethereumjs-util>ethereum-cryptography>keccak": true, + "eth-json-rpc-middleware>eth-sig-util>ethereumjs-util>ethereum-cryptography>secp256k1": true } } } diff --git a/src/create-infura-middleware.test.ts b/src/create-infura-middleware.test.ts new file mode 100644 index 0000000..2c28486 --- /dev/null +++ b/src/create-infura-middleware.test.ts @@ -0,0 +1,51 @@ +import { createInfuraMiddleware } from '.'; + +describe('createInfuraMiddleware', () => { + it('throws when an empty set of options is given', () => { + expect(() => createInfuraMiddleware({} as any)).toThrow( + /Invalid value for 'projectId'/u, + ); + }); + + it('throws when the projectId is null', () => { + expect(() => createInfuraMiddleware({ projectId: null } as any)).toThrow( + /Invalid value for 'projectId'/u, + ); + }); + + it('throws when the projectId is undefined', () => { + expect(() => + createInfuraMiddleware({ projectId: undefined } as any), + ).toThrow(/Invalid value for 'projectId'/u); + }); + + it('throws when the projectId is an empty string', () => { + expect(() => createInfuraMiddleware({ projectId: '' })).toThrow( + /Invalid value for 'projectId'/u, + ); + }); + + it('throws when the projectId is not a string', () => { + expect(() => createInfuraMiddleware({ projectId: 42 } as any)).toThrow( + /Invalid value for 'projectId'/u, + ); + }); + + it('throws when headers is null', () => { + expect(() => + createInfuraMiddleware({ projectId: 'foo', headers: null } as any), + ).toThrow(/Invalid value for 'headers'/u); + }); + + it('throws when headers is an empty string', () => { + expect(() => + createInfuraMiddleware({ projectId: 'foo', headers: '' } as any), + ).toThrow(/Invalid value for 'headers'/u); + }); + + it('throws when headers is not an object', () => { + expect(() => + createInfuraMiddleware({ projectId: 'foo', headers: 42 } as any), + ).toThrow(/Invalid value for 'headers'/u); + }); +}); diff --git a/src/create-infura-middleware.ts b/src/create-infura-middleware.ts new file mode 100644 index 0000000..07827a8 --- /dev/null +++ b/src/create-infura-middleware.ts @@ -0,0 +1,218 @@ +import type { PendingJsonRpcResponse } from 'json-rpc-engine'; +import { createAsyncMiddleware } from 'json-rpc-engine'; +import type { EthereumRpcError } from 'eth-rpc-errors'; +import { ethErrors } from 'eth-rpc-errors'; +import fetch from 'node-fetch'; +import type { + ExtendedJsonRpcRequest, + InfuraJsonRpcSupportedNetwork, + RequestHeaders, +} from './types'; +import { fetchConfigFromReq } from './fetch-config-from-req'; + +export interface CreateInfuraMiddlewareOptions { + network?: InfuraJsonRpcSupportedNetwork; + maxAttempts?: number; + source?: string; + projectId: string; + headers?: Record; +} + +const RETRIABLE_ERRORS = [ + // ignore server overload errors + 'Gateway timeout', + 'ETIMEDOUT', + 'ECONNRESET', + // ignore server sent html error pages + // or truncated json responses + 'SyntaxError', +]; + +/** + * Builds [`json-rpc-engine`](https://github.com/MetaMask/json-rpc-engine)-compatible middleware designed + * for interfacing with Infura's JSON-RPC endpoints. + * + * @param opts - The options. + * @param opts.network - A network that Infura supports; plugs into + * `https://${network}.infura.io` (default: 'mainnet'). + * @param opts.maxAttempts - The number of times a request to Infura should be + * retried in the case of failure (default: 5). + * @param opts.source - A descriptor for the entity making the request; tracked + * by Infura for analytics purposes. + * @param opts.projectId - The Infura project id. + * @param opts.headers - Extra headers that will be used to make the request. + * @returns The `json-rpc-engine`-compatible middleware. + */ +export function createInfuraMiddleware({ + network = 'mainnet', + maxAttempts = 5, + source, + projectId, + headers = {}, +}: CreateInfuraMiddlewareOptions) { + // validate options + if (!projectId || typeof projectId !== 'string') { + throw new Error(`Invalid value for 'projectId': "${projectId}"`); + } + + if (!headers || typeof headers !== 'object') { + throw new Error(`Invalid value for 'headers': "${headers}"`); + } + + if (!maxAttempts) { + throw new Error( + `Invalid value for 'maxAttempts': "${maxAttempts}" (${typeof maxAttempts})`, + ); + } + + return createAsyncMiddleware(async (req, res) => { + // retry MAX_ATTEMPTS times, if error matches filter + for (let attempt = 1; attempt <= maxAttempts; attempt++) { + try { + // attempt request + await performFetch(network, projectId, headers, req, res, source); + // request was successful + break; + } catch (err: any) { + // an error was caught while performing the request + // if not retriable, resolve with the encountered error + if (!isRetriableError(err)) { + // abort with error + throw err; + } + // if no more attempts remaining, throw an error + const remainingAttempts = maxAttempts - attempt; + if (!remainingAttempts) { + const errMsg = `InfuraProvider - cannot complete request. All retries exhausted.\nOriginal Error:\n${err.toString()}\n\n`; + const retriesExhaustedErr = new Error(errMsg); + throw retriesExhaustedErr; + } + // otherwise, ignore error and retry again after timeout + await timeout(1000); + } + } + // request was handled correctly, end + }); +} + +/** + * Makes a request to Infura, updating the given response object if the response + * has a "successful" status code or throwing an error otherwise. + * + * @param network - A network that Infura supports; plugs into + * `https://${network}.infura.io`. + * @param projectId - The Infura project id. + * @param extraHeaders - Extra headers that will be used to make the request. + * @param req - The original request object obtained via the middleware stack. + * @param res - The original response object obtained via the middleware stack. + * @param source - A descriptor for the entity making the request; + * tracked by Infura for analytics purposes. + * @throws an error with a detailed message if the HTTP status code is anywhere + * outside 2xx, and especially if it is 405, 429, 503, or 504. + */ +async function performFetch( + network: InfuraJsonRpcSupportedNetwork, + projectId: string, + extraHeaders: RequestHeaders, + req: ExtendedJsonRpcRequest, + res: PendingJsonRpcResponse, + source: string | undefined, +): Promise { + const { fetchUrl, fetchParams } = fetchConfigFromReq({ + network, + projectId, + extraHeaders, + req, + source, + }); + const response = await fetch(fetchUrl, fetchParams); + const rawData = await response.text(); + // handle errors + if (!response.ok) { + switch (response.status) { + case 405: + throw ethErrors.rpc.methodNotFound(); + + case 429: + throw createRatelimitError(); + + case 503: + case 504: + throw createTimeoutError(); + + default: + throw createInternalError(rawData); + } + } + + // special case for now + if (req.method === 'eth_getBlockByNumber' && rawData === 'Not Found') { + res.result = null; + return; + } + + // parse JSON + const data = JSON.parse(rawData); + + // finally return result + res.result = data.result; + res.error = data.error; +} + +/** + * Builds a JSON-RPC 2.0 internal error object describing a rate-limiting + * error. + * + * @returns The error object. + */ +function createRatelimitError(): EthereumRpcError { + const msg = `Request is being rate limited.`; + return createInternalError(msg); +} + +/** + * Builds a JSON-RPC 2.0 internal error object describing a timeout error. + * + * @returns The error object. + */ +function createTimeoutError(): EthereumRpcError { + let msg = `Gateway timeout. The request took too long to process. `; + msg += `This can happen when querying logs over too wide a block range.`; + return createInternalError(msg); +} + +/** + * Builds a JSON-RPC 2.0 internal error object. + * + * @param msg - The message. + * @returns The error object. + */ +function createInternalError(msg: string): EthereumRpcError { + return ethErrors.rpc.internal(msg); +} + +/** + * Upon making a request, we may get an error that is temporary and + * intermittent. In these cases we can attempt the request again with the + * assumption that the error is unlikely to occur again. Here we determine if we + * have received such an error. + * + * @param err - The error object. + * @returns Whether the request that produced the error can be retried. + */ +function isRetriableError(err: any): boolean { + const errMessage = err.toString(); + return RETRIABLE_ERRORS.some((phrase) => errMessage.includes(phrase)); +} + +/** + * A utility function that promisifies `setTimeout`. + * + * @param length - The number of milliseconds to wait. + * @returns A promise that resolves after the given time has elapsed. + */ +function timeout(length: number): Promise { + return new Promise((resolve) => { + setTimeout(resolve, length); + }); +} diff --git a/src/create-provider.ts b/src/create-provider.ts new file mode 100644 index 0000000..c61fbf8 --- /dev/null +++ b/src/create-provider.ts @@ -0,0 +1,25 @@ +import { JsonRpcEngine } from 'json-rpc-engine'; +import { providerFromEngine } from 'eth-json-rpc-middleware'; +import type { SafeEventEmitterProvider } from 'eth-json-rpc-middleware'; +import { + createInfuraMiddleware, + CreateInfuraMiddlewareOptions, +} from './create-infura-middleware'; + +/** + * Creates a provider (as defined in + * [`eth-json-rpc-middleware`](https://github.com/MetaMask/eth-json-rpc-middleware) + * which is preloaded with middleware specialized for interfacing with Infura + * JSON-RPC endpoints. + * + * @param opts - Options to {@link createInfuraMiddleware}. + * @returns The provider as returned by `providerFromEngine` (a part of + * [`eth-json-rpc-middleware`](https://github.com/MetaMask/eth-json-rpc-middleware)). + */ +export function createProvider( + opts: CreateInfuraMiddlewareOptions, +): SafeEventEmitterProvider { + const engine = new JsonRpcEngine(); + engine.push(createInfuraMiddleware(opts)); + return providerFromEngine(engine); +} diff --git a/src/createProvider.js b/src/createProvider.js deleted file mode 100644 index 2028d82..0000000 --- a/src/createProvider.js +++ /dev/null @@ -1,21 +0,0 @@ -const RpcEngine = require('json-rpc-engine'); -const providerFromEngine = require('eth-json-rpc-middleware/providerFromEngine'); -const createInfuraMiddleware = require('.'); - -module.exports = createProvider; - -/** - * Creates a provider (as defined in - * [`eth-json-rpc-middleware`](https://github.com/MetaMask/eth-json-rpc-middleware) - * which is preloaded with middleware specialized for interfacing with Infura - * JSON-RPC endpoints. - * - * @param {object} opts - Options to {@link createInfuraMiddleware}. - * @returns {object} The provider as returned by `providerFromEngine` (a part of - * [`eth-json-rpc-middleware`](https://github.com/MetaMask/eth-json-rpc-middleware)). - */ -function createProvider(opts) { - const engine = new RpcEngine(); - engine.push(createInfuraMiddleware(opts)); - return providerFromEngine(engine); -} diff --git a/src/fetch-config-from-req.test.ts b/src/fetch-config-from-req.test.ts new file mode 100644 index 0000000..6802b1d --- /dev/null +++ b/src/fetch-config-from-req.test.ts @@ -0,0 +1,181 @@ +import { fetchConfigFromReq } from '.'; + +describe('fetchConfigFromReq', () => { + it('builds the URL and params for an Infura request based on the given network, JSON-RPC request, and project ID', () => { + const { fetchUrl, fetchParams } = fetchConfigFromReq({ + network: 'mainnet', + req: { + jsonrpc: '2.0', + id: 1, + method: 'eth_getBlockByNumber', + params: ['0x482103', true], + }, + projectId: 'abcdef1234567890', + }); + const decodedFetchParams = { + ...fetchParams, + body: JSON.parse(fetchParams.body), + }; + + expect(fetchUrl).toStrictEqual( + 'https://mainnet.infura.io/v3/abcdef1234567890', + ); + + expect(decodedFetchParams).toStrictEqual({ + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: { + id: 1, + jsonrpc: '2.0', + method: 'eth_getBlockByNumber', + params: ['0x482103', true], + }, + }); + }); + + it('uses the given source to add an Infura-Source header', () => { + const { fetchUrl, fetchParams } = fetchConfigFromReq({ + network: 'mainnet', + req: { + jsonrpc: '2.0', + id: 1, + method: 'eth_getBlockByNumber', + params: ['0x482103', true], + }, + projectId: 'abcdef1234567890', + source: 'eth-json-rpc-infura', + }); + const decodedFetchParams = { + ...fetchParams, + body: JSON.parse(fetchParams.body), + }; + + expect(fetchUrl).toStrictEqual( + 'https://mainnet.infura.io/v3/abcdef1234567890', + ); + + expect(decodedFetchParams).toStrictEqual({ + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + 'Infura-Source': 'eth-json-rpc-infura/internal', + }, + body: { + id: 1, + jsonrpc: '2.0', + method: 'eth_getBlockByNumber', + params: ['0x482103', true], + }, + }); + }); + + it('uses the origin in the JSON-RPC request instead of "internal" when building the Infura-Source header', () => { + const { fetchUrl, fetchParams } = fetchConfigFromReq({ + network: 'mainnet', + req: { + jsonrpc: '2.0', + id: 1, + method: 'eth_getBlockByNumber', + params: ['0x482103', true], + origin: 'happydapp.eth', + }, + projectId: 'abcdef1234567890', + source: 'eth-json-rpc-infura', + }); + const decodedFetchParams = { + ...fetchParams, + body: JSON.parse(fetchParams.body), + }; + + expect(fetchUrl).toStrictEqual( + 'https://mainnet.infura.io/v3/abcdef1234567890', + ); + + expect(decodedFetchParams).toStrictEqual({ + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + 'Infura-Source': 'eth-json-rpc-infura/happydapp.eth', + }, + body: { + id: 1, + jsonrpc: '2.0', + method: 'eth_getBlockByNumber', + params: ['0x482103', true], + }, + }); + }); + + it('allows custom headers to be specified', () => { + const { fetchUrl, fetchParams } = fetchConfigFromReq({ + network: 'mainnet', + req: { + jsonrpc: '2.0', + id: 1, + method: 'eth_getBlockByNumber', + params: ['0x482103', true], + }, + projectId: 'abcdef1234567890', + extraHeaders: { + 'User-Agent': 'app/1.0', + }, + }); + const decodedFetchParams = { + ...fetchParams, + body: JSON.parse(fetchParams.body), + }; + + expect(fetchUrl).toStrictEqual( + 'https://mainnet.infura.io/v3/abcdef1234567890', + ); + + expect(decodedFetchParams).toStrictEqual({ + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + 'User-Agent': 'app/1.0', + }, + body: { + id: 1, + jsonrpc: '2.0', + method: 'eth_getBlockByNumber', + params: ['0x482103', true], + }, + }); + }); + + it('strips non-standard keys from the given JSON-RPC request before building the resulting fetchParams', () => { + const { fetchUrl, fetchParams } = fetchConfigFromReq({ + network: 'mainnet', + req: { + jsonrpc: '2.0', + id: 1, + method: 'eth_getBlockByNumber', + params: ['0x482103', true], + foo: 'bar', + baz: 'qux', + } as any, + projectId: 'abcdef1234567890', + }); + const decodedFetchParams = Object.assign({}, fetchParams, { + body: JSON.parse(fetchParams.body), + }); + + expect(fetchUrl).toStrictEqual( + 'https://mainnet.infura.io/v3/abcdef1234567890', + ); + + expect(decodedFetchParams.body).toStrictEqual({ + id: 1, + jsonrpc: '2.0', + method: 'eth_getBlockByNumber', + params: ['0x482103', true], + }); + }); +}); diff --git a/src/fetch-config-from-req.ts b/src/fetch-config-from-req.ts new file mode 100644 index 0000000..e0a31fa --- /dev/null +++ b/src/fetch-config-from-req.ts @@ -0,0 +1,83 @@ +import type { JsonRpcRequest } from 'json-rpc-engine'; +import type { + ExtendedJsonRpcRequest, + RequestHeaders, + InfuraJsonRpcSupportedNetwork, +} from './types'; + +interface FetchConfig { + fetchUrl: string; + fetchParams: { + method: string; + headers: RequestHeaders; + body: string; + }; +} + +/** + * Determines the arguments to feed into `fetch` in order to make a request to + * Infura. + * + * @param options - The options. + * @param options.network - A network that Infura supports; plugs into + * `https://${network}.infura.io`. + * @param options.projectId - The Infura project id. + * @param options.extraHeaders - Extra headers that will be used to make the + * request. + * @param options.req - The original request object obtained via the + * middleware stack. + * @param options.source - A descriptor for the entity making the request; + * tracked by Infura for analytics purposes. + * @returns An object containing the URL and a bag of options, both of which + * will be passed to `fetch`. + */ +export function fetchConfigFromReq({ + network, + projectId, + extraHeaders = {}, + req, + source, +}: { + network: InfuraJsonRpcSupportedNetwork; + projectId: string; + extraHeaders?: RequestHeaders; + req: ExtendedJsonRpcRequest; + source?: string; +}): FetchConfig { + const requestOrigin = req.origin || 'internal'; + const headers = Object.assign({}, extraHeaders, { + Accept: 'application/json', + 'Content-Type': 'application/json', + }); + + if (source) { + headers['Infura-Source'] = `${source}/${requestOrigin}`; + } + + return { + fetchUrl: `https://${network}.infura.io/v3/${projectId}`, + fetchParams: { + method: 'POST', + headers, + body: JSON.stringify(normalizeReq(req)), + }, + }; +} + +/** + * Strips out extra keys from a request object that could be rejected by strict + * nodes like parity. + * + * @param req - The original request object obtained via the middleware stack. + * @returns An object that describes a JSON-RPC request. + */ +function normalizeReq( + req: ExtendedJsonRpcRequest, +): JsonRpcRequest { + return { + id: req.id, + jsonrpc: req.jsonrpc, + method: req.method, + params: req.params, + }; +} diff --git a/src/index.js b/src/index.js deleted file mode 100644 index 72f4ce3..0000000 --- a/src/index.js +++ /dev/null @@ -1,274 +0,0 @@ -const createAsyncMiddleware = require('json-rpc-engine/src/createAsyncMiddleware'); -const { ethErrors } = require('eth-rpc-errors'); -const fetch = require('node-fetch'); - -const RETRIABLE_ERRORS = [ - // ignore server overload errors - 'Gateway timeout', - 'ETIMEDOUT', - 'ECONNRESET', - // ignore server sent html error pages - // or truncated json responses - 'SyntaxError', -]; - -module.exports = createInfuraMiddleware; -module.exports.fetchConfigFromReq = fetchConfigFromReq; - -/** - * Arguments to {@link fetch}. - * - * @typedef {object} FetchConfig - * @property {string} fetchUrl - The URL to fetch. - * @property {object} fetchParams - The options object to pass to {@link fetch}. - * @property {string} fetchParams.method - The request method. - * @property {Record} fetchParams.headers - The request headers. - * @property {string} fetchParams.body - The request body. - */ - -/** - * Builds [`json-rpc-engine`](https://github.com/MetaMask/json-rpc-engine)-compatible middleware designed - * for interfacing with Infura's JSON-RPC endpoints. - * - * @param {object} opts - The options. - * @param {string} [opts.network] - A network that Infura supports; plugs into - * `https://${network}.infura.io` (default: 'mainnet'). - * @param {number} [opts.maxAttempts] - The number of times a request to Infura - * should be retried in the case of failure (default: 5). - * @param {string} [opts.source] - A descriptor for the entity making the - * request; tracked by Infura for analytics purposes. - * @param {string} opts.projectId - The Infura project id. - * @param {Record} opts.headers - Extra headers that will be - * used to make the request. - * @returns {Function} The middleware. - */ -function createInfuraMiddleware(opts) { - const network = opts.network || 'mainnet'; - const maxAttempts = opts.maxAttempts || 5; - const { source, projectId, headers = {} } = opts; - - // validate options - if (!projectId || typeof projectId !== 'string') { - throw new Error(`Invalid value for 'projectId': "${projectId}"`); - } - - if (!headers || typeof headers !== 'object') { - throw new Error(`Invalid value for 'headers': "${headers}"`); - } - - if (!maxAttempts) { - throw new Error( - `Invalid value for 'maxAttempts': "${maxAttempts}" (${typeof maxAttempts})`, - ); - } - - return createAsyncMiddleware(async (req, res) => { - // retry MAX_ATTEMPTS times, if error matches filter - for (let attempt = 1; attempt <= maxAttempts; attempt++) { - try { - // attempt request - await performFetch(network, projectId, headers, req, res, source); - // request was successful - break; - } catch (err) { - // an error was caught while performing the request - // if not retriable, resolve with the encountered error - if (!isRetriableError(err)) { - // abort with error - throw err; - } - // if no more attempts remaining, throw an error - const remainingAttempts = maxAttempts - attempt; - if (!remainingAttempts) { - const errMsg = `InfuraProvider - cannot complete request. All retries exhausted.\nOriginal Error:\n${err.toString()}\n\n`; - const retriesExhaustedErr = new Error(errMsg); - throw retriesExhaustedErr; - } - // otherwise, ignore error and retry again after timeout - await timeout(1000); - } - } - // request was handled correctly, end - }); -} - -/** - * A utility function that promisifies `setTimeout`. - * - * @param {number} length - The number of milliseconds to wait. - * @returns {Promise} A promise that resolves after the given time has - * elapsed. - */ -function timeout(length) { - return new Promise((resolve) => { - setTimeout(resolve, length); - }); -} - -/** - * Upon making a request, we may get an error that is temporary and - * intermittent. In these cases we can attempt the request again with the - * assumption that the error is unlikely to occur again. Here we determine if - * we have received such an error. - * - * @param {Error} err - The error object. - * @returns {boolean} Whether the request that produced the error can be retried. - */ -function isRetriableError(err) { - const errMessage = err.toString(); - return RETRIABLE_ERRORS.some((phrase) => errMessage.includes(phrase)); -} - -/** - * Makes a request to Infura, updating the given response object if the response - * has a "successful" status code or throwing an error otherwise. - * - * @param {string} network - A network that Infura supports; plugs into - * `https://${network}.infura.io`. - * @param {string} projectId - The Infura project id. - * @param {Record} extraHeaders - Extra headers that will be - * used to make the request. - * @param {object} req - The original request object obtained via the middleware - * stack. - * @param {object} res - The original response object obtained via the - * middleware stack. - * @param {string} source - A descriptor for the entity making the request; - * tracked by Infura for analytics purposes. - * @throws an error with a detailed message if the HTTP status code is anywhere - * outside 2xx, and especially if it is 405, 429, 503, or 504. - */ -async function performFetch( - network, - projectId, - extraHeaders, - req, - res, - source, -) { - const { fetchUrl, fetchParams } = fetchConfigFromReq({ - network, - projectId, - extraHeaders, - req, - source, - }); - const response = await fetch(fetchUrl, fetchParams); - const rawData = await response.text(); - // handle errors - if (!response.ok) { - switch (response.status) { - case 405: - throw ethErrors.rpc.methodNotFound(); - - case 429: - throw createRatelimitError(); - - case 503: - case 504: - throw createTimeoutError(); - - default: - throw createInternalError(rawData); - } - } - - // special case for now - if (req.method === 'eth_getBlockByNumber' && rawData === 'Not Found') { - res.result = null; - return; - } - - // parse JSON - const data = JSON.parse(rawData); - - // finally return result - res.result = data.result; - res.error = data.error; -} - -/** - * Determines the arguments to feed into `fetch`. - * - * @param {object} options - The options. - * @param {string} options.network - A network that Infura supports; plugs into - * `https://${network}.infura.io`. - * @param {string} options.projectId - The Infura project id. - * @param {Record} [options.extraHeaders] - Extra headers that - * will be used to make the request. - * @param {object} options.req - The original request object obtained via the - * middleware stack. - * @param {string} [options.source] - A descriptor for the entity making the - * request; tracked by Infura for analytics purposes. - * @returns {FetchConfig} An object containing the URL and a bag of options, - * both of which will be passed to `fetch`. - */ -function fetchConfigFromReq({ network, projectId, extraHeaders, req, source }) { - const requestOrigin = req.origin || 'internal'; - const headers = Object.assign({}, extraHeaders, { - Accept: 'application/json', - 'Content-Type': 'application/json', - }); - - if (source) { - headers['Infura-Source'] = `${source}/${requestOrigin}`; - } - - return { - fetchUrl: `https://${network}.infura.io/v3/${projectId}`, - fetchParams: { - method: 'POST', - headers, - body: JSON.stringify(normalizeReq(req)), - }, - }; -} - -/** - * Strips out extra keys from a request object that could be rejected by strict - * nodes like parity. - * - * @param {object} req - The original request object obtained via the - * middleware stack. - * @returns {Record<'id' | 'jsonrpc' | 'method' | 'params', any>} An object that - * describes a JSON-RPC request. - */ -function normalizeReq(req) { - return { - id: req.id, - jsonrpc: req.jsonrpc, - method: req.method, - params: req.params, - }; -} - -/** - * Builds a JSON-RPC 2.0 internal error object describing a rate-limiting - * error. - * - * @returns {EthereumRpcError} The error object. - */ -function createRatelimitError() { - const msg = `Request is being rate limited.`; - return createInternalError(msg); -} - -/** - * Builds a JSON-RPC 2.0 internal error object describing a timeout error. - * - * @returns {EthereumRpcError} The error object. - */ -function createTimeoutError() { - let msg = `Gateway timeout. The request took too long to process. `; - msg += `This can happen when querying logs over too wide a block range.`; - return createInternalError(msg); -} - -/** - * Builds a JSON-RPC 2.0 internal error object. - * - * @param {string} msg - The message. - * @returns {EthereumRpcError} The error object. - */ -function createInternalError(msg) { - return ethErrors.rpc.internal(msg); -} diff --git a/src/index.test.js b/src/index.test.js deleted file mode 100644 index 941cdd4..0000000 --- a/src/index.test.js +++ /dev/null @@ -1,225 +0,0 @@ -const createInfuraMiddleware = require('.'); - -const { fetchConfigFromReq } = createInfuraMiddleware; - -describe('eth-json-rpc-infura', () => { - describe('createInfuraMiddleware', () => { - it('throws when the projectId is a number', () => { - expect(() => createInfuraMiddleware({ projectId: 42 })).toThrow( - /Invalid value for 'projectId'/u, - ); - }); - - it('throws when the projectId is null', () => { - expect(() => createInfuraMiddleware({ projectId: null })).toThrow( - /Invalid value for 'projectId'/u, - ); - }); - - it('throws when the projectId is undefined', () => { - expect(() => createInfuraMiddleware({ projectId: undefined })).toThrow( - /Invalid value for 'projectId'/u, - ); - }); - - it('throws when the projectId is an empty string', () => { - expect(() => createInfuraMiddleware({ projectId: '' })).toThrow( - /Invalid value for 'projectId'/u, - ); - }); - - it('throws when headers is null', () => { - expect(() => - createInfuraMiddleware({ projectId: 'foo', headers: null }), - ).toThrow(/Invalid value for 'headers'/u); - }); - - it('throws when headers is a number', () => { - expect(() => - createInfuraMiddleware({ projectId: 'foo', headers: 42 }), - ).toThrow(/Invalid value for 'headers'/u); - }); - - it('throws when headers is an empty string', () => { - expect(() => - createInfuraMiddleware({ projectId: 'foo', headers: '' }), - ).toThrow(/Invalid value for 'headers'/u); - }); - }); - - describe('fetchConfigFromReq', () => { - it('builds the URL and params for an Infura request based on the given network, JSON-RPC request, and project ID', () => { - const { fetchUrl, fetchParams } = fetchConfigFromReq({ - network: 'mainnet', - req: { - jsonrpc: '2.0', - id: 1, - method: 'eth_getBlockByNumber', - params: ['0x482103', true], - }, - projectId: 'abcdef1234567890', - }); - const decodedFetchParams = Object.assign({}, fetchParams, { - body: JSON.parse(fetchParams.body), - }); - - expect(fetchUrl).toStrictEqual( - 'https://mainnet.infura.io/v3/abcdef1234567890', - ); - - expect(decodedFetchParams).toStrictEqual({ - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - }, - body: { - id: 1, - jsonrpc: '2.0', - method: 'eth_getBlockByNumber', - params: ['0x482103', true], - }, - }); - }); - - it('uses the given source to add an Infura-Source header', () => { - const { fetchUrl, fetchParams } = fetchConfigFromReq({ - network: 'mainnet', - req: { - jsonrpc: '2.0', - id: 1, - method: 'eth_getBlockByNumber', - params: ['0x482103', true], - }, - projectId: 'abcdef1234567890', - source: 'eth-json-rpc-infura', - }); - const decodedFetchParams = Object.assign({}, fetchParams, { - body: JSON.parse(fetchParams.body), - }); - - expect(fetchUrl).toStrictEqual( - 'https://mainnet.infura.io/v3/abcdef1234567890', - ); - - expect(decodedFetchParams).toStrictEqual({ - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - 'Infura-Source': 'eth-json-rpc-infura/internal', - }, - body: { - id: 1, - jsonrpc: '2.0', - method: 'eth_getBlockByNumber', - params: ['0x482103', true], - }, - }); - }); - - it('uses the origin in the JSON-RPC request instead of "internal" when building the Infura-Source header', () => { - const { fetchUrl, fetchParams } = fetchConfigFromReq({ - network: 'mainnet', - req: { - jsonrpc: '2.0', - id: 1, - method: 'eth_getBlockByNumber', - params: ['0x482103', true], - origin: 'happydapp.eth', - }, - projectId: 'abcdef1234567890', - source: 'eth-json-rpc-infura', - }); - const decodedFetchParams = Object.assign({}, fetchParams, { - body: JSON.parse(fetchParams.body), - }); - - expect(fetchUrl).toStrictEqual( - 'https://mainnet.infura.io/v3/abcdef1234567890', - ); - - expect(decodedFetchParams).toStrictEqual({ - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - 'Infura-Source': 'eth-json-rpc-infura/happydapp.eth', - }, - body: { - id: 1, - jsonrpc: '2.0', - method: 'eth_getBlockByNumber', - params: ['0x482103', true], - }, - }); - }); - - it('allows custom headers to be specified', () => { - const { fetchUrl, fetchParams } = fetchConfigFromReq({ - network: 'mainnet', - req: { - jsonrpc: '2.0', - id: 1, - method: 'eth_getBlockByNumber', - params: ['0x482103', true], - }, - projectId: 'abcdef1234567890', - extraHeaders: { - 'User-Agent': 'app/1.0', - }, - }); - const decodedFetchParams = Object.assign({}, fetchParams, { - body: JSON.parse(fetchParams.body), - }); - - expect(fetchUrl).toStrictEqual( - 'https://mainnet.infura.io/v3/abcdef1234567890', - ); - - expect(decodedFetchParams).toStrictEqual({ - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - 'User-Agent': 'app/1.0', - }, - body: { - id: 1, - jsonrpc: '2.0', - method: 'eth_getBlockByNumber', - params: ['0x482103', true], - }, - }); - }); - - it('strips non-standard keys from the given JSON-RPC request before building the resulting fetchParams', () => { - const { fetchUrl, fetchParams } = fetchConfigFromReq({ - network: 'mainnet', - req: { - jsonrpc: '2.0', - id: 1, - method: 'eth_getBlockByNumber', - params: ['0x482103', true], - foo: 'bar', - baz: 'qux', - }, - projectId: 'abcdef1234567890', - }); - const decodedFetchParams = Object.assign({}, fetchParams, { - body: JSON.parse(fetchParams.body), - }); - - expect(fetchUrl).toStrictEqual( - 'https://mainnet.infura.io/v3/abcdef1234567890', - ); - - expect(decodedFetchParams.body).toStrictEqual({ - id: 1, - jsonrpc: '2.0', - method: 'eth_getBlockByNumber', - params: ['0x482103', true], - }); - }); - }); -}); diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..c5d054d --- /dev/null +++ b/src/index.ts @@ -0,0 +1,3 @@ +export * from './create-infura-middleware'; +export * from './fetch-config-from-req'; +export * from './create-provider'; diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..cd9d8f2 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,28 @@ +import type { JsonRpcRequest } from 'json-rpc-engine'; + +export type RequestHeaders = Record; + +export type ExtendedJsonRpcRequest = JsonRpcRequest & { origin?: string }; + +/** + * These are networks: + * + * 1. for which Infura has released official, production support (see ) + * 2. which support the JSON-RPC 2.0 protocol + */ +export type InfuraJsonRpcSupportedNetwork = + | 'mainnet' + | 'ropsten' + | 'rinkeby' + | 'kovan' + | 'goerli' + | 'eth2-beacon-mainnet' + | 'filecoin' + | 'polygon-mainnet' + | 'polygon-mumbai' + | 'palm-mainnet' + | 'palm-testnet' + | 'optimism-mainnet' + | 'optimism-kovan' + | 'arbitrum-mainnet' + | 'arbitrum-rinkeby'; diff --git a/tsconfig.build.json b/tsconfig.build.json new file mode 100644 index 0000000..c6e00d6 --- /dev/null +++ b/tsconfig.build.json @@ -0,0 +1,13 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "declaration": true, + "inlineSources": true, + "noEmit": false, + "outDir": "dist", + "rootDir": "src", + "sourceMap": true + }, + "include": ["./src/**/*.ts"], + "exclude": ["./src/**/*.test.ts"] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..66a51f7 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "esModuleInterop": true, + "lib": ["ES2020"], + "module": "CommonJS", + "moduleResolution": "node", + "noEmit": true, + "strict": true, + "target": "es2017" + }, + "exclude": ["./dist/**/*"] +} diff --git a/yarn.lock b/yarn.lock index 9668043..2d87935 100644 --- a/yarn.lock +++ b/yarn.lock @@ -301,6 +301,18 @@ exec-sh "^0.3.2" minimist "^1.2.0" +"@cspotcode/source-map-consumer@0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b" + integrity sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg== + +"@cspotcode/source-map-support@0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz#4789840aa859e46d2f3173727ab707c66bf344f5" + integrity sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA== + dependencies: + "@cspotcode/source-map-consumer" "0.8.0" + "@es-joy/jsdoccomment@0.10.8": version "0.10.8" resolved "https://registry.yarnpkg.com/@es-joy/jsdoccomment/-/jsdoccomment-0.10.8.tgz#b3152887e25246410ed4ea569a55926ec13b2b05" @@ -580,11 +592,21 @@ resolved "https://registry.yarnpkg.com/@metamask/eslint-config-nodejs/-/eslint-config-nodejs-9.0.0.tgz#ec737a47c04febfb921ce844362d875ca2cae9e7" integrity sha512-kPUrMPdpGeapbdG+LxysnDNzM9SlBNUvqVl1XoKnOGjo1pbZXB8hOI36PT3IlR1qa2FJumKYfgDSu7JLmOLxqQ== +"@metamask/eslint-config-typescript@^9.0.1": + version "9.0.1" + resolved "https://registry.yarnpkg.com/@metamask/eslint-config-typescript/-/eslint-config-typescript-9.0.1.tgz#900d53579ce074734ac9bf4e3f66fc20b92bd6af" + integrity sha512-+W7MXCoq8Q29wvkAv0ycwKB82xMbl+LfkUoM8oWN4n7vyMDXgcgbNjY7ug+quJPZfDTJJ7fxgPmG8m4LrkEImw== + "@metamask/eslint-config@^9.0.0": version "9.0.0" resolved "https://registry.yarnpkg.com/@metamask/eslint-config/-/eslint-config-9.0.0.tgz#22d4911b705f7e4e566efbdda0e37912da33e30f" integrity sha512-mWlLGQKjXXFOj9EtDClKSoTLeQuPW2kM1w3EpUMf4goYAQ+kLXCCa8pEff6h8ApWAnjhYmXydA1znQ2J4XvD+A== +"@metamask/safe-event-emitter@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@metamask/safe-event-emitter/-/safe-event-emitter-2.0.0.tgz#af577b477c683fad17c619a78208cede06f9605c" + integrity sha512-/kSXhY692qiV1MXu6EeOZvg5nECLclxNXcKCxJ3cXQgYuRymRHpdx/t7JXfsK+JLjwA1e1c1/SBrlQYpusC29Q== + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -647,6 +669,26 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== +"@tsconfig/node10@^1.0.7": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9" + integrity sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg== + +"@tsconfig/node12@^1.0.7": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.9.tgz#62c1f6dee2ebd9aead80dc3afa56810e58e1a04c" + integrity sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw== + +"@tsconfig/node14@^1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.1.tgz#95f2d167ffb9b8d2068b0b235302fafd4df711f2" + integrity sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg== + +"@tsconfig/node16@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e" + integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA== + "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7": version "7.1.19" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.19.tgz#7b497495b7d1b4812bdb9d02804d0576f43ee460" @@ -721,6 +763,14 @@ dependencies: "@types/istanbul-lib-report" "*" +"@types/jest@^26.0.13": + version "26.0.24" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.24.tgz#943d11976b16739185913a1936e0de0c4a7d595a" + integrity sha512-E/X5Vib8BWqZNRlDxj9vYXhsDwPYbPINqKF9BsnSoon4RQ0D9moEuLD8txgyypFLH7J4+Lho9Nr/c8H0Fi+17w== + dependencies: + jest-diff "^26.0.0" + pretty-format "^26.0.0" + "@types/json-schema@^7.0.7": version "7.0.11" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" @@ -736,7 +786,15 @@ resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40" integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ== -"@types/node@*": +"@types/node-fetch@^2.6.1": + version "2.6.1" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.1.tgz#8f127c50481db65886800ef496f20bbf15518975" + integrity sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA== + dependencies: + "@types/node" "*" + form-data "^3.0.0" + +"@types/node@*", "@types/node@^17.0.23": version "17.0.23" resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.23.tgz#3b41a6e643589ac6442bdbd7a4a3ded62f33f7da" integrity sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw== @@ -782,7 +840,21 @@ dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/experimental-utils@^4.0.1": +"@typescript-eslint/eslint-plugin@^4.21.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz#c24dc7c8069c7706bc40d99f6fa87edcb2005276" + integrity sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg== + dependencies: + "@typescript-eslint/experimental-utils" "4.33.0" + "@typescript-eslint/scope-manager" "4.33.0" + debug "^4.3.1" + functional-red-black-tree "^1.0.1" + ignore "^5.1.8" + regexpp "^3.1.0" + semver "^7.3.5" + tsutils "^3.21.0" + +"@typescript-eslint/experimental-utils@4.33.0", "@typescript-eslint/experimental-utils@^4.0.1": version "4.33.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.33.0.tgz#6f2a786a4209fa2222989e9380b5331b2810f7fd" integrity sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q== @@ -794,6 +866,16 @@ eslint-scope "^5.1.1" eslint-utils "^3.0.0" +"@typescript-eslint/parser@^4.21.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.33.0.tgz#dfe797570d9694e560528d18eecad86c8c744899" + integrity sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA== + dependencies: + "@typescript-eslint/scope-manager" "4.33.0" + "@typescript-eslint/types" "4.33.0" + "@typescript-eslint/typescript-estree" "4.33.0" + debug "^4.3.1" + "@typescript-eslint/scope-manager@4.33.0": version "4.33.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz#d38e49280d983e8772e29121cf8c6e9221f280a3" @@ -856,12 +938,17 @@ acorn-walk@^7.1.1: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== +acorn-walk@^8.1.1: + version "8.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" + integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== + acorn@^7.1.1, acorn@^7.4.0: version "7.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.2.4: +acorn@^8.2.4, acorn@^8.4.1: version "8.7.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf" integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ== @@ -958,6 +1045,11 @@ are-we-there-yet@~1.1.2: delegates "^1.0.0" readable-stream "^2.0.6" +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -1224,6 +1316,13 @@ browserslist@^4.17.5: node-releases "^2.0.2" picocolors "^1.0.0" +bs-logger@0.x: + version "0.2.6" + resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" + integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== + dependencies: + fast-json-stable-stringify "2.x" + bs58@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" @@ -1252,7 +1351,7 @@ btoa@^1.2.1: resolved "https://registry.yarnpkg.com/btoa/-/btoa-1.2.1.tgz#01a9909f8b2c93f6bf680ba26131eb30f7fa3d73" integrity sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g== -buffer-from@^1.0.0: +buffer-from@1.x, buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== @@ -1514,6 +1613,11 @@ create-hmac@^1.1.4, create-hmac@^1.1.7: safe-buffer "^5.0.1" sha.js "^2.4.8" +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + cross-spawn@^6.0.0: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" @@ -1567,7 +1671,7 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" -debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2: +debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -1667,6 +1771,11 @@ diff-sequences@^26.6.2: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q== +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + diff@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" @@ -1842,6 +1951,17 @@ eslint-import-resolver-node@^0.3.6: debug "^3.2.7" resolve "^1.20.0" +eslint-import-resolver-typescript@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.7.0.tgz#1f9d391b636dccdbaa4a3b1a87eb9a8237e23963" + integrity sha512-MNHS3u5pebvROX4MjGP9coda589ZGfL1SqdxUV4kSrcclfDRWvNE2D+eljbnWVMvWDVRgT89nhscMHPKYGcObQ== + dependencies: + debug "^4.3.4" + glob "^7.2.0" + is-glob "^4.0.3" + resolve "^1.22.0" + tsconfig-paths "^3.14.1" + eslint-module-utils@^2.7.2: version "2.7.3" resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz#ad7e3a10552fdd0642e1e55292781bd6e34876ee" @@ -2039,35 +2159,35 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== -eth-json-rpc-middleware@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/eth-json-rpc-middleware/-/eth-json-rpc-middleware-6.0.0.tgz#4fe16928b34231a2537856f08a5ebbc3d0c31175" - integrity sha512-qqBfLU2Uq1Ou15Wox1s+NX05S9OcAEL4JZ04VZox2NS0U+RtCMjSxzXhLFWekdShUPZ+P8ax3zCO2xcPrp6XJQ== +eth-block-tracker@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/eth-block-tracker/-/eth-block-tracker-5.0.1.tgz#c5ad39902bd0454223b601ec0874f9fcc9f30eed" + integrity sha512-NVs+JDSux0FdmOrl3A2YDcQFkkYf9/qW9irvPmtC7bhMoPAe6oBlaqqe/m9Ixh5rkKqAox4mEyWGpsFmf/IsNw== dependencies: + "@metamask/safe-event-emitter" "^2.0.0" + json-rpc-random-id "^1.0.1" + pify "^3.0.0" + +eth-json-rpc-middleware@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/eth-json-rpc-middleware/-/eth-json-rpc-middleware-8.1.0.tgz#d2bf980768b43dcbcb7830ed1fcc34a16f63074d" + integrity sha512-0ZZDr6KoddqN4N100a7YWzLTYrVN0P49DJ3Su1vsRavPpieq92rgZymnQcr0tLFBQDEJg1MVqLex46fA0GEkDA== + dependencies: + "@metamask/safe-event-emitter" "^2.0.0" btoa "^1.2.1" clone "^2.1.1" - eth-query "^2.1.2" - eth-rpc-errors "^3.0.0" + eth-block-tracker "^5.0.1" + eth-rpc-errors "^4.0.3" eth-sig-util "^1.4.2" - ethereumjs-util "^5.1.2" - json-rpc-engine "^5.3.0" + json-rpc-engine "^6.1.0" json-stable-stringify "^1.0.1" - node-fetch "^2.6.1" + node-fetch "^2.6.7" pify "^3.0.0" - safe-event-emitter "^1.0.1" -eth-query@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/eth-query/-/eth-query-2.1.2.tgz#d6741d9000106b51510c72db92d6365456a6da5e" - integrity sha1-1nQdkAAQa1FRDHLbktY2VFam2l4= - dependencies: - json-rpc-random-id "^1.0.0" - xtend "^4.0.1" - -eth-rpc-errors@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/eth-rpc-errors/-/eth-rpc-errors-3.0.0.tgz#d7b22653c70dbf9defd4ef490fd08fe70608ca10" - integrity sha512-iPPNHPrLwUlR9xCSYm7HHQjWBasor3+KZfRvwEWxMz3ca0yqnlBeJrnyphkGIXZ4J7AMAaOLmwy4AWhnxOiLxg== +eth-rpc-errors@^4.0.2, eth-rpc-errors@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/eth-rpc-errors/-/eth-rpc-errors-4.0.3.tgz#6ddb6190a4bf360afda82790bb7d9d5e724f423a" + integrity sha512-Z3ymjopaoft7JDoxZcEb3pwdGh7yiYMhOwm2doUt6ASXlMavpNlK6Cre0+IMl2VSGyEU9rkiperQhp5iRxn5Pg== dependencies: fast-safe-stringify "^2.0.6" @@ -2107,7 +2227,7 @@ ethereum-cryptography@^0.1.3: bn.js "^4.11.8" ethereumjs-util "^6.0.0" -ethereumjs-util@^5.1.1, ethereumjs-util@^5.1.2: +ethereumjs-util@^5.1.1: version "5.2.1" resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz#a833f0e5fca7e5b361384dc76301a721f537bf65" integrity sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ== @@ -2141,11 +2261,6 @@ ethjs-util@0.1.6, ethjs-util@^0.1.3: is-hex-prefixed "1.0.0" strip-hex-prefix "1.0.0" -events@^3.0.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" - integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== - evp_bytestokey@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" @@ -2297,7 +2412,7 @@ fast-glob@^3.0.3, fast-glob@^3.2.9: merge2 "^1.3.0" micromatch "^4.0.4" -fast-json-stable-stringify@^2.0.0: +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== @@ -2529,7 +2644,7 @@ glob-parent@^5.1.2: dependencies: is-glob "^4.0.1" -glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: +glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== @@ -2763,7 +2878,7 @@ ignore@^4.0.6: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== -ignore@^5.1.1, ignore@^5.2.0: +ignore@^5.1.1, ignore@^5.1.8, ignore@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== @@ -3197,7 +3312,7 @@ jest-config@^26.6.3: micromatch "^4.0.2" pretty-format "^26.6.2" -jest-diff@^26.6.2: +jest-diff@^26.0.0, jest-diff@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394" integrity sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA== @@ -3463,7 +3578,7 @@ jest-snapshot@^26.6.2: pretty-format "^26.6.2" semver "^7.3.2" -jest-util@^26.6.2: +jest-util@^26.1.0, jest-util@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.6.2.tgz#907535dbe4d5a6cb4c47ac9b926f6af29576cbc1" integrity sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q== @@ -3589,15 +3704,15 @@ json-parse-even-better-errors@^2.3.0: resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== -json-rpc-engine@^5.3.0: - version "5.4.0" - resolved "https://registry.yarnpkg.com/json-rpc-engine/-/json-rpc-engine-5.4.0.tgz#75758609d849e1dba1e09021ae473f3ab63161e5" - integrity sha512-rAffKbPoNDjuRnXkecTjnsE3xLLrb00rEkdgalINhaYVYIxDwWtvYBr9UFbhTvPB1B2qUOLoFd/cV6f4Q7mh7g== +json-rpc-engine@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/json-rpc-engine/-/json-rpc-engine-6.1.0.tgz#bf5ff7d029e1c1bf20cb6c0e9f348dcd8be5a393" + integrity sha512-NEdLrtrq1jUZyfjkr9OCz9EzCNhnRyWtt1PAnvnhwy6e8XETS0Dtc+ZNCO2gvuAoKsIn2+vCSowXTYE4CkgnAQ== dependencies: - eth-rpc-errors "^3.0.0" - safe-event-emitter "^1.0.1" + "@metamask/safe-event-emitter" "^2.0.0" + eth-rpc-errors "^4.0.2" -json-rpc-random-id@^1.0.0: +json-rpc-random-id@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-rpc-random-id/-/json-rpc-random-id-1.0.1.tgz#ba49d96aded1444dbb8da3d203748acbbcdec8c8" integrity sha1-uknZat7RRE27jaPSA3SKy7zeyMg= @@ -3634,6 +3749,11 @@ json-stringify-safe@~5.0.1: resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= +json5@2.x, json5@^2.1.2: + version "2.2.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" + integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== + json5@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" @@ -3641,11 +3761,6 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" -json5@^2.1.2: - version "2.2.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" - integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== - jsonify@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" @@ -3750,7 +3865,7 @@ lodash.truncate@^4.4.2: resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= -lodash@^4.17.21, lodash@^4.7.0: +lodash@4.x, lodash@^4.17.21, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -3769,6 +3884,11 @@ make-dir@^3.0.0: dependencies: semver "^6.0.0" +make-error@1.x, make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + makeerror@1.0.12: version "1.0.12" resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" @@ -3896,7 +4016,7 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp@^1.0.3: +mkdirp@1.x, mkdirp@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== @@ -3948,7 +4068,7 @@ node-addon-api@^2.0.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== -node-fetch@^2.6.1, node-fetch@^2.6.7: +node-fetch@^2.6.7: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== @@ -4353,7 +4473,7 @@ prettier@^2.6.1: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.6.1.tgz#d472797e0d7461605c1609808e27b80c0f9cfe17" integrity sha512-8UVbTBYGwN37Bs9LERmxCPjdvPxlEowx2urIL6urHzdb3SDq4B/Z6xLFCblrSnE4iKWcS6ziJ3aOYrc1kz/E2A== -pretty-format@^26.6.2: +pretty-format@^26.0.0, pretty-format@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93" integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg== @@ -4566,7 +4686,7 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@^1.10.0, resolve@^1.10.1, resolve@^1.18.1, resolve@^1.20.0: +resolve@^1.10.0, resolve@^1.10.1, resolve@^1.18.1, resolve@^1.20.0, resolve@^1.22.0: version "1.22.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== @@ -4629,13 +4749,6 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-event-emitter@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/safe-event-emitter/-/safe-event-emitter-1.0.1.tgz#5b692ef22329ed8f69fdce607e50ca734f6f20af" - integrity sha512-e1wFe99A91XYYxoQbcq2ZJUWurxEyP8vfz7A7vuUe1s95q8r5ebraVaA1BukYJcpM6V16ugWoD9vngi8Ccu5fg== - dependencies: - events "^3.0.0" - safe-regex@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" @@ -4689,18 +4802,18 @@ secp256k1@^4.0.1: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@^6.0.0, semver@^6.1.0, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - -semver@^7.2.1, semver@^7.3.2, semver@^7.3.5: +semver@7.x, semver@^7.2.1, semver@^7.3.2, semver@^7.3.5: version "7.3.5" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== dependencies: lru-cache "^6.0.0" +semver@^6.0.0, semver@^6.1.0, semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" @@ -5200,7 +5313,42 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= -tsconfig-paths@^3.12.0: +ts-jest@^26.3.0: + version "26.5.6" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-26.5.6.tgz#c32e0746425274e1dfe333f43cd3c800e014ec35" + integrity sha512-rua+rCP8DxpA8b4DQD/6X2HQS8Zy/xzViVYfEs2OQu68tkCuKLV0Md8pmX55+W24uRIyAsf/BajRfxOs+R2MKA== + dependencies: + bs-logger "0.x" + buffer-from "1.x" + fast-json-stable-stringify "2.x" + jest-util "^26.1.0" + json5 "2.x" + lodash "4.x" + make-error "1.x" + mkdirp "1.x" + semver "7.x" + yargs-parser "20.x" + +ts-node@^10.7.0: + version "10.7.0" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.7.0.tgz#35d503d0fab3e2baa672a0e94f4b40653c2463f5" + integrity sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A== + dependencies: + "@cspotcode/source-map-support" "0.7.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.0" + yn "3.1.1" + +tsconfig-paths@^3.12.0, tsconfig-paths@^3.14.1: version "3.14.1" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a" integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ== @@ -5280,6 +5428,11 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" +typescript@~4.4.0: + version "4.4.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.4.tgz#2cd01a1a1f160704d3101fd5a58ff0f9fcb8030c" + integrity sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA== + unbox-primitive@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" @@ -5345,6 +5498,11 @@ uuid@^8.3.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== +v8-compile-cache-lib@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz#0582bcb1c74f3a2ee46487ceecf372e46bce53e8" + integrity sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA== + v8-compile-cache@^2.0.3: version "2.3.0" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" @@ -5531,11 +5689,6 @@ xmlchars@^2.2.0: resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== -xtend@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" - integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== - y18n@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" @@ -5551,6 +5704,11 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== +yargs-parser@20.x, yargs-parser@^20.2.2: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + yargs-parser@^18.1.2: version "18.1.3" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" @@ -5559,11 +5717,6 @@ yargs-parser@^18.1.2: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^20.2.2: - version "20.2.9" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" - integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== - yargs-parser@^21.0.0: version "21.0.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.0.1.tgz#0267f286c877a4f0f728fceb6f8a3e4cb95c6e35" @@ -5611,3 +5764,8 @@ yargs@^17.0.1: string-width "^4.2.3" y18n "^5.0.5" yargs-parser "^21.0.0" + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==