From f690cc8b741bb0fa1062a398b576ba4ccf61e392 Mon Sep 17 00:00:00 2001 From: legobeat <109787230+legobeat@users.noreply.github.com> Date: Mon, 4 Sep 2023 08:44:55 +0000 Subject: [PATCH 1/7] deps: extension-port-stream@^2.0.1->^2.1.1 (#273) Drops deprecated webextension-polyfill-ts; dedupe webextension-polyfill --- package.json | 2 +- yarn.lock | 30 +++++++----------------------- 2 files changed, 8 insertions(+), 24 deletions(-) diff --git a/package.json b/package.json index 3ed90826..258224c4 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "@metamask/safe-event-emitter": "^3.0.0", "detect-browser": "^5.2.0", "eth-rpc-errors": "^4.0.2", - "extension-port-stream": "^2.0.1", + "extension-port-stream": "^2.1.1", "fast-deep-equal": "^3.1.3", "is-stream": "^2.0.0", "json-rpc-engine": "^6.1.0", diff --git a/yarn.lock b/yarn.lock index 08f86916..1c9faa21 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1036,7 +1036,7 @@ __metadata: eslint-plugin-node: ^11.1.0 eslint-plugin-prettier: ^4.2.1 eth-rpc-errors: ^4.0.2 - extension-port-stream: ^2.0.1 + extension-port-stream: ^2.1.1 fast-deep-equal: ^3.1.3 is-stream: ^2.0.0 jest: ^28.1.3 @@ -3234,12 +3234,12 @@ __metadata: languageName: node linkType: hard -"extension-port-stream@npm:^2.0.1": - version: 2.0.1 - resolution: "extension-port-stream@npm:2.0.1" +"extension-port-stream@npm:^2.1.1": + version: 2.1.1 + resolution: "extension-port-stream@npm:2.1.1" dependencies: - webextension-polyfill-ts: ^0.22.0 - checksum: e127fd94a9b7b2b847d5f292fa940b6f63d1088ea5ed6a1e3142628b358a503881f1a04e2d8ad5aec2642f7672e054e16accd933bf9cdcfa75465aba32470d07 + webextension-polyfill: ">=0.10.0 <1.0" + checksum: aee8bbeb2ed6f69a62f58a89580e0e9002dadb11062edbaedb7bb04cfc5a5e0b0d3980bfeaa1c3ee7e08dec7e5fac26e25497fc2f82000db7653442bd5eca157 languageName: node linkType: hard @@ -6875,29 +6875,13 @@ __metadata: languageName: node linkType: hard -"webextension-polyfill-ts@npm:^0.22.0": - version: 0.22.0 - resolution: "webextension-polyfill-ts@npm:0.22.0" - dependencies: - webextension-polyfill: ^0.7.0 - checksum: b7d60c787c2041458117f837914b6bc4f03c1685174ff7b751ad19192e232fa7e71a0ac7a22d73e898856a86de198e61e9cd59c63764279127c7ee973f3202d8 - languageName: node - linkType: hard - -"webextension-polyfill@npm:^0.10.0": +"webextension-polyfill@npm:>=0.10.0 <1.0, webextension-polyfill@npm:^0.10.0": version: 0.10.0 resolution: "webextension-polyfill@npm:0.10.0" checksum: 4a59036bda571360c2c0b2fb03fe1dc244f233946bcf9a6766f677956c40fd14d270aaa69cdba95e4ac521014afbe4008bfa5959d0ac39f91c990eb206587f91 languageName: node linkType: hard -"webextension-polyfill@npm:^0.7.0": - version: 0.7.0 - resolution: "webextension-polyfill@npm:0.7.0" - checksum: fb738a5de07feb593875e02f25c3ab4276c8736118929556c8d4bdf965bb0f11c96ea263cd397b9b21259e8faf2dce2eaaa42ce08c922d96de7adb5896ec7d10 - languageName: node - linkType: hard - "webidl-conversions@npm:^7.0.0": version: 7.0.0 resolution: "webidl-conversions@npm:7.0.0" From 74b516555a90b8ba19a4a704ae08f73533497da1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 17:53:27 +0900 Subject: [PATCH 2/7] 11.1.2 (#278) --------- Co-authored-by: github-actions Co-authored-by: legobt <6wbvkn0j@anonaddy.me> --- CHANGELOG.md | 7 ++++++- package.json | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8e97338..7ad5332e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [11.1.2] +### Changed +- Update `extension-port-stream` to `^2.1.1` ([#273](https://github.com/MetaMask/providers/pull/273)) + ## [11.1.1] ### Changed - Update `fast-deep-equal` ([#258](https://github.com/MetaMask/providers/pull/258)) @@ -228,7 +232,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 added deprecation warnings for them ([#30](https://github.com/MetaMask/providers/pull/30)) - Un-deprecated `sendAsync` ([#29](https://github.com/MetaMask/providers/pull/29)) -[Unreleased]: https://github.com/MetaMask/providers/compare/v11.1.1...HEAD +[Unreleased]: https://github.com/MetaMask/providers/compare/v11.1.2...HEAD +[11.1.2]: https://github.com/MetaMask/providers/compare/v11.1.1...v11.1.2 [11.1.1]: https://github.com/MetaMask/providers/compare/v11.1.0...v11.1.1 [11.1.0]: https://github.com/MetaMask/providers/compare/v11.0.0...v11.1.0 [11.0.0]: https://github.com/MetaMask/providers/compare/v10.2.1...v11.0.0 diff --git a/package.json b/package.json index 258224c4..5fbaa06d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@metamask/providers", - "version": "11.1.1", + "version": "11.1.2", "description": "A JavaScript Ethereum provider that connects to the wallet over a stream.", "keywords": [ "MetaMask", From 080979dcf12fc3c7e82a30e2c1504138c1354892 Mon Sep 17 00:00:00 2001 From: Frederik Bolding Date: Tue, 5 Sep 2023 09:01:12 +0200 Subject: [PATCH 3/7] Use `@metamask/json-rpc-engine` and `@metamask/rpc-errors` (#253) * Use metamask/json-rpc-engine@^7.1.1 and @metamask/rpc-errors@6.0.0 * update @metamask/utils usage * deps: @metamask/json-rpc-engine@^7.0.0->^7.1.1 * deps: @noble/hashes@1.3.1->1.3.2 * test: type fixes --------- Co-authored-by: legobeat <109787230+legobeat@users.noreply.github.com> Co-authored-by: legobt <6wbvkn0j@anonaddy.me> --- package.json | 5 +- src/BaseProvider.ts | 39 ++- src/MetaMaskInpageProvider.test.ts | 4 +- src/MetaMaskInpageProvider.ts | 27 +- src/StreamProvider.test.ts | 5 +- src/StreamProvider.ts | 5 +- .../createExternalExtensionProvider.test.ts | 4 +- .../createRpcWarningMiddleware.test.ts | 162 +++++++----- src/middleware/createRpcWarningMiddleware.ts | 5 +- src/siteMetadata.ts | 2 +- src/utils.ts | 16 +- test/mocks/MockConnectionStream.ts | 14 +- test/mocks/MockPort.ts | 21 +- yarn.lock | 237 ++++++++++++++++-- 14 files changed, 388 insertions(+), 158 deletions(-) diff --git a/package.json b/package.json index 5fbaa06d..513c2862 100644 --- a/package.json +++ b/package.json @@ -37,14 +37,15 @@ "test:watch": "jest --watch" }, "dependencies": { + "@metamask/json-rpc-engine": "^7.1.1", "@metamask/object-multiplex": "^1.1.0", + "@metamask/rpc-errors": "^6.0.0", "@metamask/safe-event-emitter": "^3.0.0", + "@metamask/utils": "^8.1.0", "detect-browser": "^5.2.0", - "eth-rpc-errors": "^4.0.2", "extension-port-stream": "^2.1.1", "fast-deep-equal": "^3.1.3", "is-stream": "^2.0.0", - "json-rpc-engine": "^6.1.0", "json-rpc-middleware-stream": "^4.2.1", "pump": "^3.0.0", "webextension-polyfill": "^0.10.0" diff --git a/src/BaseProvider.ts b/src/BaseProvider.ts index 3ab1e17d..db1459d1 100644 --- a/src/BaseProvider.ts +++ b/src/BaseProvider.ts @@ -1,14 +1,15 @@ +import { JsonRpcEngine, JsonRpcMiddleware } from '@metamask/json-rpc-engine'; +import { rpcErrors, JsonRpcError } from '@metamask/rpc-errors'; import SafeEventEmitter from '@metamask/safe-event-emitter'; -import { ethErrors, EthereumRpcError } from 'eth-rpc-errors'; -import dequal from 'fast-deep-equal'; import { - JsonRpcEngine, JsonRpcRequest, JsonRpcId, - JsonRpcVersion, + JsonRpcVersion2, JsonRpcSuccess, - JsonRpcMiddleware, -} from 'json-rpc-engine'; + JsonRpcParams, + Json, +} from '@metamask/utils'; +import dequal from 'fast-deep-equal'; import messages from './messages'; import { @@ -20,7 +21,7 @@ import { export type UnvalidatedJsonRpcRequest = { id?: JsonRpcId; - jsonrpc?: JsonRpcVersion; + jsonrpc?: JsonRpcVersion2; method: string; params?: unknown; }; @@ -37,10 +38,10 @@ export type BaseProviderOptions = { maxEventListeners?: number; /** - * `json-rpc-engine` middleware. The middleware will be inserted in the given + * `@metamask/json-rpc-engine` middleware. The middleware will be inserted in the given * order immediately after engine initialization. */ - rpcMiddleware?: JsonRpcMiddleware[]; + rpcMiddleware?: JsonRpcMiddleware[]; }; export type RequestArguments = { @@ -169,7 +170,7 @@ export abstract class BaseProvider extends SafeEventEmitter { */ async request(args: RequestArguments): Promise> { if (!args || typeof args !== 'object' || Array.isArray(args)) { - throw ethErrors.rpc.invalidRequest({ + throw rpcErrors.invalidRequest({ message: messages.errors.invalidRequestArgs(), data: args, }); @@ -178,7 +179,7 @@ export abstract class BaseProvider extends SafeEventEmitter { const { method, params } = args; if (typeof method !== 'string' || method.length === 0) { - throw ethErrors.rpc.invalidRequest({ + throw rpcErrors.invalidRequest({ message: messages.errors.invalidRequestMethod(), data: args, }); @@ -189,7 +190,7 @@ export abstract class BaseProvider extends SafeEventEmitter { !Array.isArray(params) && (typeof params !== 'object' || params === null) ) { - throw ethErrors.rpc.invalidRequest({ + throw rpcErrors.invalidRequest({ message: messages.errors.invalidRequestParams(), data: args, }); @@ -285,15 +286,9 @@ export abstract class BaseProvider extends SafeEventEmitter { callback(error, response); }; } - return this._rpcEngine.handle( - payload as JsonRpcRequest, - callbackWrapper, - ); + return this._rpcEngine.handle(payload as JsonRpcRequest, callbackWrapper); } - return this._rpcEngine.handle( - payload as JsonRpcRequest[], - callbackWrapper, - ); + return this._rpcEngine.handle(payload as JsonRpcRequest[], callbackWrapper); } /** @@ -331,13 +326,13 @@ export abstract class BaseProvider extends SafeEventEmitter { let error; if (isRecoverable) { - error = new EthereumRpcError( + error = new JsonRpcError( 1013, // Try again later errorMessage ?? messages.errors.disconnected(), ); this._log.debug(error); } else { - error = new EthereumRpcError( + error = new JsonRpcError( 1011, // Internal error errorMessage ?? messages.errors.permanentlyDisconnected(), ); diff --git a/src/MetaMaskInpageProvider.test.ts b/src/MetaMaskInpageProvider.test.ts index ac8a375d..744bb6a3 100644 --- a/src/MetaMaskInpageProvider.test.ts +++ b/src/MetaMaskInpageProvider.test.ts @@ -1,4 +1,4 @@ -import { JsonRpcRequest } from 'json-rpc-engine'; +import { JsonRpcRequest } from '@metamask/utils'; import messages from './messages'; import { @@ -58,7 +58,7 @@ async function getInitializedProvider({ onMethodCalled?: { substream: string; method: string; - callback: (data: JsonRpcRequest) => void; + callback: (data: JsonRpcRequest) => void; }[]; } = {}): Promise { const onWrite = jest.fn(); diff --git a/src/MetaMaskInpageProvider.ts b/src/MetaMaskInpageProvider.ts index cdb07167..d9e03d1f 100644 --- a/src/MetaMaskInpageProvider.ts +++ b/src/MetaMaskInpageProvider.ts @@ -1,8 +1,8 @@ -import { ethErrors } from 'eth-rpc-errors'; -import type { JsonRpcRequest, JsonRpcResponse } from 'json-rpc-engine'; +import { rpcErrors } from '@metamask/rpc-errors'; +import type { Json, JsonRpcRequest, JsonRpcResponse } from '@metamask/utils'; import type { Duplex } from 'stream'; -import type { UnvalidatedJsonRpcRequest } from './BaseProvider'; +import { UnvalidatedJsonRpcRequest } from './BaseProvider'; import messages from './messages'; import { sendSiteMetadata } from './siteMetadata'; import { @@ -22,7 +22,7 @@ export type SendSyncJsonRpcRequest = { | 'eth_coinbase' | 'eth_uninstallFilter' | 'net_version'; -} & JsonRpcRequest; +} & JsonRpcRequest; type WarningEventName = keyof SentWarningsState['events']; @@ -171,8 +171,8 @@ export class MetaMaskInpageProvider extends AbstractStreamProvider { * @param callback - The callback function. */ sendAsync( - payload: JsonRpcRequest, - callback: (error: Error | null, result?: JsonRpcResponse) => void, + payload: JsonRpcRequest, + callback: (error: Error | null, result?: JsonRpcResponse) => void, ): void { this._rpcRequest(payload, callback); } @@ -283,7 +283,10 @@ export class MetaMaskInpageProvider extends AbstractStreamProvider { * @returns A Promise that resolves with the JSON-RPC response object for the * request. */ - send(method: string, params?: T[]): Promise>; + send( + method: string, + params?: T[], + ): Promise>; /** * Submits an RPC request per the given JSON-RPC request object. @@ -293,8 +296,8 @@ export class MetaMaskInpageProvider extends AbstractStreamProvider { * @param callback - An error-first callback that will receive the JSON-RPC * response object. */ - send( - payload: JsonRpcRequest, + send( + payload: JsonRpcRequest, callback: (error: Error | null, result?: JsonRpcResponse) => void, ): void; @@ -306,7 +309,7 @@ export class MetaMaskInpageProvider extends AbstractStreamProvider { * @param payload - A JSON-RPC request object. * @returns A JSON-RPC response object. */ - send(payload: SendSyncJsonRpcRequest): JsonRpcResponse; + send(payload: SendSyncJsonRpcRequest): JsonRpcResponse; // eslint-disable-next-line @typescript-eslint/promise-function-async send(methodOrPayload: unknown, callbackOrArgs?: unknown): unknown { @@ -335,7 +338,7 @@ export class MetaMaskInpageProvider extends AbstractStreamProvider { typeof callbackOrArgs === 'function' ) { return this._rpcRequest( - methodOrPayload as JsonRpcRequest, + methodOrPayload as JsonRpcRequest, callbackOrArgs as (...args: unknown[]) => void, ); } @@ -412,7 +415,7 @@ export class MetaMaskInpageProvider extends AbstractStreamProvider { */ requestBatch: async (requests: UnvalidatedJsonRpcRequest[]) => { if (!Array.isArray(requests)) { - throw ethErrors.rpc.invalidRequest({ + throw rpcErrors.invalidRequest({ message: 'Batch requests must be made with an array of request objects.', data: requests, diff --git a/src/StreamProvider.test.ts b/src/StreamProvider.test.ts index d10b14b5..babfa9d0 100644 --- a/src/StreamProvider.test.ts +++ b/src/StreamProvider.test.ts @@ -1,4 +1,5 @@ -import type { JsonRpcMiddleware } from 'json-rpc-engine'; +import type { JsonRpcMiddleware } from '@metamask/json-rpc-engine'; +import { Json, JsonRpcParams } from '@metamask/utils'; import messages from './messages'; import { StreamProvider } from './StreamProvider'; @@ -15,7 +16,7 @@ const mockStreamName = 'mock-stream'; * @returns A tuple containing the StreamProvider instance and the mock stream. */ function getStreamProvider( - rpcMiddleware: JsonRpcMiddleware[] = [], + rpcMiddleware: JsonRpcMiddleware[] = [], ) { const mockStream = new MockConnectionStream(); const streamProvider = new StreamProvider(mockStream, { diff --git a/src/StreamProvider.ts b/src/StreamProvider.ts index 51745c8a..c53a13df 100644 --- a/src/StreamProvider.ts +++ b/src/StreamProvider.ts @@ -1,7 +1,8 @@ +import type { JsonRpcMiddleware } from '@metamask/json-rpc-engine'; import ObjectMultiplex from '@metamask/object-multiplex'; import SafeEventEmitter from '@metamask/safe-event-emitter'; +import { Json, JsonRpcParams } from '@metamask/utils'; import { duplex as isDuplex } from 'is-stream'; -import type { JsonRpcMiddleware } from 'json-rpc-engine'; import { createStreamMiddleware } from 'json-rpc-middleware-stream'; import pump from 'pump'; import type { Duplex } from 'stream'; @@ -23,7 +24,7 @@ export type StreamProviderOptions = { export type JsonRpcConnection = { events: SafeEventEmitter; - middleware: JsonRpcMiddleware; + middleware: JsonRpcMiddleware; stream: Duplex; }; diff --git a/src/extension-provider/createExternalExtensionProvider.test.ts b/src/extension-provider/createExternalExtensionProvider.test.ts index 26782365..52f3a830 100644 --- a/src/extension-provider/createExternalExtensionProvider.test.ts +++ b/src/extension-provider/createExternalExtensionProvider.test.ts @@ -1,4 +1,4 @@ -import type { JsonRpcRequest } from 'json-rpc-engine'; +import type { JsonRpcRequest } from '@metamask/utils'; import { createExternalExtensionProvider } from './createExternalExtensionProvider'; import config from './external-extension-config.json'; @@ -49,7 +49,7 @@ async function getInitializedProvider({ onMethodCalled?: { substream: string; method: string; - callback: (data: JsonRpcRequest) => void; + callback: (data: JsonRpcRequest) => void; }[]; } = {}): Promise { const onWrite = jest.fn(); diff --git a/src/middleware/createRpcWarningMiddleware.test.ts b/src/middleware/createRpcWarningMiddleware.test.ts index 4e638515..1e14a99a 100644 --- a/src/middleware/createRpcWarningMiddleware.test.ts +++ b/src/middleware/createRpcWarningMiddleware.test.ts @@ -1,9 +1,23 @@ -import { JsonRpcEngine, JsonRpcFailure, JsonRpcSuccess } from 'json-rpc-engine'; +import { JsonRpcEngine } from '@metamask/json-rpc-engine'; +import { + Json, + JsonRpcFailure, + JsonRpcParams, + JsonRpcRequest, + JsonRpcSuccess, +} from '@metamask/utils'; import { createRpcWarningMiddleware } from './createRpcWarningMiddleware'; import messages from '../messages'; -const affected = [ +type Scenario = { + scenario: string; + method: string; + warning?: string; + params?: JsonRpcParams; +}; + +const affected: Scenario[] = [ { scenario: 'eth_decrypt', method: 'eth_decrypt', @@ -34,7 +48,7 @@ const affected = [ }, ]; -const unaffected = [ +const unaffected: Scenario[] = [ { scenario: 'eth_chainId', method: 'eth_chainId', @@ -50,67 +64,84 @@ const unaffected = [ ]; describe('createRpcWarningMiddleware', () => { - describe.each(affected)('$scenario', ({ method, params = {}, warning }) => { - it('should warn the first time the method is called', async () => { - const consoleWarnSpy = jest.spyOn(globalThis.console, 'warn'); - const middleware = createRpcWarningMiddleware(globalThis.console); - const engine = new JsonRpcEngine(); - engine.push(middleware); - - await engine.handle({ jsonrpc: '2.0', id: 1, method, params }); - - expect(consoleWarnSpy).toHaveBeenCalledWith(warning); - expect(consoleWarnSpy).toHaveBeenCalledTimes(1); - }); - - it('should not warn the second time the method is called', async () => { - const consoleWarnSpy = jest.spyOn(globalThis.console, 'warn'); - const middleware = createRpcWarningMiddleware(globalThis.console); - const engine = new JsonRpcEngine(); - engine.push(middleware); - - await engine.handle({ jsonrpc: '2.0', id: 1, method, params }); - await engine.handle({ jsonrpc: '2.0', id: 1, method, params }); - - expect(consoleWarnSpy).toHaveBeenCalledWith(warning); - expect(consoleWarnSpy).toHaveBeenCalledTimes(1); - }); - - it('should allow the method to succeed', async () => { - const middleware = createRpcWarningMiddleware(globalThis.console); - const engine = new JsonRpcEngine(); - engine.push(middleware); - engine.push((_req, res, _next, end) => { - res.result = 'success!'; - end(); + describe.each(affected)( + '$scenario', + ({ method, params = {}, warning }: Scenario) => { + it('should warn the first time the method is called', async () => { + const consoleWarnSpy = jest.spyOn(globalThis.console, 'warn'); + const middleware = createRpcWarningMiddleware(globalThis.console); + const engine = new JsonRpcEngine(); + engine.push(middleware); + + await engine.handle({ + jsonrpc: '2.0', + id: 1, + method, + params, + } as JsonRpcRequest); + expect(consoleWarnSpy).toHaveBeenCalledWith(warning); + expect(consoleWarnSpy).toHaveBeenCalledTimes(1); }); - const response = (await engine.handle({ - jsonrpc: '2.0', - id: 1, - method, - })) as JsonRpcSuccess; - - expect(response.result).toBe('success!'); - }); - - it('should allow the method to fail', async () => { - const middleware = createRpcWarningMiddleware(globalThis.console); - const engine = new JsonRpcEngine(); - engine.push(middleware); - engine.push(() => { - throw new Error('Failure!'); + it('should not warn the second time the method is called', async () => { + const consoleWarnSpy = jest.spyOn(globalThis.console, 'warn'); + const middleware = createRpcWarningMiddleware(globalThis.console); + const engine = new JsonRpcEngine(); + engine.push(middleware); + + await engine.handle({ + jsonrpc: '2.0', + id: 1, + method, + params, + } as JsonRpcRequest); + await engine.handle({ + jsonrpc: '2.0', + id: 1, + method, + params, + } as JsonRpcRequest); + + expect(consoleWarnSpy).toHaveBeenCalledWith(warning); + expect(consoleWarnSpy).toHaveBeenCalledTimes(1); }); - const result = (await engine.handle({ - jsonrpc: '2.0', - id: 1, - method, - })) as JsonRpcFailure; + it('should allow the method to succeed', async () => { + const middleware = createRpcWarningMiddleware(globalThis.console); + const engine = new JsonRpcEngine(); + engine.push(middleware); + engine.push((_req, res, _next, end) => { + res.result = 'success!'; + end(); + }); + + const response = (await engine.handle({ + jsonrpc: '2.0', + id: 1, + method, + })) as JsonRpcSuccess; + + expect(response.result).toBe('success!'); + }); - expect(result.error.message).toBe('Failure!'); - }); - }); + it('should allow the method to fail', async () => { + const middleware = createRpcWarningMiddleware(globalThis.console); + const engine = new JsonRpcEngine(); + engine.push(middleware); + engine.push(() => { + throw new Error('Failure!'); + }); + + const result = (await engine.handle({ + jsonrpc: '2.0', + id: 1, + method, + })) as JsonRpcFailure; + + expect(result.error.message).toBe('Internal JSON-RPC error.'); + }); + }, + ); describe.each(unaffected)('$scenario', ({ method, params = {} }) => { it('should not issue a warning', async () => { @@ -119,7 +150,12 @@ describe('createRpcWarningMiddleware', () => { const engine = new JsonRpcEngine(); engine.push(middleware); - await engine.handle({ jsonrpc: '2.0', id: 1, method, params }); + await engine.handle({ + jsonrpc: '2.0', + id: 1, + method, + params, + } as JsonRpcRequest); expect(consoleWarnSpy).not.toHaveBeenCalled(); }); @@ -138,7 +174,7 @@ describe('createRpcWarningMiddleware', () => { id: 1, method, params, - })) as JsonRpcSuccess; + } as JsonRpcRequest)) as JsonRpcSuccess; expect(response.result).toBe('success!'); }); @@ -156,9 +192,9 @@ describe('createRpcWarningMiddleware', () => { id: 1, method, params, - })) as JsonRpcFailure; + } as JsonRpcRequest)) as JsonRpcFailure; - expect(result.error.message).toBe('Failure!'); + expect(result.error.message).toBe('Internal JSON-RPC error.'); }); }); }); diff --git a/src/middleware/createRpcWarningMiddleware.ts b/src/middleware/createRpcWarningMiddleware.ts index be57d293..313bebf7 100644 --- a/src/middleware/createRpcWarningMiddleware.ts +++ b/src/middleware/createRpcWarningMiddleware.ts @@ -1,4 +1,5 @@ -import type { JsonRpcMiddleware, JsonRpcRequest } from 'json-rpc-engine'; +import type { JsonRpcMiddleware } from '@metamask/json-rpc-engine'; +import type { Json, JsonRpcParams, JsonRpcRequest } from '@metamask/utils'; import { ERC1155, ERC721 } from '../constants'; import messages from '../messages'; @@ -12,7 +13,7 @@ import type { ConsoleLike } from '../utils'; */ export function createRpcWarningMiddleware( log: ConsoleLike, -): JsonRpcMiddleware { +): JsonRpcMiddleware { const sentWarnings = { ethDecryptDeprecation: false, ethGetEncryptionPublicKeyDeprecation: false, diff --git a/src/siteMetadata.ts b/src/siteMetadata.ts index 96d74ca5..e192872c 100644 --- a/src/siteMetadata.ts +++ b/src/siteMetadata.ts @@ -1,4 +1,4 @@ -import { JsonRpcEngine } from 'json-rpc-engine'; +import { JsonRpcEngine } from '@metamask/json-rpc-engine'; import messages from './messages'; import { ConsoleLike, NOOP } from './utils'; diff --git a/src/utils.ts b/src/utils.ts index 43326037..394e7fc0 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,9 +1,9 @@ -import { ethErrors } from 'eth-rpc-errors'; import { createIdRemapMiddleware, JsonRpcMiddleware, - PendingJsonRpcResponse, -} from 'json-rpc-engine'; +} from '@metamask/json-rpc-engine'; +import { rpcErrors } from '@metamask/rpc-errors'; +import { Json, JsonRpcParams, PendingJsonRpcResponse } from '@metamask/utils'; import { createRpcWarningMiddleware } from './middleware/createRpcWarningMiddleware'; @@ -27,7 +27,7 @@ export const EMITTED_NOTIFICATIONS = Object.freeze([ * remapping middleware and an error middleware. * * @param logger - The logger to use in the error middleware. - * @returns An array of json-rpc-engine middleware functions. + * @returns An array of @metamask/json-rpc-engine middleware functions. */ export const getDefaultExternalMiddleware = (logger: ConsoleLike = console) => [ createIdRemapMiddleware(), @@ -40,15 +40,15 @@ export const getDefaultExternalMiddleware = (logger: ConsoleLike = console) => [ * method. * * @param log - The logging API to use. - * @returns A json-rpc-engine middleware function. + * @returns A @metamask/json-rpc-engine middleware function. */ function createErrorMiddleware( log: ConsoleLike, -): JsonRpcMiddleware { +): JsonRpcMiddleware { return (request, response, next) => { // json-rpc-engine will terminate the request when it notices this error if (typeof request.method !== 'string' || !request.method) { - response.error = ethErrors.rpc.invalidRequest({ + response.error = rpcErrors.invalidRequest({ message: `The request 'method' must be a non-empty string.`, data: request, }); @@ -72,7 +72,7 @@ export const getRpcPromiseCallback = reject: (error?: Error) => void, unwrapResult = true, ) => - (error: Error, response: PendingJsonRpcResponse): void => { + (error: Error, response: PendingJsonRpcResponse): void => { if (error || response.error) { reject(error || response.error); } else { diff --git a/test/mocks/MockConnectionStream.ts b/test/mocks/MockConnectionStream.ts index c959af99..2f3246c1 100644 --- a/test/mocks/MockConnectionStream.ts +++ b/test/mocks/MockConnectionStream.ts @@ -1,8 +1,9 @@ import { + Json, JsonRpcNotification, JsonRpcRequest, JsonRpcResponse, -} from 'json-rpc-engine'; +} from '@metamask/utils'; import { Duplex } from 'stream'; /** @@ -10,7 +11,7 @@ import { Duplex } from 'stream'; * provider to the wallet. */ export class MockConnectionStream extends Duplex { - #onWrite?: (name: string, data: JsonRpcRequest) => void; + #onWrite?: (name: string, data: JsonRpcRequest) => void; /** * Construct a mock connection stream. @@ -19,8 +20,7 @@ export class MockConnectionStream extends Duplex { * the provider to the wallet are passed to this function. */ constructor( - onWrite: (name: string, data: JsonRpcRequest) => void = () => - undefined, + onWrite: (name: string, data: JsonRpcRequest) => void = () => undefined, ) { super({ objectMode: true }); this.#onWrite = onWrite; @@ -41,7 +41,7 @@ export class MockConnectionStream extends Duplex { * the write has completed. */ _write( - message: { name: string; data: JsonRpcRequest }, + message: { name: string; data: JsonRpcRequest }, _encoding: string, callback: (error?: Error) => void, ) { @@ -72,7 +72,7 @@ export class MockConnectionStream extends Duplex { * @param substream - The substream this reply is included in. * @param message - The JSON RPC response. */ - reply(substream: string, message: JsonRpcResponse) { + reply(substream: string, message: JsonRpcResponse) { this.push({ name: substream, data: message }); } @@ -82,7 +82,7 @@ export class MockConnectionStream extends Duplex { * @param substream - The substream this notification is included in. * @param message - The JSON RPC notification. */ - notify(substream: string, message: JsonRpcNotification) { + notify(substream: string, message: JsonRpcNotification) { this.push({ name: substream, data: message }); } } diff --git a/test/mocks/MockPort.ts b/test/mocks/MockPort.ts index 7264d5f1..6e916170 100644 --- a/test/mocks/MockPort.ts +++ b/test/mocks/MockPort.ts @@ -1,9 +1,10 @@ -import { EventEmitter } from 'events'; import { + Json, JsonRpcNotification, JsonRpcRequest, JsonRpcResponse, -} from 'json-rpc-engine'; +} from '@metamask/utils'; +import { EventEmitter } from 'events'; /** * A mock WebExtension Port for multiplexed JSON-RPC messages, used to @@ -17,7 +18,7 @@ export class MockPort { #eventEmitter = new EventEmitter(); - #onWrite?: (name: string, data: JsonRpcRequest) => void; + #onWrite?: (name: string, data: JsonRpcRequest) => void; /** * Construct a mock WebExtension Port. @@ -26,8 +27,7 @@ export class MockPort { * from another extension to the wallet are passed to this function. */ constructor( - onWrite: (name: string, data: JsonRpcRequest) => void = () => - undefined, + onWrite: (name: string, data: JsonRpcRequest) => void = () => undefined, ) { this.#onWrite = onWrite; } @@ -46,7 +46,7 @@ export class MockPort { * in. * @param message.data - The JSON-RPC request. */ - postMessage(message: { name: string; data: JsonRpcRequest }) { + postMessage(message: { name: string; data: JsonRpcRequest }) { if (!this.#connected) { throw new Error('Disconnected'); } else if (this.#onWrite) { @@ -65,10 +65,7 @@ export class MockPort { get onMessage() { return { addListener: ( - listener: (message: { - name: string; - data: JsonRpcRequest; - }) => void, + listener: (message: { name: string; data: JsonRpcRequest }) => void, ) => { this.#eventEmitter.addListener('message', listener); }, @@ -81,7 +78,7 @@ export class MockPort { * @param substream - The substream this reply is included in. * @param message - The JSON RPC response. */ - reply(substream: string, message: JsonRpcResponse) { + reply(substream: string, message: JsonRpcResponse) { if (!this.#connected) { throw new Error( 'It is not possible to reply after the port has disconnected', @@ -96,7 +93,7 @@ export class MockPort { * @param substream - The substream this notification is included in. * @param message - The JSoN RPC notification. */ - notify(substream: string, message: JsonRpcNotification) { + notify(substream: string, message: JsonRpcNotification) { if (!this.#connected) { throw new Error( 'It is not possible to notify after the port has disconnected', diff --git a/yarn.lock b/yarn.lock index 1c9faa21..5192e89b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -408,6 +408,33 @@ __metadata: languageName: node linkType: hard +"@chainsafe/as-sha256@npm:^0.4.1": + version: 0.4.1 + resolution: "@chainsafe/as-sha256@npm:0.4.1" + checksum: 6d86975e648ecdafd366802278ac15b392b252e967f3681412ec48b5a3518b936cc5e977517499882b084991446d25787d98f8f585891943688cc81549a44e9a + languageName: node + linkType: hard + +"@chainsafe/persistent-merkle-tree@npm:^0.6.1": + version: 0.6.1 + resolution: "@chainsafe/persistent-merkle-tree@npm:0.6.1" + dependencies: + "@chainsafe/as-sha256": ^0.4.1 + "@noble/hashes": ^1.3.0 + checksum: 74614b8d40970dc930d5bf741619498b0bbbde5ff24ce45fce6ad122143aa77bf57249a28175b1b972cf56bff57d529a4258b7222ab4e60c1261119b5986c51b + languageName: node + linkType: hard + +"@chainsafe/ssz@npm:^0.11.1": + version: 0.11.1 + resolution: "@chainsafe/ssz@npm:0.11.1" + dependencies: + "@chainsafe/as-sha256": ^0.4.1 + "@chainsafe/persistent-merkle-tree": ^0.6.1 + checksum: e3c2928f9ab4a0544e645f0302b9535046d1e6e1d4b3bd1c3dd6bc8e6302fddad6036d65e7900d1446f285f496051da05fa14c1bde590b511d03033907175c8f + languageName: node + linkType: hard + "@cspotcode/source-map-support@npm:^0.8.0": version: 0.8.1 resolution: "@cspotcode/source-map-support@npm:0.8.1" @@ -470,6 +497,55 @@ __metadata: languageName: node linkType: hard +"@ethereumjs/common@npm:^3.1.2": + version: 3.1.2 + resolution: "@ethereumjs/common@npm:3.1.2" + dependencies: + "@ethereumjs/util": ^8.0.6 + crc-32: ^1.2.0 + checksum: e80a8bc86476f1ce878bacb1915d91681671bb5303291cdcece26e456ac13a6158f0f59625cb02a1cfbdd7c9a7dc8b175f8d8f0fee596b3eb9dfb965465ad43d + languageName: node + linkType: hard + +"@ethereumjs/rlp@npm:^4.0.1": + version: 4.0.1 + resolution: "@ethereumjs/rlp@npm:4.0.1" + bin: + rlp: bin/rlp + checksum: 30db19c78faa2b6ff27275ab767646929207bb207f903f09eb3e4c273ce2738b45f3c82169ddacd67468b4f063d8d96035f2bf36f02b6b7e4d928eefe2e3ecbc + languageName: node + linkType: hard + +"@ethereumjs/tx@npm:^4.1.2": + version: 4.1.2 + resolution: "@ethereumjs/tx@npm:4.1.2" + dependencies: + "@chainsafe/ssz": ^0.11.1 + "@ethereumjs/common": ^3.1.2 + "@ethereumjs/rlp": ^4.0.1 + "@ethereumjs/util": ^8.0.6 + ethereum-cryptography: ^2.0.0 + peerDependencies: + c-kzg: ^1.0.8 + peerDependenciesMeta: + c-kzg: + optional: true + checksum: ad2fb692c3746cd5935b01c98b6b54046ae2a1fccff57ad2209e10446f3b279a204d7477accf05b27078445b14379314077769662142ac07117c45a5a1ea427f + languageName: node + linkType: hard + +"@ethereumjs/util@npm:^8.0.6": + version: 8.0.6 + resolution: "@ethereumjs/util@npm:8.0.6" + dependencies: + "@chainsafe/ssz": ^0.11.1 + "@ethereumjs/rlp": ^4.0.1 + ethereum-cryptography: ^2.0.0 + micro-ftch: ^0.3.1 + checksum: 034e06cddec27417318434a1a7cd7a9dc0f0b447c1f54423c515d8809c9697386eee6429d0a1c13517a85c696e6fdba570b243d882e65764c274859606027015 + languageName: node + linkType: hard + "@gar/promisify@npm:^1.1.3": version: 1.1.3 resolution: "@gar/promisify@npm:1.1.3" @@ -995,6 +1071,17 @@ __metadata: languageName: node linkType: hard +"@metamask/json-rpc-engine@npm:^7.1.1": + version: 7.1.1 + resolution: "@metamask/json-rpc-engine@npm:7.1.1" + dependencies: + "@metamask/rpc-errors": ^6.0.0 + "@metamask/safe-event-emitter": ^3.0.0 + "@metamask/utils": ^8.1.0 + checksum: 9dddd9142965ccd86313cda5bf13f15bf99c6c14631f93aab78de353317d548a334b5b125cdc134edd7d54e2f2e4961a0bdcd24fba997b2913083955df8fefa1 + languageName: node + linkType: hard + "@metamask/object-multiplex@npm:^1.1.0": version: 1.2.0 resolution: "@metamask/object-multiplex@npm:1.2.0" @@ -1016,8 +1103,11 @@ __metadata: "@metamask/eslint-config-jest": ^11.0.0 "@metamask/eslint-config-nodejs": ^11.0.1 "@metamask/eslint-config-typescript": ^11.0.0 + "@metamask/json-rpc-engine": ^7.1.1 "@metamask/object-multiplex": ^1.1.0 + "@metamask/rpc-errors": ^6.0.0 "@metamask/safe-event-emitter": ^3.0.0 + "@metamask/utils": ^8.1.0 "@types/chrome": ^0.0.233 "@types/jest": ^28.1.6 "@types/node": ^17.0.23 @@ -1035,7 +1125,6 @@ __metadata: eslint-plugin-jsdoc: ^39.6.2 eslint-plugin-node: ^11.1.0 eslint-plugin-prettier: ^4.2.1 - eth-rpc-errors: ^4.0.2 extension-port-stream: ^2.1.1 fast-deep-equal: ^3.1.3 is-stream: ^2.0.0 @@ -1043,7 +1132,6 @@ __metadata: jest-chrome: ^0.7.1 jest-environment-jsdom: ^29.5.0 jest-it-up: ^2.0.2 - json-rpc-engine: ^6.1.0 json-rpc-middleware-stream: ^4.2.1 prettier: ^2.7.1 prettier-plugin-packagejson: ^2.3.0 @@ -1057,6 +1145,16 @@ __metadata: languageName: unknown linkType: soft +"@metamask/rpc-errors@npm:^6.0.0": + version: 6.0.0 + resolution: "@metamask/rpc-errors@npm:6.0.0" + dependencies: + "@metamask/utils": ^8.0.0 + fast-safe-stringify: ^2.0.6 + checksum: 7e1ee1a98972266af4a34f0bbc842cdc11dc565056f0b8fbc93aa95663a7027eab8ff1fecbe3e09c38a1dc199f8219a6c69b2237015b2fdb8de0e5b35027c3f8 + languageName: node + linkType: hard + "@metamask/safe-event-emitter@npm:^2.0.0": version: 2.0.0 resolution: "@metamask/safe-event-emitter@npm:2.0.0" @@ -1071,6 +1169,43 @@ __metadata: languageName: node linkType: hard +"@metamask/utils@npm:^8.0.0, @metamask/utils@npm:^8.1.0": + version: 8.1.0 + resolution: "@metamask/utils@npm:8.1.0" + dependencies: + "@ethereumjs/tx": ^4.1.2 + "@noble/hashes": ^1.3.1 + "@types/debug": ^4.1.7 + debug: ^4.3.4 + semver: ^7.5.4 + superstruct: ^1.0.3 + checksum: 4cbee36d0c227f3e528930e83f75a0c6b71b55b332c3e162f0e87f3dd86ae017d0b20405d76ea054ab99e4d924d3d9b8b896ed12a12aae57b090350e5a625999 + languageName: node + linkType: hard + +"@noble/curves@npm:1.0.0, @noble/curves@npm:~1.0.0": + version: 1.0.0 + resolution: "@noble/curves@npm:1.0.0" + dependencies: + "@noble/hashes": 1.3.0 + checksum: 6bcef44d626c640dc8961819d68dd67dffb907e3b973b7c27efe0ecdd9a5c6ce62c7b9e3dfc930c66605dced7f1ec0514d191c09a2ce98d6d52b66e3315ffa79 + languageName: node + linkType: hard + +"@noble/hashes@npm:1.3.0": + version: 1.3.0 + resolution: "@noble/hashes@npm:1.3.0" + checksum: d7ddb6d7c60f1ce1f87facbbef5b724cdea536fc9e7f59ae96e0fc9de96c8f1a2ae2bdedbce10f7dcc621338dfef8533daa73c873f2b5c87fa1a4e05a95c2e2e + languageName: node + linkType: hard + +"@noble/hashes@npm:^1.3.0, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:~1.3.0": + version: 1.3.2 + resolution: "@noble/hashes@npm:1.3.2" + checksum: fe23536b436539d13f90e4b9be843cc63b1b17666a07634a2b1259dded6f490be3d050249e6af98076ea8f2ea0d56f578773c2197f2aa0eeaa5fba5bc18ba474 + languageName: node + linkType: hard + "@nodelib/fs.scandir@npm:2.1.5": version: 2.1.5 resolution: "@nodelib/fs.scandir@npm:2.1.5" @@ -1161,6 +1296,34 @@ __metadata: languageName: node linkType: hard +"@scure/base@npm:~1.1.0": + version: 1.1.1 + resolution: "@scure/base@npm:1.1.1" + checksum: b4fc810b492693e7e8d0107313ac74c3646970c198bbe26d7332820886fa4f09441991023ec9aa3a2a51246b74409ab5ebae2e8ef148bbc253da79ac49130309 + languageName: node + linkType: hard + +"@scure/bip32@npm:1.3.0": + version: 1.3.0 + resolution: "@scure/bip32@npm:1.3.0" + dependencies: + "@noble/curves": ~1.0.0 + "@noble/hashes": ~1.3.0 + "@scure/base": ~1.1.0 + checksum: 6eae997f9bdf41fe848134898960ac48e645fa10e63d579be965ca331afd0b7c1b8ebac170770d237ab4099dafc35e5a82995384510025ccf2abe669f85e8918 + languageName: node + linkType: hard + +"@scure/bip39@npm:1.2.0": + version: 1.2.0 + resolution: "@scure/bip39@npm:1.2.0" + dependencies: + "@noble/hashes": ~1.3.0 + "@scure/base": ~1.1.0 + checksum: 980d761f53e63de04a9e4db840eb13bfb1bd1b664ecb04a71824c12c190f4972fd84146f3ed89b2a8e4c6bd2c17c15f8b592b7ac029e903323b0f9e2dae6916b + languageName: node + linkType: hard + "@sinclair/typebox@npm:^0.24.1": version: 0.24.51 resolution: "@sinclair/typebox@npm:0.24.51" @@ -1314,6 +1477,15 @@ __metadata: languageName: node linkType: hard +"@types/debug@npm:^4.1.7": + version: 4.1.7 + resolution: "@types/debug@npm:4.1.7" + dependencies: + "@types/ms": "*" + checksum: 0a7b89d8ed72526858f0b61c6fd81f477853e8c4415bb97f48b1b5545248d2ae389931680b94b393b993a7cfe893537a200647d93defe6d87159b96812305adc + languageName: node + linkType: hard + "@types/filesystem@npm:*": version: 0.0.30 resolution: "@types/filesystem@npm:0.0.30" @@ -1413,6 +1585,13 @@ __metadata: languageName: node linkType: hard +"@types/ms@npm:*": + version: 0.7.31 + resolution: "@types/ms@npm:0.7.31" + checksum: daadd354aedde024cce6f5aa873fefe7b71b22cd0e28632a69e8b677aeb48ae8caa1c60e5919bb781df040d116b01cb4316335167a3fc0ef6a63fa3614c0f6da + languageName: node + linkType: hard + "@types/node@npm:*, @types/node@npm:^17.0.23": version: 17.0.45 resolution: "@types/node@npm:17.0.45" @@ -2439,6 +2618,15 @@ __metadata: languageName: node linkType: hard +"crc-32@npm:^1.2.0": + version: 1.2.2 + resolution: "crc-32@npm:1.2.2" + bin: + crc32: bin/crc32.njs + checksum: ad2d0ad0cbd465b75dcaeeff0600f8195b686816ab5f3ba4c6e052a07f728c3e70df2e3ca9fd3d4484dc4ba70586e161ca5a2334ec8bf5a41bf022a6103ff243 + languageName: node + linkType: hard + "create-require@npm:^1.1.0": version: 1.1.1 resolution: "create-require@npm:1.1.1" @@ -3188,12 +3376,15 @@ __metadata: languageName: node linkType: hard -"eth-rpc-errors@npm:^4.0.2": - version: 4.0.2 - resolution: "eth-rpc-errors@npm:4.0.2" +"ethereum-cryptography@npm:^2.0.0": + version: 2.0.0 + resolution: "ethereum-cryptography@npm:2.0.0" dependencies: - fast-safe-stringify: ^2.0.6 - checksum: 1dbdee8f416090f1d318e17bdee2251d174d73c8faa4286fa364bc51ae9105672045f2d078ec23ca6a2b4b92af7cfbe7fa1ba17ad49e591fc653a363bf8cbab2 + "@noble/curves": 1.0.0 + "@noble/hashes": 1.3.0 + "@scure/bip32": 1.3.0 + "@scure/bip39": 1.2.0 + checksum: 958f8aab2d1b32aa759fb27a27877b3647410e8bb9aca7d65d1d477db4864cf7fc46b918eb52a1e246c25e98ee0a35a632c88b496aeaefa13469ee767a76c8db languageName: node linkType: hard @@ -4817,16 +5008,6 @@ __metadata: languageName: node linkType: hard -"json-rpc-engine@npm:^6.1.0": - version: 6.1.0 - resolution: "json-rpc-engine@npm:6.1.0" - dependencies: - "@metamask/safe-event-emitter": ^2.0.0 - eth-rpc-errors: ^4.0.2 - checksum: 33b6c9bbd81abf8e323a0281ee05871713203c40d34a4d0bda27706cd0a0935c7b51845238ba89b73027e44ebc8034bbd82db9f962e6c578eb922d9b95acc8bd - languageName: node - linkType: hard - "json-rpc-middleware-stream@npm:^4.2.1": version: 4.2.1 resolution: "json-rpc-middleware-stream@npm:4.2.1" @@ -5082,6 +5263,13 @@ __metadata: languageName: node linkType: hard +"micro-ftch@npm:^0.3.1": + version: 0.3.1 + resolution: "micro-ftch@npm:0.3.1" + checksum: 0e496547253a36e98a83fb00c628c53c3fb540fa5aaeaf718438873785afd193244988c09d219bb1802984ff227d04938d9571ef90fe82b48bd282262586aaff + languageName: node + linkType: hard + "micromatch@npm:^4.0.4": version: 4.0.5 resolution: "micromatch@npm:4.0.5" @@ -6056,14 +6244,14 @@ __metadata: languageName: node linkType: hard -"semver@npm:7.x, semver@npm:^7.3.2, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8": - version: 7.5.0 - resolution: "semver@npm:7.5.0" +"semver@npm:7.x, semver@npm:^7.3.2, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:^7.5.4": + version: 7.5.4 + resolution: "semver@npm:7.5.4" dependencies: lru-cache: ^6.0.0 bin: semver: bin/semver.js - checksum: 2d266937756689a76f124ffb4c1ea3e1bbb2b263219f90ada8a11aebebe1280b13bb76cca2ca96bdee3dbc554cbc0b24752eb895b2a51577aa644427e9229f2b + checksum: 12d8ad952fa353b0995bf180cdac205a4068b759a140e5d3c608317098b3575ac2f1e09182206bf2eb26120e1c0ed8fb92c48c592f6099680de56bb071423ca3 languageName: node linkType: hard @@ -6397,6 +6585,13 @@ __metadata: languageName: node linkType: hard +"superstruct@npm:^1.0.3": + version: 1.0.3 + resolution: "superstruct@npm:1.0.3" + checksum: 761790bb111e6e21ddd608299c252f3be35df543263a7ebbc004e840d01fcf8046794c274bcb351bdf3eae4600f79d317d085cdbb19ca05803a4361840cc9bb1 + languageName: node + linkType: hard + "supports-color@npm:^5.3.0": version: 5.5.0 resolution: "supports-color@npm:5.5.0" From ece06509da546b0dd97239403f399100af98cbfd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 5 Sep 2023 16:19:44 +0900 Subject: [PATCH 4/7] 12.0.0 (#279) --------- Co-authored-by: github-actions Co-authored-by: legobt <6wbvkn0j@anonaddy.me> --- CHANGELOG.md | 9 ++++++++- package.json | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ad5332e..0f11be4a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [12.0.0] +### Changed +- **BREAKING**: Replace `eth-rpc-errors`@`^4.0.2` with `@metamask/rpc-errors`@`6.0.0` ([#277](https://github.com/MetaMask/providers/pull/277)) +- **BREAKING**: Replace `json-rpc-engine`@`^6.1.0` with `@metamask/json-rpc-engine`@`7.1.1` ([#277](https://github.com/MetaMask/providers/pull/277)) +- Upgrade `@metamask/utils` from `^6.2.0` to `^8.1.0` ([#277](https://github.com/MetaMask/providers/pull/277)) + ## [11.1.2] ### Changed - Update `extension-port-stream` to `^2.1.1` ([#273](https://github.com/MetaMask/providers/pull/273)) @@ -232,7 +238,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 added deprecation warnings for them ([#30](https://github.com/MetaMask/providers/pull/30)) - Un-deprecated `sendAsync` ([#29](https://github.com/MetaMask/providers/pull/29)) -[Unreleased]: https://github.com/MetaMask/providers/compare/v11.1.2...HEAD +[Unreleased]: https://github.com/MetaMask/providers/compare/v12.0.0...HEAD +[12.0.0]: https://github.com/MetaMask/providers/compare/v11.1.2...v12.0.0 [11.1.2]: https://github.com/MetaMask/providers/compare/v11.1.1...v11.1.2 [11.1.1]: https://github.com/MetaMask/providers/compare/v11.1.0...v11.1.1 [11.1.0]: https://github.com/MetaMask/providers/compare/v11.0.0...v11.1.0 diff --git a/package.json b/package.json index 513c2862..217d8561 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@metamask/providers", - "version": "11.1.2", + "version": "12.0.0", "description": "A JavaScript Ethereum provider that connects to the wallet over a stream.", "keywords": [ "MetaMask", From 8bd6311b34a803b2bf5942b40285eaa20eab4d24 Mon Sep 17 00:00:00 2001 From: Frederik Bolding Date: Mon, 11 Sep 2023 14:42:43 +0200 Subject: [PATCH 5/7] Remove `pump` (#281) Remove pump --- package.json | 2 -- src/StreamProvider.ts | 8 ++++---- yarn.lock | 25 ++----------------------- 3 files changed, 6 insertions(+), 29 deletions(-) diff --git a/package.json b/package.json index 217d8561..f6462adb 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,6 @@ "fast-deep-equal": "^3.1.3", "is-stream": "^2.0.0", "json-rpc-middleware-stream": "^4.2.1", - "pump": "^3.0.0", "webextension-polyfill": "^0.10.0" }, "devDependencies": { @@ -60,7 +59,6 @@ "@types/chrome": "^0.0.233", "@types/jest": "^28.1.6", "@types/node": "^17.0.23", - "@types/pump": "^1.1.0", "@types/readable-stream": "^2.3.15", "@types/webextension-polyfill": "^0.10.0", "@typescript-eslint/eslint-plugin": "^5.43.0", diff --git a/src/StreamProvider.ts b/src/StreamProvider.ts index c53a13df..e42e8305 100644 --- a/src/StreamProvider.ts +++ b/src/StreamProvider.ts @@ -4,7 +4,7 @@ import SafeEventEmitter from '@metamask/safe-event-emitter'; import { Json, JsonRpcParams } from '@metamask/utils'; import { duplex as isDuplex } from 'is-stream'; import { createStreamMiddleware } from 'json-rpc-middleware-stream'; -import pump from 'pump'; +import { pipeline } from 'stream'; import type { Duplex } from 'stream'; import { BaseProvider, BaseProviderOptions } from './BaseProvider'; @@ -68,7 +68,7 @@ export abstract class AbstractStreamProvider extends BaseProvider { // Set up connectionStream multiplexing const mux = new ObjectMultiplex(); - pump( + pipeline( connectionStream, mux as unknown as Duplex, connectionStream, @@ -82,7 +82,7 @@ export abstract class AbstractStreamProvider extends BaseProvider { retryOnMessage: 'METAMASK_EXTENSION_CONNECT_CAN_RETRY', }) as unknown as JsonRpcConnection; - pump( + pipeline( this._jsonRpcConnection.stream, mux.createStream(jsonRpcStreamName) as unknown as Duplex, this._jsonRpcConnection.stream, @@ -151,7 +151,7 @@ export abstract class AbstractStreamProvider extends BaseProvider { * disconnected. */ // eslint-disable-next-line no-restricted-syntax - private _handleStreamDisconnect(streamName: string, error: Error) { + private _handleStreamDisconnect(streamName: string, error: Error | null) { let warningMsg = `MetaMask: Lost connection to "${streamName}".`; if (error?.stack) { warningMsg += `\n${error.stack}`; diff --git a/yarn.lock b/yarn.lock index 5192e89b..ebeb554b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1111,7 +1111,6 @@ __metadata: "@types/chrome": ^0.0.233 "@types/jest": ^28.1.6 "@types/node": ^17.0.23 - "@types/pump": ^1.1.0 "@types/readable-stream": ^2.3.15 "@types/webextension-polyfill": ^0.10.0 "@typescript-eslint/eslint-plugin": ^5.43.0 @@ -1135,7 +1134,6 @@ __metadata: json-rpc-middleware-stream: ^4.2.1 prettier: ^2.7.1 prettier-plugin-packagejson: ^2.3.0 - pump: ^3.0.0 rimraf: ^3.0.2 ts-jest: ^28.0.7 ts-node: ^10.7.0 @@ -1613,15 +1611,6 @@ __metadata: languageName: node linkType: hard -"@types/pump@npm:^1.1.0": - version: 1.1.0 - resolution: "@types/pump@npm:1.1.0" - dependencies: - "@types/node": "*" - checksum: 12e94dd77fef10e4ed9bd22eeda20a2b6de72424727559d7fb04ad21f51da4486b2aa993c44af98483ef2432692c8111722f10937ffd8e83e459e32c5df118ba - languageName: node - linkType: hard - "@types/readable-stream@npm:^2.3.15": version: 2.3.15 resolution: "@types/readable-stream@npm:2.3.15" @@ -2921,7 +2910,7 @@ __metadata: languageName: node linkType: hard -"end-of-stream@npm:^1.1.0, end-of-stream@npm:^1.4.4": +"end-of-stream@npm:^1.4.4": version: 1.4.4 resolution: "end-of-stream@npm:1.4.4" dependencies: @@ -5611,7 +5600,7 @@ __metadata: languageName: node linkType: hard -"once@npm:^1.3.0, once@npm:^1.3.1, once@npm:^1.4.0": +"once@npm:^1.3.0, once@npm:^1.4.0": version: 1.4.0 resolution: "once@npm:1.4.0" dependencies: @@ -5946,16 +5935,6 @@ __metadata: languageName: node linkType: hard -"pump@npm:^3.0.0": - version: 3.0.0 - resolution: "pump@npm:3.0.0" - dependencies: - end-of-stream: ^1.1.0 - once: ^1.3.1 - checksum: e42e9229fba14732593a718b04cb5e1cfef8254544870997e0ecd9732b189a48e1256e4e5478148ecb47c8511dca2b09eae56b4d0aad8009e6fac8072923cfc9 - languageName: node - linkType: hard - "punycode@npm:^2.1.0, punycode@npm:^2.1.1": version: 2.1.1 resolution: "punycode@npm:2.1.1" From ede28d5950981193bdcea88b68396bf4d0c1ec41 Mon Sep 17 00:00:00 2001 From: jiexi Date: Mon, 11 Sep 2023 11:54:27 -0700 Subject: [PATCH 6/7] Make `chainId`, `networkVersion`, `selectedAddress` readonly and log deprecation warning (#280) * Make chainId, networkVersion, selectedAddress readonly and log warning * Fix Proxy getter * Loosen jest coverage * Add link to deprecation messages --- jest.config.js | 8 +-- src/BaseProvider.ts | 34 +++++++---- src/MetaMaskInpageProvider.test.ts | 93 ++++++++++++++++++++++++++++++ src/MetaMaskInpageProvider.ts | 52 ++++++++++++++--- src/initializeInpageProvider.ts | 5 ++ src/messages.ts | 4 ++ 6 files changed, 173 insertions(+), 23 deletions(-) diff --git a/jest.config.js b/jest.config.js index 8d45ad1f..8c924598 100644 --- a/jest.config.js +++ b/jest.config.js @@ -45,10 +45,10 @@ const baseConfig = { // An object that configures minimum threshold enforcement for coverage results coverageThreshold: { global: { - branches: 61.29, - functions: 60.24, - lines: 63.82, - statements: 63.97, + branches: 61.9, + functions: 61.79, + lines: 65.18, + statements: 65.3, }, }, diff --git a/src/BaseProvider.ts b/src/BaseProvider.ts index db1459d1..a530f1f9 100644 --- a/src/BaseProvider.ts +++ b/src/BaseProvider.ts @@ -89,14 +89,14 @@ export abstract class BaseProvider extends SafeEventEmitter { * The chain ID of the currently connected Ethereum chain. * See [chainId.network]{@link https://chainid.network} for more information. */ - public chainId: string | null; + #chainId: string | null; /** * The user's currently selected Ethereum address. * If null, MetaMask is either locked or the user has not permitted any * addresses to be viewed. */ - public selectedAddress: string | null; + #selectedAddress: string | null; /** * Create a new instance of the provider. @@ -124,8 +124,8 @@ export abstract class BaseProvider extends SafeEventEmitter { }; // Public state - this.selectedAddress = null; - this.chainId = null; + this.#selectedAddress = null; + this.#chainId = null; // Bind functions to prevent consumers from making unbound calls this._handleAccountsChanged = this._handleAccountsChanged.bind(this); @@ -145,6 +145,18 @@ export abstract class BaseProvider extends SafeEventEmitter { this._rpcEngine = rpcEngine; } + //==================== + // Public Properties + //==================== + + get chainId(): string | null { + return this.#chainId; + } + + get selectedAddress(): string | null { + return this.#selectedAddress; + } + //==================== // Public Methods //==================== @@ -337,9 +349,9 @@ export abstract class BaseProvider extends SafeEventEmitter { errorMessage ?? messages.errors.permanentlyDisconnected(), ); this._log.error(error); - this.chainId = null; + this.#chainId = null; this._state.accounts = null; - this.selectedAddress = null; + this.#selectedAddress = null; this._state.isUnlocked = false; this._state.isPermanentlyDisconnected = true; } @@ -372,10 +384,10 @@ export abstract class BaseProvider extends SafeEventEmitter { this._handleConnect(chainId); - if (chainId !== this.chainId) { - this.chainId = chainId; + if (chainId !== this.#chainId) { + this.#chainId = chainId; if (this._state.initialized) { - this.emit('chainChanged', this.chainId); + this.emit('chainChanged', this.#chainId); } } } @@ -428,8 +440,8 @@ export abstract class BaseProvider extends SafeEventEmitter { this._state.accounts = _accounts as string[]; // handle selectedAddress - if (this.selectedAddress !== _accounts[0]) { - this.selectedAddress = (_accounts[0] as string) || null; + if (this.#selectedAddress !== _accounts[0]) { + this.#selectedAddress = (_accounts[0] as string) || null; } // finally, after all state has been updated, emit the event diff --git a/src/MetaMaskInpageProvider.test.ts b/src/MetaMaskInpageProvider.test.ts index 744bb6a3..cf8ed69e 100644 --- a/src/MetaMaskInpageProvider.test.ts +++ b/src/MetaMaskInpageProvider.test.ts @@ -1070,4 +1070,97 @@ describe('MetaMaskInpageProvider: Miscellanea', () => { expect(provider.isMetaMask).toBe(true); }); }); + + describe('chainId', () => { + let provider: any | MetaMaskInpageProvider; + + beforeEach(async () => { + provider = ( + await getInitializedProvider({ + initialState: { + chainId: '0x5', + }, + }) + ).provider; + }); + + it('should warn the first time chainId is accessed', async () => { + const consoleWarnSpy = jest.spyOn(globalThis.console, 'warn'); + + expect(provider.chainId).toBe('0x5'); + expect(consoleWarnSpy).toHaveBeenCalledWith( + messages.warnings.chainIdDeprecation, + ); + expect(consoleWarnSpy).toHaveBeenCalledTimes(1); + }); + + it('should not allow chainId to be modified', () => { + expect(() => (provider.chainId = '0x539')).toThrow( + 'Cannot set property chainId', + ); + expect(provider.chainId).toBe('0x5'); + }); + }); + + describe('networkVersion', () => { + let provider: any | MetaMaskInpageProvider; + + beforeEach(async () => { + provider = ( + await getInitializedProvider({ + initialState: { + networkVersion: '5', + }, + }) + ).provider; + }); + + it('should warn the first time networkVersion is accessed', async () => { + const consoleWarnSpy = jest.spyOn(globalThis.console, 'warn'); + + expect(provider.networkVersion).toBe('5'); + expect(consoleWarnSpy).toHaveBeenCalledWith( + messages.warnings.networkVersionDeprecation, + ); + expect(consoleWarnSpy).toHaveBeenCalledTimes(1); + }); + + it('should not allow networkVersion to be modified', () => { + expect(() => (provider.networkVersion = '1337')).toThrow( + 'Cannot set property networkVersion', + ); + expect(provider.networkVersion).toBe('5'); + }); + }); + + describe('selectedAddress', () => { + let provider: any | MetaMaskInpageProvider; + + beforeEach(async () => { + provider = ( + await getInitializedProvider({ + initialState: { + accounts: ['0xdeadbeef'], + }, + }) + ).provider; + }); + + it('should warn the first time selectedAddress is accessed', async () => { + const consoleWarnSpy = jest.spyOn(globalThis.console, 'warn'); + + expect(provider.selectedAddress).toBe('0xdeadbeef'); + expect(consoleWarnSpy).toHaveBeenCalledWith( + messages.warnings.selectedAddressDeprecation, + ); + expect(consoleWarnSpy).toHaveBeenCalledTimes(1); + }); + + it('should not allow selectedAddress to be modified', () => { + expect(() => (provider.selectedAddress = '0x12345678')).toThrow( + 'Cannot set property selectedAddress', + ); + expect(provider.selectedAddress).toBe('0xdeadbeef'); + }); + }); }); diff --git a/src/MetaMaskInpageProvider.ts b/src/MetaMaskInpageProvider.ts index d9e03d1f..f15c8c12 100644 --- a/src/MetaMaskInpageProvider.ts +++ b/src/MetaMaskInpageProvider.ts @@ -36,6 +36,10 @@ export type MetaMaskInpageProviderOptions = { } & Partial>; type SentWarningsState = { + // properties + chainId: boolean; + networkVersion: boolean; + selectedAddress: boolean; // methods enable: boolean; experimentalMethods: boolean; @@ -56,6 +60,10 @@ export const MetaMaskInpageProviderStreamName = 'metamask-provider'; export class MetaMaskInpageProvider extends AbstractStreamProvider { protected _sentWarnings: SentWarningsState = { + // properties + chainId: false, + networkVersion: false, + selectedAddress: false, // methods enable: false, experimentalMethods: false, @@ -76,7 +84,7 @@ export class MetaMaskInpageProvider extends AbstractStreamProvider { MetaMaskInpageProvider['_getExperimentalApi'] >; - public networkVersion: string | null; + #networkVersion: string | null; /** * Indicating that this provider is a MetaMask provider. @@ -118,7 +126,7 @@ export class MetaMaskInpageProvider extends AbstractStreamProvider { // eslint-disable-next-line @typescript-eslint/no-floating-promises this._initializeStateAsync(); - this.networkVersion = null; + this.#networkVersion = null; this.isMetaMask = true; this._sendSync = this._sendSync.bind(this); @@ -160,6 +168,34 @@ export class MetaMaskInpageProvider extends AbstractStreamProvider { } } + //==================== + // Deprecated Properties + //==================== + + get chainId(): string | null { + if (!this._sentWarnings.chainId) { + this._log.warn(messages.warnings.chainIdDeprecation); + this._sentWarnings.chainId = true; + } + return super.chainId; + } + + get networkVersion(): string | null { + if (!this._sentWarnings.networkVersion) { + this._log.warn(messages.warnings.networkVersionDeprecation); + this._sentWarnings.networkVersion = true; + } + return this.#networkVersion; + } + + get selectedAddress(): string | null { + if (!this._sentWarnings.selectedAddress) { + this._log.warn(messages.warnings.selectedAddressDeprecation); + this._sentWarnings.selectedAddress = true; + } + return super.selectedAddress; + } + //==================== // Public Methods //==================== @@ -228,8 +264,8 @@ export class MetaMaskInpageProvider extends AbstractStreamProvider { */ protected _handleDisconnect(isRecoverable: boolean, errorMessage?: string) { super._handleDisconnect(isRecoverable, errorMessage); - if (this.networkVersion && !isRecoverable) { - this.networkVersion = null; + if (this.#networkVersion && !isRecoverable) { + this.#networkVersion = null; } } @@ -369,7 +405,7 @@ export class MetaMaskInpageProvider extends AbstractStreamProvider { break; case 'net_version': - result = this.networkVersion ?? null; + result = this.#networkVersion ?? null; break; default: @@ -457,10 +493,10 @@ export class MetaMaskInpageProvider extends AbstractStreamProvider { // networkVersion is 'loading'. super._handleChainChanged({ chainId, networkVersion }); - if (this._state.isConnected && networkVersion !== this.networkVersion) { - this.networkVersion = networkVersion as string; + if (this._state.isConnected && networkVersion !== this.#networkVersion) { + this.#networkVersion = networkVersion as string; if (this._state.initialized) { - this.emit('networkChanged', this.networkVersion); + this.emit('networkChanged', this.#networkVersion); } } } diff --git a/src/initializeInpageProvider.ts b/src/initializeInpageProvider.ts index f95f832b..bae6d2b9 100644 --- a/src/initializeInpageProvider.ts +++ b/src/initializeInpageProvider.ts @@ -55,6 +55,11 @@ export function initializeProvider({ const proxiedProvider = new Proxy(provider, { // some common libraries, e.g. web3@1.x, mess with our API deleteProperty: () => true, + // fix issue with Proxy unable to access private variables from getters + // https://stackoverflow.com/a/73051482 + get(target, propName: 'chainId' | 'networkVersion' | 'selectedAddress') { + return target[propName]; + }, }); if (shouldSetOnWindow) { diff --git a/src/messages.ts b/src/messages.ts index ca5c4bb0..c17778c1 100644 --- a/src/messages.ts +++ b/src/messages.ts @@ -24,6 +24,10 @@ const messages = { `MetaMask: Connected to chain with ID "${chainId}".`, }, warnings: { + // deprecated properties + chainIdDeprecation: `MetaMask: 'ethereum.chainId' is deprecated and may be removed in the future. Please use the 'eth_chainId' RPC method instead.\nFor more information, see: https://github.com/MetaMask/metamask-improvement-proposals/discussions/23`, + networkVersionDeprecation: `MetaMask: 'ethereum.networkVersion' is deprecated and may be removed in the future. Please use the 'net_version' RPC method instead.\nFor more information, see: https://github.com/MetaMask/metamask-improvement-proposals/discussions/23`, + selectedAddressDeprecation: `MetaMask: 'ethereum.selectedAddress' is deprecated and may be removed in the future. Please use the 'eth_accounts' RPC method instead.\nFor more information, see: https://github.com/MetaMask/metamask-improvement-proposals/discussions/23`, // deprecated methods enableDeprecation: `MetaMask: 'ethereum.enable()' is deprecated and may be removed in the future. Please use the 'eth_requestAccounts' RPC method instead.\nFor more information, see: https://eips.ethereum.org/EIPS/eip-1102`, sendDeprecation: `MetaMask: 'ethereum.send(...)' is deprecated and may be removed in the future. Please use 'ethereum.sendAsync(...)' or 'ethereum.request(...)' instead.\nFor more information, see: https://eips.ethereum.org/EIPS/eip-1193`, From 101eb3a0f2a5154ed6ead909e51cef7044614d3a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 14 Sep 2023 09:22:36 -0700 Subject: [PATCH 7/7] 13.0.0 (#283) --------- Co-authored-by: github-actions Co-authored-by: Jiexi Luan --- CHANGELOG.md | 9 ++++++++- package.json | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f11be4a..254d20f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [13.0.0] +### Changed +- **BREAKING**: Update `chainId`, `networkVersion`, and `selectedAddress` to be read-only ([#280](https://github.com/MetaMask/providers/pull/280)) +- Log deprecation warning when accessing `chainId`, `networkVersion`, and `selectedAddress` ([#280](https://github.com/MetaMask/providers/pull/280)) +- Remove `pump` ([#281](https://github.com/MetaMask/providers/pull/281)) + ## [12.0.0] ### Changed - **BREAKING**: Replace `eth-rpc-errors`@`^4.0.2` with `@metamask/rpc-errors`@`6.0.0` ([#277](https://github.com/MetaMask/providers/pull/277)) @@ -238,7 +244,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 added deprecation warnings for them ([#30](https://github.com/MetaMask/providers/pull/30)) - Un-deprecated `sendAsync` ([#29](https://github.com/MetaMask/providers/pull/29)) -[Unreleased]: https://github.com/MetaMask/providers/compare/v12.0.0...HEAD +[Unreleased]: https://github.com/MetaMask/providers/compare/v13.0.0...HEAD +[13.0.0]: https://github.com/MetaMask/providers/compare/v12.0.0...v13.0.0 [12.0.0]: https://github.com/MetaMask/providers/compare/v11.1.2...v12.0.0 [11.1.2]: https://github.com/MetaMask/providers/compare/v11.1.1...v11.1.2 [11.1.1]: https://github.com/MetaMask/providers/compare/v11.1.0...v11.1.1 diff --git a/package.json b/package.json index f6462adb..dbbf4de2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@metamask/providers", - "version": "12.0.0", + "version": "13.0.0", "description": "A JavaScript Ethereum provider that connects to the wallet over a stream.", "keywords": [ "MetaMask",