diff --git a/packages/multichain/jest.config.js b/packages/multichain/jest.config.js index ca084133399..f8be8cb30ee 100644 --- a/packages/multichain/jest.config.js +++ b/packages/multichain/jest.config.js @@ -17,10 +17,10 @@ module.exports = merge(baseConfig, { // An object that configures minimum threshold enforcement for coverage results coverageThreshold: { global: { - branches: 100, - functions: 100, - lines: 100, - statements: 100, + branches: 83.05, + functions: 87.37, + lines: 86.73, + statements: 87.17, }, }, }); diff --git a/packages/multichain/package.json b/packages/multichain/package.json index bfb1d8dd296..e633a1bb2b7 100644 --- a/packages/multichain/package.json +++ b/packages/multichain/package.json @@ -18,33 +18,43 @@ "sideEffects": false, "exports": { ".": { - "import": "./dist/index.mjs", - "require": "./dist/index.js", - "types": "./dist/types/index.d.ts" + "import": { + "types": "./dist/index.d.mts", + "default": "./dist/index.mjs" + }, + "require": { + "types": "./dist/index.d.cts", + "default": "./dist/index.cjs" + } }, "./package.json": "./package.json" }, - "main": "./dist/index.js", - "types": "./dist/types/index.d.ts", + "main": "./dist/index.cjs", + "types": "./dist/index.d.cts", "files": [ "dist/" ], "scripts": { "build": "ts-bridge --project tsconfig.build.json --verbose --clean --no-references", "build:docs": "typedoc", + "changelog:update": "../../scripts/update-changelog.sh @metamask/multichain", "changelog:validate": "../../scripts/validate-changelog.sh @metamask/multichain", "publish:preview": "yarn npm publish --tag preview", - "test": "jest --reporters=jest-silent-reporter", - "test:clean": "jest --clearCache", - "test:verbose": "jest --verbose", - "test:watch": "jest --watch" + "since-latest-release": "../../scripts/since-latest-release.sh", + "test": "NODE_OPTIONS=--experimental-vm-modules jest --reporters=jest-silent-reporter", + "test:clean": "NODE_OPTIONS=--experimental-vm-modules jest --clearCache", + "test:verbose": "NODE_OPTIONS=--experimental-vm-modules jest --verbose", + "test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch" }, "dependencies": { "@metamask/api-specs": "^0.10.12", + "@metamask/controller-utils": "^11.3.0", "@metamask/eth-json-rpc-filters": "^7.0.0", - "@metamask/json-rpc-engine": "^9.0.3", "@metamask/rpc-errors": "^6.3.1", + "@metamask/safe-event-emitter": "^3.0.0", + "@metamask/utils": "^9.1.0", "@open-rpc/schema-utils-js": "^2.0.5", + "jsonschema": "^1.2.4", "lodash": "^4.17.21" }, "devDependencies": { diff --git a/packages/multichain/src/adapters/caip-permission-adapter-eth-accounts.test.ts b/packages/multichain/src/adapters/caip-permission-adapter-eth-accounts.test.ts index 04aba6a3301..9434fab81d9 100644 --- a/packages/multichain/src/adapters/caip-permission-adapter-eth-accounts.test.ts +++ b/packages/multichain/src/adapters/caip-permission-adapter-eth-accounts.test.ts @@ -1,4 +1,4 @@ -import { Caip25CaveatValue } from '../caip25Permission'; +import type { Caip25CaveatValue } from '../caip25Permission'; import { getEthAccounts, setEthAccounts, diff --git a/packages/multichain/src/adapters/caip-permission-adapter-eth-accounts.ts b/packages/multichain/src/adapters/caip-permission-adapter-eth-accounts.ts index d7291f42b87..95cb5dd0ec7 100644 --- a/packages/multichain/src/adapters/caip-permission-adapter-eth-accounts.ts +++ b/packages/multichain/src/adapters/caip-permission-adapter-eth-accounts.ts @@ -1,16 +1,13 @@ import { - CaipAccountId, - Hex, + type CaipAccountId, + type Hex, KnownCaipNamespace, parseCaipAccountId, } from '@metamask/utils'; -import { Caip25CaveatValue } from '../caip25Permission'; -import { - mergeScopes, - parseScopeString, - ScopesObject, - ScopeString, -} from '../scope'; + +import type { Caip25CaveatValue } from '../caip25Permission'; +import type { ScopesObject } from '../scope'; +import { mergeScopes, parseScopeString, type ScopeString } from '../scope'; const isEip155ScopeString = (scopeString: ScopeString) => { const { namespace, reference } = parseScopeString(scopeString); diff --git a/packages/multichain/src/adapters/caip-permission-adapter-middleware.test.ts b/packages/multichain/src/adapters/caip-permission-adapter-middleware.test.ts index f8c0f981371..ea6318074ab 100644 --- a/packages/multichain/src/adapters/caip-permission-adapter-middleware.test.ts +++ b/packages/multichain/src/adapters/caip-permission-adapter-middleware.test.ts @@ -1,11 +1,15 @@ import { providerErrors } from '@metamask/rpc-errors'; +import type { JsonRpcRequest } from '@metamask/utils'; + import { Caip25CaveatType, Caip25EndowmentPermissionName, -} from '../caip25permissions'; -import { CaipPermissionAdapterMiddleware } from './caip-permission-adapter-middleware'; +} from '../caip25Permission'; +import { caipPermissionAdapterMiddleware } from './caip-permission-adapter-middleware'; const baseRequest = { + id: 1, + jsonrpc: '2.0' as const, origin: 'http://test.com', networkClientId: 'mainnet', method: 'eth_call', @@ -48,7 +52,7 @@ const createMockedHandler = () => { }); const getNetworkConfigurationByNetworkClientId = jest .fn() - .mockImplementation((networkClientId) => { + .mockImplementation((networkClientId: string) => { const chainId = { mainnet: '0x1', @@ -58,8 +62,13 @@ const createMockedHandler = () => { chainId, }; }); - const handler = (request) => - CaipPermissionAdapterMiddleware(request, {}, next, end, { + const handler = ( + request: JsonRpcRequest & { + networkClientId: string; + origin: string; + }, + ) => + caipPermissionAdapterMiddleware(request, {}, next, end, { getCaveat, getNetworkConfigurationByNetworkClientId, }); diff --git a/packages/multichain/src/adapters/caip-permission-adapter-middleware.ts b/packages/multichain/src/adapters/caip-permission-adapter-middleware.ts index 867288eb95a..d92e0292e22 100644 --- a/packages/multichain/src/adapters/caip-permission-adapter-middleware.ts +++ b/packages/multichain/src/adapters/caip-permission-adapter-middleware.ts @@ -1,16 +1,43 @@ +import type { NetworkConfiguration } from '@metamask/network-controller'; +import type { Caveat } from '@metamask/permission-controller'; import { providerErrors } from '@metamask/rpc-errors'; +import type { JsonRpcRequest } from '@metamask/utils'; + +import type { Caip25CaveatValue } from '../caip25Permission'; import { Caip25CaveatType, Caip25EndowmentPermissionName, -} from '../caip25permissions'; +} from '../caip25Permission'; +import type { ScopeString } from '../scope'; import { mergeScopes } from '../scope'; -export async function CaipPermissionAdapterMiddleware( - request, - _response, - next, - end, - hooks, +/** + * Middleware to handle CAIP-25 permission requests. + * + * @param request - The request object. + * @param _response - The response object. + * @param next - The next middleware function. + * @param end - The end function. + * @param hooks - The hooks object. + * @param hooks.getCaveat - Function to retrieve a caveat. + * @param hooks.getNetworkConfigurationByNetworkClientId - Function to retrieve a network configuration. + */ +export async function caipPermissionAdapterMiddleware( + request: JsonRpcRequest & { + networkClientId: string; + origin: string; + }, + _response: unknown, + next: () => Promise, + end: (error?: Error) => void, + hooks: { + getCaveat: ( + ...args: unknown[] + ) => Caveat; + getNetworkConfigurationByNetworkClientId: ( + networkClientId: string, + ) => NetworkConfiguration; + }, ) { const { networkClientId, method } = request; @@ -31,7 +58,7 @@ export async function CaipPermissionAdapterMiddleware( const { chainId } = hooks.getNetworkConfigurationByNetworkClientId(networkClientId); - const scope = `eip155:${parseInt(chainId, 16)}`; + const scope: ScopeString = `eip155:${parseInt(chainId, 16)}`; const scopesObject = mergeScopes( caveat.value.requiredScopes, diff --git a/packages/multichain/src/adapters/caip-permission-adapter-permittedChains.test.ts b/packages/multichain/src/adapters/caip-permission-adapter-permittedChains.test.ts index e83562f7dcb..a7402078079 100644 --- a/packages/multichain/src/adapters/caip-permission-adapter-permittedChains.test.ts +++ b/packages/multichain/src/adapters/caip-permission-adapter-permittedChains.test.ts @@ -1,4 +1,4 @@ -import { Caip25CaveatValue } from '../caip25Permission'; +import type { Caip25CaveatValue } from '../caip25Permission'; import { KnownNotifications, KnownRpcMethods } from '../scope'; import { addPermittedEthChainId, diff --git a/packages/multichain/src/adapters/caip-permission-adapter-permittedChains.ts b/packages/multichain/src/adapters/caip-permission-adapter-permittedChains.ts index 39b2b86bea3..cfbbdedd298 100644 --- a/packages/multichain/src/adapters/caip-permission-adapter-permittedChains.ts +++ b/packages/multichain/src/adapters/caip-permission-adapter-permittedChains.ts @@ -1,13 +1,14 @@ -import { Hex, KnownCaipNamespace } from '@metamask/utils'; import { toHex } from '@metamask/controller-utils'; -import { Caip25CaveatValue } from '../caip25Permission'; +import type { Hex } from '@metamask/utils'; +import { KnownCaipNamespace } from '@metamask/utils'; + +import type { Caip25CaveatValue } from '../caip25Permission'; +import type { ScopesObject, ScopeString } from '../scope'; import { KnownNotifications, KnownRpcMethods, mergeScopes, parseScopeString, - ScopesObject, - ScopeString, } from '../scope'; export const getPermittedEthChainIds = ( diff --git a/packages/multichain/src/caip25Permission.test.ts b/packages/multichain/src/caip25Permission.test.ts index cf63cf0a0c2..7e6d6f243d7 100644 --- a/packages/multichain/src/caip25Permission.test.ts +++ b/packages/multichain/src/caip25Permission.test.ts @@ -1,19 +1,20 @@ +import type { NonEmptyArray } from '@metamask/controller-utils'; +import type { CaveatConstraint } from '@metamask/permission-controller'; import { - CaveatConstraint, CaveatMutatorOperation, PermissionType, SubjectType, } from '@metamask/permission-controller'; -import { NonEmptyArray } from '@metamask/controller-utils'; -import * as Scope from './scope'; + +import type { Caip25CaveatValue } from './caip25Permission'; import { Caip25CaveatType, - Caip25CaveatValue, caip25EndowmentBuilder, Caip25EndowmentPermissionName, Caip25CaveatMutatorFactories, removeScope, } from './caip25Permission'; +import * as Scope from './scope'; jest.mock('./scope', () => ({ validateAndFlattenScopes: jest.fn(), @@ -655,34 +656,36 @@ describe('endowment:caip25', () => { }, }, }); - validator({ - caveats: [ - { - type: Caip25CaveatType, - value: { - requiredScopes: { - 'eip155:1': { - methods: ['eth_chainId'], - notifications: [], - accounts: ['eip155:1:0xdead'], + expect( + validator({ + caveats: [ + { + type: Caip25CaveatType, + value: { + requiredScopes: { + 'eip155:1': { + methods: ['eth_chainId'], + notifications: [], + accounts: ['eip155:1:0xdead'], + }, }, - }, - optionalScopes: { - 'eip155:5': { - methods: [], - notifications: [], - accounts: ['eip155:5:0xbeef'], + optionalScopes: { + 'eip155:5': { + methods: [], + notifications: [], + accounts: ['eip155:5:0xbeef'], + }, }, + isMultichainOrigin: true, }, - isMultichainOrigin: true, }, - }, - ], - date: 1234, - id: '1', - invoker: 'test.com', - parentCapability: Caip25EndowmentPermissionName, - }); + ], + date: 1234, + id: '1', + invoker: 'test.com', + parentCapability: Caip25EndowmentPermissionName, + }), + ).toBeUndefined(); }); }); }); diff --git a/packages/multichain/src/caip25Permission.ts b/packages/multichain/src/caip25Permission.ts index 35312b2e11f..d4cb66428cd 100644 --- a/packages/multichain/src/caip25Permission.ts +++ b/packages/multichain/src/caip25Permission.ts @@ -1,4 +1,4 @@ -import { strict as assert } from 'assert'; +import type { NetworkClientId } from '@metamask/network-controller'; import type { PermissionSpecificationBuilder, EndowmentGetterParams, @@ -11,22 +11,17 @@ import { PermissionType, SubjectType, } from '@metamask/permission-controller'; +import type { CaipAccountId, Json } from '@metamask/utils'; import { - CaipAccountId, - Json, parseCaipAccountId, type Hex, type NonEmptyArray, } from '@metamask/utils'; -import { NetworkClientId } from '@metamask/network-controller'; +import { strict as assert } from 'assert'; import { cloneDeep, isEqual } from 'lodash'; -import { - ExternalScopeString, - validateAndFlattenScopes, - ScopesObject, - ScopeObject, - assertScopesSupported, -} from './scope'; + +import type { ExternalScopeString, ScopesObject, ScopeObject } from './scope'; +import { validateAndFlattenScopes, assertScopesSupported } from './scope'; export type Caip25CaveatValue = { requiredScopes: ScopesObject; @@ -58,7 +53,7 @@ type Caip25EndowmentSpecification = ValidPermissionSpecification<{ * `endowment:caip25` returns nothing atm; * * @param builderOptions - The specification builder options. - * @param builderOptions.findNetworkClientIdByChainId + * @param builderOptions.findNetworkClientIdByChainId - The hook to find the networkClientId for a chainId. * @returns The specification for the `caip25` endowment. */ const specificationBuilder: PermissionSpecificationBuilder< @@ -145,9 +140,9 @@ export const Caip25CaveatMutatorFactories = { }, }; -const reduceKeysHelper = ( - acc: Record, - [key, value]: [K, V], +const reduceKeysHelper = ( + acc: Record, + [key, value]: [Key, Value], ) => { return { ...acc, @@ -155,6 +150,12 @@ const reduceKeysHelper = ( }; }; +/** + * Removes the account from the scope object. + * + * @param targetAddress - The address to remove from the scope object. + * @returns A function that removes the account from the scope object. + */ function removeAccountFilterFn(targetAddress: string) { return (account: CaipAccountId) => { const parsed = parseCaipAccountId(account); @@ -162,6 +163,12 @@ function removeAccountFilterFn(targetAddress: string) { }; } +/** + * Removes the account from the scope object. + * + * @param targetAddress - The address to remove from the scope object. + * @param scopeObject - The scope object to remove the account from. + */ function removeAccountOnScope(targetAddress: string, scopeObject: ScopeObject) { if (scopeObject.accounts) { scopeObject.accounts = scopeObject.accounts.filter( @@ -170,6 +177,13 @@ function removeAccountOnScope(targetAddress: string, scopeObject: ScopeObject) { } } +/** + * Removes the target account from the scope object. + * + * @param targetAddress - The address to remove from the scope object. + * @param existingScopes - The scope object to remove the account from. + * @returns The updated scope object. + */ function removeAccount( targetAddress: string, // non caip-10 formatted address existingScopes: Caip25CaveatValue, @@ -208,6 +222,7 @@ function removeAccount( * * @param targetScopeString - The scope that is being removed. * @param caip25CaveatValue - The CAIP-25 permission caveat value to remove the scope from. + * @returns The updated CAIP-25 permission caveat value. */ export function removeScope( targetScopeString: ExternalScopeString, diff --git a/packages/multichain/src/handlers/wallet-getSession.test.ts b/packages/multichain/src/handlers/wallet-getSession.test.ts index bbbad820a20..ebee666967b 100644 --- a/packages/multichain/src/handlers/wallet-getSession.test.ts +++ b/packages/multichain/src/handlers/wallet-getSession.test.ts @@ -1,12 +1,17 @@ +import type { JsonRpcRequest } from '@metamask/utils'; + import { Caip25CaveatType, Caip25EndowmentPermissionName, } from '../caip25Permission'; import { walletGetSessionHandler } from './wallet-getSession'; -const baseRequest = { +const baseRequest: JsonRpcRequest & { origin: string } = { origin: 'http://test.com', + jsonrpc: '2.0' as const, + method: 'wallet_getSession', params: {}, + id: 1, }; const createMockedHandler = () => { @@ -36,8 +41,14 @@ const createMockedHandler = () => { }, }, }); - const response = {}; - const handler = (request) => + const response = { + result: { + sessionScopes: {}, + }, + id: 1, + jsonrpc: '2.0' as const, + }; + const handler = (request: JsonRpcRequest & { origin: string }) => walletGetSessionHandler(request, response, next, end, { getCaveat, }); diff --git a/packages/multichain/src/handlers/wallet-getSession.ts b/packages/multichain/src/handlers/wallet-getSession.ts index 5bcefe07059..7f0032d00d7 100644 --- a/packages/multichain/src/handlers/wallet-getSession.ts +++ b/packages/multichain/src/handlers/wallet-getSession.ts @@ -1,15 +1,36 @@ +import type { Caveat } from '@metamask/permission-controller'; +import type { JsonRpcRequest, JsonRpcSuccess } from '@metamask/utils'; + +import type { Caip25CaveatValue } from '../caip25Permission'; import { Caip25CaveatType, Caip25EndowmentPermissionName, } from '../caip25Permission'; +import type { ScopesObject } from '../scope'; import { mergeScopes } from '../scope'; +/** + * Handler for the `wallet_getSession` RPC method. + * + * @param request - The request object. + * @param response - The response object. + * @param _next - The next middleware function. + * @param end - The end function. + * @param hooks - The hooks object. + * @param hooks.getCaveat - Function to retrieve a caveat. + */ export async function walletGetSessionHandler( - request, - response, - _next, - end, - hooks, + request: JsonRpcRequest & { origin: string }, + response: JsonRpcSuccess<{ sessionScopes: ScopesObject }>, + _next: () => void, + end: () => void, + hooks: { + getCaveat: ( + origin: string, + endowmentPermissionName: string, + caveatType: string, + ) => Caveat; + }, ) { let caveat; try { diff --git a/packages/multichain/src/handlers/wallet-invokeMethod.test.ts b/packages/multichain/src/handlers/wallet-invokeMethod.test.ts index 56d46d1c024..ebffb0ece8b 100644 --- a/packages/multichain/src/handlers/wallet-invokeMethod.test.ts +++ b/packages/multichain/src/handlers/wallet-invokeMethod.test.ts @@ -1,4 +1,6 @@ import { providerErrors, rpcErrors } from '@metamask/rpc-errors'; +import type { JsonRpcRequest } from '@metamask/utils'; + import { Caip25CaveatType, Caip25EndowmentPermissionName, @@ -6,7 +8,10 @@ import { import { walletInvokeMethodHandler } from './wallet-invokeMethod'; const createMockedRequest = () => ({ + jsonrpc: '2.0' as const, + id: 0, origin: 'http://test.com', + method: 'wallet_invokeMethod', params: { scope: 'eip155:1', request: { @@ -54,8 +59,8 @@ const createMockedHandler = () => { const getSelectedNetworkClientId = jest .fn() .mockReturnValue('selectedNetworkClientId'); - const handler = (request) => - walletInvokeMethodHandler(request, {}, next, end, { + const handler = (request: JsonRpcRequest & { origin: string }) => + walletInvokeMethodHandler(request, { jsonrpc: '2.0', id: 1 }, next, end, { getCaveat, findNetworkClientIdByChainId, getSelectedNetworkClientId, @@ -180,6 +185,8 @@ describe('wallet_invokeMethod', () => { await handler(request); expect(request).toStrictEqual({ + jsonrpc: '2.0' as const, + id: 0, scope: 'eip155:1', origin: 'http://test.com', networkClientId: 'mainnet', @@ -248,6 +255,8 @@ describe('wallet_invokeMethod', () => { }; await handler(walletRequest); expect(walletRequest).toStrictEqual({ + jsonrpc: '2.0' as const, + id: 0, scope: 'wallet', origin: 'http://test.com', networkClientId: 'selectedNetworkClientId', diff --git a/packages/multichain/src/handlers/wallet-invokeMethod.ts b/packages/multichain/src/handlers/wallet-invokeMethod.ts index 1ca8ff5b1ea..55f60060831 100644 --- a/packages/multichain/src/handlers/wallet-invokeMethod.ts +++ b/packages/multichain/src/handlers/wallet-invokeMethod.ts @@ -1,19 +1,51 @@ -import { numberToHex } from '@metamask/utils'; +import type { Caveat } from '@metamask/permission-controller'; import { providerErrors, rpcErrors } from '@metamask/rpc-errors'; +import type { + Json, + JsonRpcRequest, + PendingJsonRpcResponse, +} from '@metamask/utils'; +import { numberToHex } from '@metamask/utils'; + +import type { Caip25CaveatValue } from '../caip25Permission'; import { Caip25CaveatType, Caip25EndowmentPermissionName, } from '../caip25Permission'; +import type { ScopeString } from '../scope'; import { mergeScopes, parseScopeString } from '../scope'; +/** + * Handler for the `wallet_invokeMethod` RPC method. + * + * @param request - The request object. + * @param _response - The response object. + * @param next - The next middleware function. + * @param end - The end function. + * @param hooks - The hooks object. + * @param hooks.getCaveat - the hook for getting a caveat from a permission for an origin. + * @param hooks.findNetworkClientIdByChainId - the hook for finding the networkClientId for a chainId. + * @param hooks.getSelectedNetworkClientId - the hook for getting the current globally selected networkClientId. + */ export async function walletInvokeMethodHandler( - request, - _response, - next, - end, - hooks, + request: JsonRpcRequest & { origin: string }, + _response: PendingJsonRpcResponse, + next: () => void, + end: (error: Error) => void, + hooks: { + getCaveat: ( + origin: string, + endowmentPermissionName: string, + caveatType: string, + ) => Caveat; + findNetworkClientIdByChainId: (chainId: string) => string | undefined; + getSelectedNetworkClientId: () => string; + }, ) { - const { scope, request: wrappedRequest } = request.params; + const { scope, request: wrappedRequest } = request.params as { + scope: ScopeString; + request: JsonRpcRequest; + }; let caveat; try { diff --git a/packages/multichain/src/handlers/wallet-revokeSession.test.ts b/packages/multichain/src/handlers/wallet-revokeSession.test.ts index 6a6add80232..695d0eb4304 100644 --- a/packages/multichain/src/handlers/wallet-revokeSession.test.ts +++ b/packages/multichain/src/handlers/wallet-revokeSession.test.ts @@ -3,20 +3,29 @@ import { UnrecognizedSubjectError, } from '@metamask/permission-controller'; import { rpcErrors } from '@metamask/rpc-errors'; +import type { JsonRpcRequest } from '@metamask/utils'; + import { Caip25EndowmentPermissionName } from '../caip25Permission'; import { walletRevokeSessionHandler } from './wallet-revokeSession'; -const baseRequest = { +const baseRequest: JsonRpcRequest & { origin: string } = { origin: 'http://test.com', params: {}, + jsonrpc: '2.0' as const, + id: 1, + method: 'wallet_revokeSession', }; const createMockedHandler = () => { const next = jest.fn(); const end = jest.fn(); const revokePermission = jest.fn(); - const response = {}; - const handler = (request) => + const response = { + result: true, + id: 1, + jsonrpc: '2.0' as const, + }; + const handler = (request: JsonRpcRequest & { origin: string }) => walletRevokeSessionHandler(request, response, next, end, { revokePermission, }); @@ -44,21 +53,24 @@ describe('wallet_revokeSession', () => { it('returns true if the CAIP-25 endowment permission does not exist', async () => { const { handler, response, revokePermission } = createMockedHandler(); revokePermission.mockImplementation(() => { - throw new PermissionDoesNotExistError(); + throw new PermissionDoesNotExistError( + 'foo.com', + Caip25EndowmentPermissionName, + ); }); await handler(baseRequest); - expect(response.result).toStrictEqual(true); + expect(response.result).toBe(true); }); it('returns true if the subject does not exist', async () => { const { handler, response, revokePermission } = createMockedHandler(); revokePermission.mockImplementation(() => { - throw new UnrecognizedSubjectError(); + throw new UnrecognizedSubjectError('foo.com'); }); await handler(baseRequest); - expect(response.result).toStrictEqual(true); + expect(response.result).toBe(true); }); it('throws an internal RPC error if something unexpected goes wrong with revoking the permission', async () => { @@ -75,6 +87,6 @@ describe('wallet_revokeSession', () => { const { handler, response } = createMockedHandler(); await handler(baseRequest); - expect(response.result).toStrictEqual(true); + expect(response.result).toBe(true); }); }); diff --git a/packages/multichain/src/handlers/wallet-revokeSession.ts b/packages/multichain/src/handlers/wallet-revokeSession.ts index d76994de901..1aec0b7245b 100644 --- a/packages/multichain/src/handlers/wallet-revokeSession.ts +++ b/packages/multichain/src/handlers/wallet-revokeSession.ts @@ -1,20 +1,33 @@ -import type { JsonRpcEngineNextCallback, JsonRpcEngineEndCallback } from '@metamask/json-rpc-engine'; +import type { + JsonRpcEngineNextCallback, + JsonRpcEngineEndCallback, +} from '@metamask/json-rpc-engine'; import { PermissionDoesNotExistError, UnrecognizedSubjectError, - PermissionController } from '@metamask/permission-controller'; import { rpcErrors } from '@metamask/rpc-errors'; +import type { JsonRpcSuccess, Json, JsonRpcRequest } from '@metamask/utils'; + import { Caip25EndowmentPermissionName } from '../caip25Permission'; -import { JsonRpcRequest, JsonRpcResponse } from '@metamask/utils'; +/** + * Handles the `wallet_revokeSession` RPC method. + * + * @param request - The JSON-RPC request object. + * @param response - The JSON-RPC response object. + * @param _next - The next middleware function. + * @param end - The end callback function. + * @param hooks - The hooks object. + * @param hooks.revokePermission - The revokePermission function. + */ export async function walletRevokeSessionHandler( - request: JsonRpcRequest, - response: JsonRpcResponse, + request: JsonRpcRequest & { origin: string }, + response: JsonRpcSuccess, _next: JsonRpcEngineNextCallback, end: JsonRpcEngineEndCallback, hooks: { - revokePermission: PermissionController['revokePermission'] + revokePermission: (origin: string, permissionName: string) => void; }, ) { try { diff --git a/packages/multichain/src/middlewares/MultichainMiddlewareManager.test.ts b/packages/multichain/src/middlewares/MultichainMiddlewareManager.test.ts index b00cd7ab4ed..c6097529946 100644 --- a/packages/multichain/src/middlewares/MultichainMiddlewareManager.test.ts +++ b/packages/multichain/src/middlewares/MultichainMiddlewareManager.test.ts @@ -1,7 +1,5 @@ -import { JsonRpcRequest } from '@metamask/utils'; -import MultichainMiddlewareManager, { - ExtendedJsonRpcMiddleware, -} from './MultichainMiddlewareManager'; +import type { ExtendedJsonRpcMiddleware } from './MultichainMiddlewareManager'; +import MultichainMiddlewareManager from './MultichainMiddlewareManager'; const scope = 'eip155:1'; const origin = 'example.com'; @@ -28,13 +26,13 @@ describe('MultichainMiddlewareManager', () => { const endSpy = jest.fn(); middleware( - { scope } as unknown as JsonRpcRequest, + { jsonrpc: '2.0' as const, id: 0, method: 'method', scope }, { jsonrpc: '2.0', id: 0 }, nextSpy, endSpy, ); expect(middlewareSpy).toHaveBeenCalledWith( - { scope } as unknown as JsonRpcRequest, + { jsonrpc: '2.0' as const, id: 0, method: 'method', scope }, { jsonrpc: '2.0', id: 0 }, nextSpy, endSpy, @@ -43,7 +41,7 @@ describe('MultichainMiddlewareManager', () => { expect(endSpy).not.toHaveBeenCalled(); }); - it('should remove middleware by origin and tabId when the multiplexing middleware is destroyed', () => { + it('should remove middleware by origin and tabId when the multiplexing middleware is destroyed', async () => { const multichainMiddlewareManager = new MultichainMiddlewareManager(); const middlewareSpy = jest.fn() as unknown as ExtendedJsonRpcMiddleware; multichainMiddlewareManager.addMiddleware({ @@ -59,13 +57,13 @@ describe('MultichainMiddlewareManager', () => { 123, ); - middleware.destroy?.(); + await middleware.destroy?.(); const nextSpy = jest.fn(); const endSpy = jest.fn(); middleware( - { scope } as unknown as JsonRpcRequest, + { jsonrpc: '2.0' as const, id: 0, method: 'method', scope }, { jsonrpc: '2.0', id: 0 }, nextSpy, endSpy, @@ -97,7 +95,7 @@ describe('MultichainMiddlewareManager', () => { const endSpy = jest.fn(); middleware( - { scope } as unknown as JsonRpcRequest, + { jsonrpc: '2.0' as const, id: 0, method: 'method', scope }, { jsonrpc: '2.0', id: 0 }, nextSpy, endSpy, @@ -129,7 +127,7 @@ describe('MultichainMiddlewareManager', () => { const endSpy = jest.fn(); middleware( - { scope } as unknown as JsonRpcRequest, + { jsonrpc: '2.0' as const, id: 0, method: 'method', scope }, { jsonrpc: '2.0', id: 0 }, nextSpy, endSpy, @@ -161,7 +159,7 @@ describe('MultichainMiddlewareManager', () => { const endSpy = jest.fn(); middleware( - { scope } as unknown as JsonRpcRequest, + { jsonrpc: '2.0' as const, id: 0, method: 'method', scope }, { jsonrpc: '2.0', id: 0 }, nextSpy, endSpy, diff --git a/packages/multichain/src/middlewares/MultichainMiddlewareManager.ts b/packages/multichain/src/middlewares/MultichainMiddlewareManager.ts index e4c2663099b..205c6a67518 100644 --- a/packages/multichain/src/middlewares/MultichainMiddlewareManager.ts +++ b/packages/multichain/src/middlewares/MultichainMiddlewareManager.ts @@ -1,11 +1,23 @@ -import { JsonRpcMiddleware } from '@metamask/json-rpc-engine'; -import { ExternalScopeString } from '../scope'; -import { Json, JsonRpcParams } from '@metamask/utils'; - -// Extend JsonRpcMiddleware to include the destroy method -// this was introduced in 7.0.0 of json-rpc-engine: https://github.com/MetaMask/json-rpc-engine/blob/v7.0.0/src/JsonRpcEngine.ts#L29-L40 -export type ExtendedJsonRpcMiddleware = JsonRpcMiddleware & { - destroy?: () => void; +import type { + JsonRpcEngineEndCallback, + JsonRpcEngineNextCallback, +} from '@metamask/json-rpc-engine'; +import type { + Json, + JsonRpcRequest, + PendingJsonRpcResponse, +} from '@metamask/utils'; + +import type { ExternalScopeString } from '../scope'; + +export type ExtendedJsonRpcMiddleware = { + ( + req: JsonRpcRequest & { scope: string }, + res: PendingJsonRpcResponse, + next: JsonRpcEngineNextCallback, + end: JsonRpcEngineEndCallback, + ): void; + destroy?: () => void | Promise; }; type MiddlewareKey = { @@ -57,7 +69,10 @@ export default class MultichainMiddlewareManager { return; } - existingMiddlewareEntry.middleware.destroy?.(); + // When the destroy function on the middleware is async, + // we don't need to wait for it complete + // eslint-disable-next-line no-void + void existingMiddlewareEntry.middleware.destroy?.(); this.#removeMiddlewareEntry(middlewareKey); } @@ -97,10 +112,7 @@ export default class MultichainMiddlewareManager { tabId?: number, ) { const middleware: ExtendedJsonRpcMiddleware = (req, res, next, end) => { - const r = req as unknown as { - scope: string; - }; - const { scope } = r; + const { scope } = req; const middlewareEntry = this.#getMiddlewareEntry({ scope, origin, @@ -110,8 +122,9 @@ export default class MultichainMiddlewareManager { if (middlewareEntry) { middlewareEntry.middleware(req, res, next, end); } else { - next(); + return next(); } + return undefined; }; middleware.destroy = this.removeMiddlewareByOriginAndTabId.bind( this, diff --git a/packages/multichain/src/middlewares/MultichainSubscriptionManager.test.ts b/packages/multichain/src/middlewares/MultichainSubscriptionManager.test.ts index 86fda171cec..c951b711af7 100644 --- a/packages/multichain/src/middlewares/MultichainSubscriptionManager.test.ts +++ b/packages/multichain/src/middlewares/MultichainSubscriptionManager.test.ts @@ -1,4 +1,5 @@ import createSubscriptionManager from '@metamask/eth-json-rpc-filters/subscriptionManager'; + import MultichainSubscriptionManager from './MultichainSubscriptionManager'; jest.mock('@metamask/eth-json-rpc-filters/subscriptionManager', () => @@ -63,7 +64,7 @@ describe('MultichainSubscriptionManager', () => { }); it('should subscribe to a scope, origin, and tabId', () => { - const { multichainSubscriptionManager} = + const { multichainSubscriptionManager } = createMultichainSubscriptionManager(); multichainSubscriptionManager.subscribe({ scope, origin, tabId }); const onNotificationSpy = jest.fn(); @@ -83,7 +84,7 @@ describe('MultichainSubscriptionManager', () => { }); it('should unsubscribe from a scope', () => { - const { multichainSubscriptionManager} = + const { multichainSubscriptionManager } = createMultichainSubscriptionManager(); multichainSubscriptionManager.subscribe({ scope, origin, tabId }); multichainSubscriptionManager.unsubscribeByScope(scope); @@ -92,7 +93,7 @@ describe('MultichainSubscriptionManager', () => { }); it('should unsubscribe from a scope and origin', () => { - const { multichainSubscriptionManager} = + const { multichainSubscriptionManager } = createMultichainSubscriptionManager(); multichainSubscriptionManager.subscribe({ scope, origin, tabId }); multichainSubscriptionManager.unsubscribeByScopeAndOrigin(scope, origin); @@ -105,7 +106,7 @@ describe('MultichainSubscriptionManager', () => { }); it('should unsubscribe from a origin and tabId', () => { - const { multichainSubscriptionManager} = + const { multichainSubscriptionManager } = createMultichainSubscriptionManager(); multichainSubscriptionManager.subscribe({ scope, origin, tabId }); multichainSubscriptionManager.unsubscribeByOriginAndTabId(origin, tabId); diff --git a/packages/multichain/src/middlewares/MultichainSubscriptionManager.ts b/packages/multichain/src/middlewares/MultichainSubscriptionManager.ts index 668aa431aa6..7ffb75b7230 100644 --- a/packages/multichain/src/middlewares/MultichainSubscriptionManager.ts +++ b/packages/multichain/src/middlewares/MultichainSubscriptionManager.ts @@ -1,9 +1,11 @@ -import EventEmitter from 'events'; -import { NetworkController } from '@metamask/network-controller'; -import SafeEventEmitter from '@metamask/safe-event-emitter'; -import { CaipChainId, Hex, parseCaipChainId } from '@metamask/utils'; import { toHex } from '@metamask/controller-utils'; -import { ExternalScopeString } from '../scope'; +import type { NetworkController } from '@metamask/network-controller'; +import SafeEventEmitter from '@metamask/safe-event-emitter'; +import type { CaipChainId, Hex } from '@metamask/utils'; +import { parseCaipChainId } from '@metamask/utils'; +import type EventEmitter from 'events'; + +import type { ExternalScopeString } from '../scope'; export type SubscriptionManager = { events: EventEmitter; diff --git a/packages/multichain/src/middlewares/multichainMethodCallValidator.ts b/packages/multichain/src/middlewares/multichainMethodCallValidator.ts index 7c68c56b916..a57ac7835a6 100644 --- a/packages/multichain/src/middlewares/multichainMethodCallValidator.ts +++ b/packages/multichain/src/middlewares/multichainMethodCallValidator.ts @@ -1,21 +1,22 @@ import { MultiChainOpenRPCDocument } from '@metamask/api-specs'; +import type { JsonRpcMiddleware } from '@metamask/json-rpc-engine'; import { rpcErrors } from '@metamask/rpc-errors'; -import { +import { isObject } from '@metamask/utils'; +import type { + Json, JsonRpcError, JsonRpcParams, JsonRpcRequest, - isObject, } from '@metamask/utils'; -import { +import type { ContentDescriptorObject, MethodObject, OpenrpcDocument, } from '@open-rpc/meta-schema'; import dereferenceDocument from '@open-rpc/schema-utils-js/build/dereference-document'; import { makeCustomResolver } from '@open-rpc/schema-utils-js/build/parse-open-rpc-document'; -import { JsonRpcMiddleware } from '@metamask/json-rpc-engine'; -import { Json } from '@metamask/utils'; -import { Schema, ValidationError, Validator } from 'jsonschema'; +import type { Schema, ValidationError } from 'jsonschema'; +import { Validator } from 'jsonschema'; const transformError = ( error: ValidationError, @@ -88,6 +89,7 @@ export const multichainMethodCallValidatorMiddleware: JsonRpcMiddleware< JsonRpcRequest, Json > = function (request, _response, next, end) { + // eslint-disable-next-line @typescript-eslint/no-floating-promises multichainMethodCallValidator(request.method, request.params).then( (errors) => { if (errors) { diff --git a/packages/multichain/src/scope/assert.test.ts b/packages/multichain/src/scope/assert.test.ts index 919b6e6a388..75485352bac 100644 --- a/packages/multichain/src/scope/assert.test.ts +++ b/packages/multichain/src/scope/assert.test.ts @@ -1,6 +1,7 @@ import { JsonRpcError } from '@metamask/rpc-errors'; + import { assertScopeSupported, assertScopesSupported } from './assert'; -import { ScopeObject } from './scope'; +import type { ScopeObject } from './scope'; import * as Supported from './supported'; jest.mock('./supported', () => ({ @@ -133,10 +134,7 @@ describe('Scope Assert', () => { }, ); }).toThrow( - new JsonRpcError( - 5102, - 'Requested notifications are not supported', - ), + new JsonRpcError(5102, 'Requested notifications are not supported'), ); }); @@ -165,12 +163,14 @@ describe('Scope Assert', () => { const isChainIdSupported = jest.fn(); it('does not throw an error if no scopes are defined', () => { - assertScopesSupported( - {}, - { - isChainIdSupported, - }, - ); + expect( + assertScopesSupported( + {}, + { + isChainIdSupported, + }, + ), + ).toBeUndefined(); }); it('throws an error if any scope is invalid', () => { @@ -185,9 +185,7 @@ describe('Scope Assert', () => { isChainIdSupported, }, ); - }).toThrow( - new JsonRpcError(5100, 'Requested chains are not supported'), - ); + }).toThrow(new JsonRpcError(5100, 'Requested chains are not supported')); }); it('does not throw an error if all scopes are valid', () => { diff --git a/packages/multichain/src/scope/assert.ts b/packages/multichain/src/scope/assert.ts index 2724ecd2214..ea436fb9091 100644 --- a/packages/multichain/src/scope/assert.ts +++ b/packages/multichain/src/scope/assert.ts @@ -1,11 +1,12 @@ -import { Hex } from '@metamask/utils'; import { JsonRpcError } from '@metamask/rpc-errors'; +import type { Hex } from '@metamask/utils'; + +import type { ScopeObject, ScopesObject } from './scope'; import { isSupportedMethod, isSupportedNotification, isSupportedScopeString, } from './supported'; -import { ScopeObject, ScopesObject } from './scope'; export const assertScopeSupported = ( scopeString: string, @@ -50,10 +51,7 @@ export const assertScopeSupported = ( // When provider does not recognize one or more requested notification(s) // code = 5202 // message = "Unknown notification(s) requested" - throw new JsonRpcError( - 5102, - 'Requested notifications are not supported', - ); + throw new JsonRpcError(5102, 'Requested notifications are not supported'); } }; diff --git a/packages/multichain/src/scope/authorization.test.ts b/packages/multichain/src/scope/authorization.test.ts index 69e57dc32ca..318718133d3 100644 --- a/packages/multichain/src/scope/authorization.test.ts +++ b/packages/multichain/src/scope/authorization.test.ts @@ -1,11 +1,8 @@ -import * as Validation from './validation'; -import * as Transform from './transform'; +import { bucketScopes, validateAndFlattenScopes } from './authorization'; import * as Filter from './filter'; -import { - bucketScopes, - validateAndFlattenScopes, -} from './authorization'; -import { ExternalScopeObject } from './scope'; +import type { ExternalScopeObject } from './scope'; +import * as Transform from './transform'; +import * as Validation from './validation'; jest.mock('./validation', () => ({ validateScopes: jest.fn(), diff --git a/packages/multichain/src/scope/authorization.ts b/packages/multichain/src/scope/authorization.ts index b6c83cb1cf6..3b4f5e06199 100644 --- a/packages/multichain/src/scope/authorization.ts +++ b/packages/multichain/src/scope/authorization.ts @@ -1,8 +1,9 @@ -import { validateScopes } from './validation'; -import { ExternalScopesObject, ScopesObject, ScopedProperties } from './scope'; -import { flattenMergeScopes } from './transform'; +import type { Hex } from '@metamask/utils'; + import { bucketScopesBySupport } from './filter'; -import { Hex } from '@metamask/utils'; +import type { ExternalScopesObject, ScopesObject } from './scope'; +import { flattenMergeScopes } from './transform'; +import { validateScopes } from './validation'; export type Caip25Authorization = | { diff --git a/packages/multichain/src/scope/filter.ts b/packages/multichain/src/scope/filter.ts index 06b9795c497..ab5e889af17 100644 --- a/packages/multichain/src/scope/filter.ts +++ b/packages/multichain/src/scope/filter.ts @@ -1,6 +1,7 @@ -import { CaipChainId, Hex } from '@metamask/utils'; -import { ScopesObject } from './scope'; +import type { CaipChainId, Hex } from '@metamask/utils'; + import { assertScopeSupported } from './assert'; +import type { ScopesObject } from './scope'; export const bucketScopesBySupport = ( scopes: ScopesObject, diff --git a/packages/multichain/src/scope/scope.test.ts b/packages/multichain/src/scope/scope.test.ts index 2441c41c348..d3a58b3221a 100644 --- a/packages/multichain/src/scope/scope.test.ts +++ b/packages/multichain/src/scope/scope.test.ts @@ -6,7 +6,7 @@ describe('Scope', () => { expect(parseScopeString('abc')).toStrictEqual({ namespace: 'abc' }); }); - it('returns the namespace and reference if scopeString is a CAIP chain ID ', () => { + it('returns the namespace and reference if scopeString is a CAIP chain ID', () => { expect(parseScopeString('abc:foo')).toStrictEqual({ namespace: 'abc', reference: 'foo', diff --git a/packages/multichain/src/scope/scope.ts b/packages/multichain/src/scope/scope.ts index ae452ee6536..c899fa8a40e 100644 --- a/packages/multichain/src/scope/scope.ts +++ b/packages/multichain/src/scope/scope.ts @@ -1,18 +1,20 @@ import MetaMaskOpenRPCDocument from '@metamask/api-specs'; -import { +import type { CaipChainId, CaipReference, CaipAccountId, + KnownCaipNamespace, + CaipNamespace, +} from '@metamask/utils'; +import { isCaipNamespace, isCaipChainId, parseCaipChainId, - KnownCaipNamespace, - CaipNamespace, } from '@metamask/utils'; -export type NonWalletKnownCaipNamespace = Exclude< +export type NonWalletKnownCaipNamespace = Extract< KnownCaipNamespace, - KnownCaipNamespace.Wallet + KnownCaipNamespace.Eip155 >; export const KnownWalletRpcMethods: string[] = [ @@ -22,7 +24,7 @@ export const KnownWalletRpcMethods: string[] = [ const WalletEip155Methods = ['wallet_addEthereumChain']; const Eip155Methods = MetaMaskOpenRPCDocument.methods - .map(({ name }: { name: string}) => name) + .map(({ name }: { name: string }) => name) .filter((method: string) => !WalletEip155Methods.includes(method)) .filter((method: string) => !KnownWalletRpcMethods.includes(method)); diff --git a/packages/multichain/src/scope/supported.test.ts b/packages/multichain/src/scope/supported.test.ts index 30b85491079..b8146ebd157 100644 --- a/packages/multichain/src/scope/supported.test.ts +++ b/packages/multichain/src/scope/supported.test.ts @@ -1,14 +1,14 @@ -import { - isSupportedMethod, - isSupportedNotification, - isSupportedScopeString, -} from './supported'; import { KnownNotifications, KnownRpcMethods, KnownWalletNamespaceRpcMethods, KnownWalletRpcMethods, } from './scope'; +import { + isSupportedMethod, + isSupportedNotification, + isSupportedScopeString, +} from './supported'; describe('Scope Support', () => { describe('isSupportedNotification', () => { @@ -16,18 +16,14 @@ describe('Scope Support', () => { 'returns true for each %s scope method', (scopeString: string, notifications: string[]) => { notifications.forEach((notification) => { - expect( - isSupportedNotification(scopeString, notification), - ).toStrictEqual(true); + expect(isSupportedNotification(scopeString, notification)).toBe(true); }); }, ); it('returns false otherwise', () => { - expect(isSupportedNotification('eip155', 'anything else')).toStrictEqual( - false, - ); - expect(isSupportedNotification('', '')).toStrictEqual(false); + expect(isSupportedNotification('eip155', 'anything else')).toBe(false); + expect(isSupportedNotification('', '')).toBe(false); }); }); @@ -36,14 +32,14 @@ describe('Scope Support', () => { 'returns true for each %s scoped method', (scopeString: string, methods: string[]) => { methods.forEach((method) => { - expect(isSupportedMethod(scopeString, method)).toStrictEqual(true); + expect(isSupportedMethod(scopeString, method)).toBe(true); }); }, ); it('returns true for each wallet scoped method', () => { KnownWalletRpcMethods.forEach((method) => { - expect(isSupportedMethod('wallet', method)).toStrictEqual(true); + expect(isSupportedMethod('wallet', method)).toBe(true); }); }); @@ -51,46 +47,42 @@ describe('Scope Support', () => { 'returns true for each wallet:%s scoped method', (scopeString: string, methods: string[]) => { methods.forEach((method) => { - expect( - isSupportedMethod(`wallet:${scopeString}`, method), - ).toStrictEqual(true); + expect(isSupportedMethod(`wallet:${scopeString}`, method)).toBe(true); }); }, ); it('returns false otherwise', () => { - expect(isSupportedMethod('eip155', 'anything else')).toStrictEqual(false); - expect(isSupportedMethod('', '')).toStrictEqual(false); + expect(isSupportedMethod('eip155', 'anything else')).toBe(false); + expect(isSupportedMethod('', '')).toBe(false); }); }); describe('isSupportedScopeString', () => { it('returns true for the wallet namespace', () => { - expect(isSupportedScopeString('wallet', jest.fn())).toStrictEqual(true); + expect(isSupportedScopeString('wallet', jest.fn())).toBe(true); }); it('returns false for the wallet namespace when a reference is included', () => { - expect(isSupportedScopeString('wallet:someref', jest.fn())).toStrictEqual( - false, - ); + expect(isSupportedScopeString('wallet:someref', jest.fn())).toBe(false); }); it('returns true for the ethereum namespace', () => { - expect(isSupportedScopeString('eip155', jest.fn())).toStrictEqual(true); + expect(isSupportedScopeString('eip155', jest.fn())).toBe(true); }); it('returns true for the ethereum namespace when a network client exists for the reference', () => { const isChainIdSupportedMock = jest.fn().mockReturnValue(true); - expect( - isSupportedScopeString('eip155:1', isChainIdSupportedMock), - ).toStrictEqual(true); + expect(isSupportedScopeString('eip155:1', isChainIdSupportedMock)).toBe( + true, + ); }); it('returns false for the ethereum namespace when a network client does not exist for the reference', () => { const isChainIdSupportedMock = jest.fn().mockReturnValue(false); - expect( - isSupportedScopeString('eip155:1', isChainIdSupportedMock), - ).toStrictEqual(false); + expect(isSupportedScopeString('eip155:1', isChainIdSupportedMock)).toBe( + false, + ); }); }); }); diff --git a/packages/multichain/src/scope/supported.ts b/packages/multichain/src/scope/supported.ts index 9ca98be6e23..80ceb961f4d 100644 --- a/packages/multichain/src/scope/supported.ts +++ b/packages/multichain/src/scope/supported.ts @@ -1,29 +1,31 @@ +import { toHex } from '@metamask/controller-utils'; +import type { CaipAccountId, Hex } from '@metamask/utils'; import { - CaipAccountId, - Hex, isCaipChainId, isCaipNamespace, KnownCaipNamespace, parseCaipAccountId, parseCaipChainId, } from '@metamask/utils'; -import { toHex } from '@metamask/controller-utils'; -import { InternalAccount } from '@metamask/keyring-api'; + +import type { NonWalletKnownCaipNamespace, ExternalScopeString } from './scope'; import { KnownNotifications, KnownRpcMethods, KnownWalletNamespaceRpcMethods, KnownWalletRpcMethods, - NonWalletKnownCaipNamespace, parseScopeString, - ExternalScopeString, } from './scope'; // TODO Maybe this gets DRY'ed into utils?.. It's used in TokenDetectionController too -function isEqualCaseInsensitive( - value1: string, - value2: string, -): boolean { +/** + * Checks if two strings are equal, ignoring case. + * + * @param value1 - The first string to compare. + * @param value2 - The second string to compare. + * @returns `true` if the strings are equal, ignoring case; otherwise, `false`. + */ +function isEqualCaseInsensitive(value1: string, value2: string): boolean { if (typeof value1 !== 'string' || typeof value2 !== 'string') { return false; } @@ -68,7 +70,7 @@ export const isSupportedScopeString = ( export const isSupportedAccount = ( account: CaipAccountId, - getInternalAccounts: () => InternalAccount[], + getInternalAccounts: () => { type: string; address: string }[], ) => { const { address, diff --git a/packages/multichain/src/scope/transform.test.ts b/packages/multichain/src/scope/transform.test.ts index df0b529822f..d092735eb67 100644 --- a/packages/multichain/src/scope/transform.test.ts +++ b/packages/multichain/src/scope/transform.test.ts @@ -1,4 +1,4 @@ -import { ExternalScopeObject } from './scope'; +import type { ExternalScopeObject } from './scope'; import { flattenScope, mergeScopes, diff --git a/packages/multichain/src/scope/transform.ts b/packages/multichain/src/scope/transform.ts index a31faf2d34c..097ad725d89 100644 --- a/packages/multichain/src/scope/transform.ts +++ b/packages/multichain/src/scope/transform.ts @@ -1,16 +1,23 @@ -import { CaipReference } from '@metamask/utils'; +import type { CaipReference } from '@metamask/utils'; import { cloneDeep } from 'lodash'; -import { + +import type { ExternalScopeObject, ExternalScopesObject, ScopeString, ScopeObject, ScopesObject, - parseScopeString, } from './scope'; +import { parseScopeString } from './scope'; -// DRY THIS -function unique(list: T[]): T[] { +// TODO: DRY THIS +/** + * Returns a list of unique items + * + * @param list - The list of items to filter + * @returns A list of unique items + */ +function unique(list: Value[]): Value[] { return Array.from(new Set(list)); } @@ -32,7 +39,7 @@ export const flattenScope = ( const { namespace, reference } = parseScopeString(scopeString); // Scope is already a CAIP-2 ID and has no references to flatten - if (reference || !references) { + if (!namespace || reference || !references) { return { [scopeString]: scopeObject }; } diff --git a/packages/multichain/src/scope/validation.test.ts b/packages/multichain/src/scope/validation.test.ts index 507f24b328b..f4f4ae63e34 100644 --- a/packages/multichain/src/scope/validation.test.ts +++ b/packages/multichain/src/scope/validation.test.ts @@ -1,8 +1,5 @@ -import { ExternalScopeObject } from './scope'; -import { - isValidScope, - validateScopes, -} from './validation'; +import type { ExternalScopeObject } from './scope'; +import { isValidScope, validateScopes } from './validation'; const validScopeString = 'eip155:1'; const validScopeObject: ExternalScopeObject = { @@ -128,7 +125,9 @@ describe('Scope Validation', () => { scopeString: string, scopeObject: unknown, ) => { - expect(isValidScope(scopeString, scopeObject as ExternalScopeObject)).toStrictEqual(expected); + expect( + isValidScope(scopeString, scopeObject as ExternalScopeObject), + ).toStrictEqual(expected); }, ); }); @@ -140,16 +139,20 @@ describe('Scope Validation', () => { }; it('does not throw an error if required scopes are defined but none are valid', () => { - validateScopes( - { 'eip155:1': {} as unknown as ExternalScopeObject }, - undefined, - ); + expect( + validateScopes( + { 'eip155:1': {} as unknown as ExternalScopeObject }, + undefined, + ), + ).toStrictEqual({ validRequiredScopes: {}, validOptionalScopes: {} }); }); it('does not throw an error if optional scopes are defined but none are valid', () => { - validateScopes(undefined, { - 'eip155:1': {} as unknown as ExternalScopeObject, - }); + expect( + validateScopes(undefined, { + 'eip155:1': {} as unknown as ExternalScopeObject, + }), + ).toStrictEqual({ validRequiredScopes: {}, validOptionalScopes: {} }); }); it('returns the valid required and optional scopes', () => { diff --git a/packages/multichain/src/scope/validation.ts b/packages/multichain/src/scope/validation.ts index 8a5ab1a1cdf..69bc3e1bb9c 100644 --- a/packages/multichain/src/scope/validation.ts +++ b/packages/multichain/src/scope/validation.ts @@ -1,10 +1,11 @@ import { isCaipReference } from '@metamask/utils'; -import { + +import type { ExternalScopeString, - parseScopeString, ExternalScopeObject, ExternalScopesObject, } from './scope'; +import { parseScopeString } from './scope'; export const isValidScope = ( scopeString: ExternalScopeString, diff --git a/packages/multichain/tsconfig.build.json b/packages/multichain/tsconfig.build.json index 02a0eea03fe..f2108df2764 100644 --- a/packages/multichain/tsconfig.build.json +++ b/packages/multichain/tsconfig.build.json @@ -3,8 +3,16 @@ "compilerOptions": { "baseUrl": "./", "outDir": "./dist", - "rootDir": "./src" + "rootDir": "./src", + "resolveJsonModule": true }, - "references": [], + "references": [ + { + "path": "../network-controller/tsconfig.build.json" + }, + { + "path": "../permission-controller/tsconfig.build.json" + } + ], "include": ["../../types", "./src"] } diff --git a/yarn.lock b/yarn.lock index b43e7ce698f..88a64433d35 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3089,16 +3089,20 @@ __metadata: dependencies: "@metamask/api-specs": "npm:^0.10.12" "@metamask/auto-changelog": "npm:^3.4.4" + "@metamask/controller-utils": "npm:^11.3.0" "@metamask/eth-json-rpc-filters": "npm:^7.0.0" "@metamask/json-rpc-engine": "npm:^9.0.3" "@metamask/network-controller": "npm:^21.0.1" "@metamask/permission-controller": "npm:^11.0.2" "@metamask/rpc-errors": "npm:^6.3.1" + "@metamask/safe-event-emitter": "npm:^3.0.0" + "@metamask/utils": "npm:^9.1.0" "@open-rpc/meta-schema": "npm:^1.14.6" "@open-rpc/schema-utils-js": "npm:^2.0.5" "@types/jest": "npm:^27.4.1" deepmerge: "npm:^4.2.2" jest: "npm:^27.5.1" + jsonschema: "npm:^1.2.4" lodash: "npm:^4.17.21" ts-jest: "npm:^27.1.4" typedoc: "npm:^0.24.8"