From c92d2548ac652f7394c5a4c67187c70d482f4cf7 Mon Sep 17 00:00:00 2001 From: Maarten Zuidhoorn Date: Fri, 14 Apr 2023 13:07:29 +0200 Subject: [PATCH 1/4] Use `@metamask/json-rpc-engine`, `@metamask/rpc-errors`, and `@metamask/utils` in `permission-controller` In order to remove the dependency on `@metamask/types` inside `snaps-monorepo`, we need the `PermittedHandlerExport` type. This type depends on types from the `@metamask/json-rpc-engine` package, which were previously duplicated inside `@metamask/types`. In this commit, I have moved `PermittedHandlerExport` to this package, which required bumping `@metamask/json-rpc-engine`, `@metamask/rpc-errors`, and `@metamask/utils`. --- package.json | 6 +- packages/permission-controller/package.json | 5 +- packages/permission-controller/src/Caveat.ts | 3 +- .../permission-controller/src/Permission.ts | 8 +- .../src/PermissionController.test.ts | 65 ++++-- .../src/PermissionController.ts | 36 ++-- .../src/SubjectMetadataController.ts | 2 +- packages/permission-controller/src/errors.ts | 42 ++-- packages/permission-controller/src/index.ts | 1 + .../src/permission-middleware.ts | 12 +- .../src/permitted-handler.ts | 45 +++++ .../src/rpc-methods/getPermissions.test.ts | 2 +- .../src/rpc-methods/getPermissions.ts | 10 +- .../rpc-methods/requestPermissions.test.ts | 17 +- .../src/rpc-methods/requestPermissions.ts | 13 +- yarn.lock | 190 +++++++++++------- 16 files changed, 300 insertions(+), 157 deletions(-) create mode 100644 packages/permission-controller/src/permitted-handler.ts diff --git a/package.json b/package.json index b895889cec..629671fbc3 100644 --- a/package.json +++ b/package.json @@ -81,7 +81,11 @@ "web3-provider-engine>ethereumjs-util>keccak": false, "web3-provider-engine>ethereumjs-util>secp256k1": false, "web3-provider-engine>ethereumjs-util>ethereum-cryptography>keccak": false, - "web3-provider-engine>ethereumjs-util>ethereum-cryptography>secp256k1": false + "web3-provider-engine>ethereumjs-util>ethereum-cryptography>secp256k1": false, + "web3-provider-engine>eth-sig-util>ethereumjs-abi>ethereumjs-util>keccak": false, + "web3-provider-engine>eth-sig-util>ethereumjs-abi>ethereumjs-util>secp256k1": false, + "web3-provider-engine>ethereumjs-vm>ethereumjs-util>keccak": false, + "web3-provider-engine>ethereumjs-vm>ethereumjs-util>secp256k1": false } } } diff --git a/packages/permission-controller/package.json b/packages/permission-controller/package.json index d054126192..492e3eb930 100644 --- a/packages/permission-controller/package.json +++ b/packages/permission-controller/package.json @@ -32,13 +32,12 @@ "@metamask/approval-controller": "workspace:^", "@metamask/base-controller": "workspace:^", "@metamask/controller-utils": "workspace:^", - "@metamask/types": "^1.1.0", + "@metamask/json-rpc-engine": "^7.0.0", + "@metamask/rpc-errors": "^5.0.0", "@metamask/utils": "^5.0.2", "@types/deep-freeze-strict": "^1.1.0", "deep-freeze-strict": "^1.1.1", - "eth-rpc-errors": "^4.0.2", "immer": "^9.0.6", - "json-rpc-engine": "^6.1.0", "nanoid": "^3.1.31" }, "devDependencies": { diff --git a/packages/permission-controller/src/Caveat.ts b/packages/permission-controller/src/Caveat.ts index 4626986344..bd46d2e1d1 100644 --- a/packages/permission-controller/src/Caveat.ts +++ b/packages/permission-controller/src/Caveat.ts @@ -1,5 +1,4 @@ -import { Json } from '@metamask/types'; -import { hasProperty } from '@metamask/utils'; +import { Json, hasProperty } from '@metamask/utils'; import { CaveatSpecificationMismatchError, UnrecognizedCaveatTypeError, diff --git a/packages/permission-controller/src/Permission.ts b/packages/permission-controller/src/Permission.ts index 3d4751b770..9e00ded102 100644 --- a/packages/permission-controller/src/Permission.ts +++ b/packages/permission-controller/src/Permission.ts @@ -1,6 +1,5 @@ -import { Json } from '@metamask/types'; +import { Json, NonEmptyArray } from '@metamask/utils'; import { nanoid } from 'nanoid'; -import { NonEmptyArray } from '@metamask/controller-utils'; import { ActionConstraint, EventConstraint } from '@metamask/base-controller'; import type { SubjectType } from './SubjectMetadataController'; import { CaveatConstraint } from './Caveat'; @@ -259,7 +258,10 @@ type RestrictedMethodContext = Readonly<{ [key: string]: any; }>; -export type RestrictedMethodParameters = Json[] | Record | void; +export type RestrictedMethodParameters = + | Json[] + | Record + | undefined; /** * The arguments passed to a restricted method implementation. diff --git a/packages/permission-controller/src/PermissionController.test.ts b/packages/permission-controller/src/PermissionController.test.ts index c566fe20f9..a39200f59b 100644 --- a/packages/permission-controller/src/PermissionController.test.ts +++ b/packages/permission-controller/src/PermissionController.test.ts @@ -1,5 +1,5 @@ import assert from 'assert'; -import { JsonRpcEngine, PendingJsonRpcResponse } from 'json-rpc-engine'; +import { JsonRpcEngine } from '@metamask/json-rpc-engine'; import { AcceptRequest as AcceptApprovalRequest, AddApprovalRequest, @@ -7,9 +7,14 @@ import { RejectRequest as RejectApprovalRequest, } from '@metamask/approval-controller'; import { ControllerMessenger } from '@metamask/base-controller'; -import { hasProperty } from '@metamask/utils'; -import { Json, isPlainObject } from '@metamask/controller-utils'; +import { + hasProperty, + PendingJsonRpcResponse, + isPlainObject, + Json, +} from '@metamask/utils'; import { GetSubjectMetadata, SubjectType } from './SubjectMetadataController'; + import * as errors from './errors'; import { EndowmentGetterParams } from './Permission'; import { @@ -251,7 +256,7 @@ function getDefaultPermissionSpecifications() { CaveatTypes.filterArrayResponse, CaveatTypes.reverseArrayResponse, ], - methodImplementation: (_args: RestrictedMethodOptions) => { + methodImplementation: (_args: RestrictedMethodOptions) => { return ['a', 'b', 'c']; }, }, @@ -262,7 +267,7 @@ function getDefaultPermissionSpecifications() { CaveatTypes.filterObjectResponse, CaveatTypes.noopCaveat, ], - methodImplementation: (_args: RestrictedMethodOptions) => { + methodImplementation: (_args: RestrictedMethodOptions) => { return { a: 'x', b: 'y', c: 'z' }; }, validator: (permission: PermissionConstraint) => { @@ -279,7 +284,7 @@ function getDefaultPermissionSpecifications() { permissionType: PermissionType.RestrictedMethod, targetKey: PermissionKeys['wallet_getSecret_*'], allowedCaveats: [CaveatTypes.noopCaveat], - methodImplementation: (args: RestrictedMethodOptions) => { + methodImplementation: (args: RestrictedMethodOptions) => { return `Hello, secret friend "${args.method.replace( 'wallet_getSecret_', '', @@ -315,7 +320,7 @@ function getDefaultPermissionSpecifications() { permissionType: PermissionType.RestrictedMethod, targetKey: PermissionKeys.wallet_noop, allowedCaveats: null, - methodImplementation: (_args: RestrictedMethodOptions) => { + methodImplementation: (_args: RestrictedMethodOptions) => { return null; }, }, @@ -323,7 +328,7 @@ function getDefaultPermissionSpecifications() { permissionType: PermissionType.RestrictedMethod, targetKey: PermissionKeys.wallet_noopWithPermittedAndFailureSideEffects, allowedCaveats: null, - methodImplementation: (_args: RestrictedMethodOptions) => { + methodImplementation: (_args: RestrictedMethodOptions) => { return null; }, sideEffect: { @@ -335,7 +340,7 @@ function getDefaultPermissionSpecifications() { permissionType: PermissionType.RestrictedMethod, targetKey: PermissionKeys.wallet_noopWithPermittedAndFailureSideEffects2, allowedCaveats: null, - methodImplementation: (_args: RestrictedMethodOptions) => { + methodImplementation: (_args: RestrictedMethodOptions) => { return null; }, sideEffect: { @@ -347,7 +352,7 @@ function getDefaultPermissionSpecifications() { permissionType: PermissionType.RestrictedMethod, targetKey: PermissionKeys.wallet_noopWithPermittedSideEffects, allowedCaveats: null, - methodImplementation: (_args: RestrictedMethodOptions) => { + methodImplementation: (_args: RestrictedMethodOptions) => { return null; }, sideEffect: { @@ -358,7 +363,7 @@ function getDefaultPermissionSpecifications() { [PermissionKeys.wallet_noopWithValidator]: { permissionType: PermissionType.RestrictedMethod, targetKey: PermissionKeys.wallet_noopWithValidator, - methodImplementation: (_args: RestrictedMethodOptions) => { + methodImplementation: (_args: RestrictedMethodOptions) => { return null; }, allowedCaveats: [CaveatTypes.noopCaveat, CaveatTypes.filterArrayResponse], @@ -377,7 +382,7 @@ function getDefaultPermissionSpecifications() { [PermissionKeys.wallet_noopWithFactory]: { permissionType: PermissionType.RestrictedMethod, targetKey: PermissionKeys.wallet_noopWithFactory, - methodImplementation: (_args: RestrictedMethodOptions) => { + methodImplementation: (_args: RestrictedMethodOptions) => { return null; }, allowedCaveats: [CaveatTypes.filterArrayResponse], @@ -404,7 +409,7 @@ function getDefaultPermissionSpecifications() { permissionType: PermissionType.RestrictedMethod, targetKey: PermissionKeys.snap_foo, allowedCaveats: null, - methodImplementation: (_args: RestrictedMethodOptions) => { + methodImplementation: (_args: RestrictedMethodOptions) => { return null; }, subjectTypes: [SubjectType.Snap], @@ -3748,6 +3753,40 @@ describe('PermissionController', () => { ); }); + it('throws if requested permissions object is not JSON', async () => { + const options = getPermissionControllerOptions(); + const { messenger } = options; + const origin = 'metamask.io'; + const controller = getDefaultPermissionController(options); + + jest + .spyOn(messenger, 'call') + .mockImplementationOnce(async (...args: any) => { + const [, { requestData }] = args; + return { + metadata: { ...requestData.metadata }, + permissions: { ...requestData.permissions }, + }; + }); + + await expect( + async () => + await controller.requestPermissions( + { origin }, + { + [PermissionNames.wallet_getSecretArray]: { + // @ts-expect-error - Invalid JSON value. + foo: () => undefined, + }, + }, + ), + ).rejects.toThrow( + errors.invalidParams({ + message: `Approved permissions request for subject "metamask.io" is not a valid JSON value.`, + }), + ); + }); + it('throws if requested permissions object is not a plain object', async () => { const options = getPermissionControllerOptions(); const { messenger } = options; diff --git a/packages/permission-controller/src/PermissionController.ts b/packages/permission-controller/src/PermissionController.ts index 1297954fe3..7874910d9b 100644 --- a/packages/permission-controller/src/PermissionController.ts +++ b/packages/permission-controller/src/PermissionController.ts @@ -1,10 +1,9 @@ /* eslint-enable @typescript-eslint/no-unused-vars */ -import { Mutable } from '@metamask/types'; import deepFreeze from 'deep-freeze-strict'; import { castDraft, Draft, Patch } from 'immer'; import { nanoid } from 'nanoid'; -import { EthereumRpcError } from 'eth-rpc-errors'; -import { hasProperty } from '@metamask/utils'; +import { hasProperty, Mutable } from '@metamask/utils'; +import { JsonRpcError, serializeError } from '@metamask/rpc-errors'; import { AcceptRequest as AcceptApprovalRequest, AddApprovalRequest, @@ -26,6 +25,7 @@ import { NonEmptyArray, } from '@metamask/controller-utils'; import { GetSubjectMetadata } from './SubjectMetadataController'; + import { CaveatConstraint, CaveatSpecificationConstraint, @@ -1849,6 +1849,10 @@ export class PermissionController< origin: OriginString, target: string, ): void { + if (!isValidJson(caveat)) { + throw new CaveatInvalidJsonError(caveat, origin, target); + } + if (!isPlainObject(caveat)) { throw new InvalidCaveatError(caveat, origin, target); } @@ -1870,10 +1874,6 @@ export class PermissionController< throw new CaveatMissingValueError(caveat, origin, target); } - if (!isValidJson(caveat.value)) { - throw new CaveatInvalidJsonError(caveat, origin, target); - } - // Typecast: TypeScript still believes that the caveat is a PlainObject. specification.validator?.(caveat as CaveatConstraint, origin, target); } @@ -1987,7 +1987,7 @@ export class PermissionController< */ private validateRequestedPermissions( origin: OriginString, - requestedPermissions: unknown, + requestedPermissions: Json, ): void { if (!isPlainObject(requestedPermissions)) { throw invalidParams({ @@ -2127,7 +2127,9 @@ export class PermissionController< failureHandlersList.map((failureHandler) => failureHandler(params)), ); } catch (error) { - throw internalError('Unexpected error in side-effects', { error }); + throw internalError('Unexpected error in side-effects', { + error: serializeError(error), + }); } } const reasons = rejectedHandlers.map((handler) => handler.reason); @@ -2139,7 +2141,7 @@ export class PermissionController< throw reasons.length > 1 ? internalError( 'Multiple errors occurred during side-effects execution', - { errors: reasons }, + { errors: reasons.map((error) => serializeError(error)) }, ) : reasons[0]; } @@ -2169,6 +2171,12 @@ export class PermissionController< ) { const { id, origin } = originalMetadata; + if (!isValidJson(approvedRequest)) { + throw internalError( + `Approved permissions request for subject "${origin}" is not a valid JSON value.`, + ); + } + if ( !isPlainObject(approvedRequest) || !isPlainObject(approvedRequest.metadata) @@ -2201,7 +2209,7 @@ export class PermissionController< try { this.validateRequestedPermissions(origin, permissions); } catch (error) { - if (error instanceof EthereumRpcError) { + if (error instanceof JsonRpcError) { // Re-throw as an internal error; we should never receive invalid approved // permissions. throw internalError( @@ -2209,7 +2217,11 @@ export class PermissionController< error.data, ); } - throw internalError('Unrecognized error type', { error }); + + /* istanbul ignore next: should never happen */ + throw internalError('Unrecognized error type', { + error: serializeError(error), + }); } } diff --git a/packages/permission-controller/src/SubjectMetadataController.ts b/packages/permission-controller/src/SubjectMetadataController.ts index 8a6c909bdc..d4083854b9 100644 --- a/packages/permission-controller/src/SubjectMetadataController.ts +++ b/packages/permission-controller/src/SubjectMetadataController.ts @@ -1,5 +1,5 @@ import type { Patch } from 'immer'; -import { Json } from '@metamask/types'; +import { Json } from '@metamask/utils'; import { BaseControllerV2, RestrictedControllerMessenger, diff --git a/packages/permission-controller/src/errors.ts b/packages/permission-controller/src/errors.ts index 9da9c67682..6f17d34b1f 100644 --- a/packages/permission-controller/src/errors.ts +++ b/packages/permission-controller/src/errors.ts @@ -1,8 +1,14 @@ -import { errorCodes, ethErrors, EthereumRpcError } from 'eth-rpc-errors'; +import { + errorCodes, + rpcErrors, + providerErrors, + JsonRpcError, +} from '@metamask/rpc-errors'; +import { Json } from '@metamask/utils'; import { PermissionType } from './Permission'; type UnauthorizedArg = { - data?: Record; + data?: Record; }; /** @@ -12,7 +18,7 @@ type UnauthorizedArg = { * @returns The built error */ export function unauthorized(opts: UnauthorizedArg) { - return ethErrors.provider.unauthorized({ + return providerErrors.unauthorized({ message: 'Unauthorized to perform action. Try requesting the required permission(s) first. For more information, see: https://docs.metamask.io/guide/rpc-api.html#permissions', data: opts.data, @@ -26,19 +32,19 @@ export function unauthorized(opts: UnauthorizedArg) { * @param data - Optional data for context. * @returns The built error */ -export function methodNotFound(method: string, data?: unknown) { +export function methodNotFound(method: string, data?: Json) { const message = `The method "${method}" does not exist / is not available.`; - const opts: Parameters[0] = { message }; + const opts: Parameters[0] = { message }; if (data !== undefined) { opts.data = data; } - return ethErrors.rpc.methodNotFound(opts); + return rpcErrors.methodNotFound(opts); } type InvalidParamsArg = { message?: string; - data?: unknown; + data?: Json; }; /** @@ -48,7 +54,7 @@ type InvalidParamsArg = { * @returns The built error */ export function invalidParams(opts: InvalidParamsArg) { - return ethErrors.rpc.invalidParams({ + return rpcErrors.invalidParams({ data: opts.data, message: opts.message, }); @@ -60,10 +66,10 @@ export function invalidParams(opts: InvalidParamsArg) { * @param data - Optional data to add extra context * @returns The built error */ -export function userRejectedRequest>( +export function userRejectedRequest>( data?: Data, -): EthereumRpcError { - return ethErrors.provider.userRejectedRequest({ data }); +): JsonRpcError { + return providerErrors.userRejectedRequest({ data }); } /** @@ -73,11 +79,11 @@ export function userRejectedRequest>( * @param data - Optional data to add extra context * @returns The built error */ -export function internalError>( +export function internalError>( message: string, data?: Data, -): EthereumRpcError { - return ethErrors.rpc.internal({ message, data }); +): JsonRpcError { + return rpcErrors.internal({ message, data }); } export class InvalidSubjectIdentifierError extends Error { @@ -182,10 +188,10 @@ export class CaveatAlreadyExistsError extends Error { } } -export class InvalidCaveatError extends EthereumRpcError { +export class InvalidCaveatError extends JsonRpcError { public override data: { origin: string; target: string }; - constructor(receivedCaveat: unknown, origin: string, target: string) { + constructor(receivedCaveat: Json, origin: string, target: string) { super( errorCodes.rpc.invalidParams, `Invalid caveat. Caveats must be plain objects.`, @@ -223,12 +229,12 @@ export class CaveatMissingValueError extends Error { export class CaveatInvalidJsonError extends Error { public data: { - caveat: Record; + caveat: unknown; origin: string; target: string; }; - constructor(caveat: Record, origin: string, target: string) { + constructor(caveat: unknown, origin: string, target: string) { super(`Caveat "value" is invalid JSON.`); this.data = { caveat, origin, target }; } diff --git a/packages/permission-controller/src/index.ts b/packages/permission-controller/src/index.ts index 0d3f11a480..aac5bf1a09 100644 --- a/packages/permission-controller/src/index.ts +++ b/packages/permission-controller/src/index.ts @@ -2,6 +2,7 @@ export * from './Caveat'; export * from './errors'; export * from './Permission'; export * from './PermissionController'; +export * from './permitted-handler'; export * from './utils'; export * as permissionRpcMethods from './rpc-methods'; export * from './SubjectMetadataController'; diff --git a/packages/permission-controller/src/permission-middleware.ts b/packages/permission-controller/src/permission-middleware.ts index 9f55e4f996..9acc2c9765 100644 --- a/packages/permission-controller/src/permission-middleware.ts +++ b/packages/permission-controller/src/permission-middleware.ts @@ -1,13 +1,15 @@ -import type { Json } from '@metamask/types'; +import type { + Json, + JsonRpcRequest, + PendingJsonRpcResponse, +} from '@metamask/utils'; import { JsonRpcMiddleware, AsyncJsonRpcEngineNextCallback, createAsyncMiddleware, - PendingJsonRpcResponse, - JsonRpcRequest, -} from 'json-rpc-engine'; +} from '@metamask/json-rpc-engine'; // eslint-disable-next-line @typescript-eslint/no-unused-vars -import type { JsonRpcEngine } from 'json-rpc-engine'; +import type { JsonRpcEngine } from '@metamask/json-rpc-engine'; import { internalError } from './errors'; // eslint-disable-next-line @typescript-eslint/no-unused-vars import type { PermissionController } from './PermissionController'; diff --git a/packages/permission-controller/src/permitted-handler.ts b/packages/permission-controller/src/permitted-handler.ts new file mode 100644 index 0000000000..0a5c3c9eb3 --- /dev/null +++ b/packages/permission-controller/src/permitted-handler.ts @@ -0,0 +1,45 @@ +import { + Json, + JsonRpcParams, + JsonRpcRequest, + PendingJsonRpcResponse, +} from '@metamask/utils'; +import { + JsonRpcEngineEndCallback, + JsonRpcEngineNextCallback, +} from '@metamask/json-rpc-engine'; + +export type HandlerMiddlewareFunction< + Hooks, + Params extends JsonRpcParams, + Response extends Json, +> = ( + req: JsonRpcRequest, + res: PendingJsonRpcResponse, + next: JsonRpcEngineNextCallback, + end: JsonRpcEngineEndCallback, + hooks: Hooks, +) => void | Promise; + +type BaseHandlerExport = { + methodNames: string[]; +}; + +/** + * We use a mapped object type in order to create a type that requires the + * presence of the names of all hooks for the given handler. + * This can then be used to select only the necessary hooks whenever a method + * is called for purposes of POLA. + */ +export type HookNames = { + [Property in keyof Type]: true; +}; + +export type PermittedHandlerExport< + Hooks, + Params extends JsonRpcParams, + Result extends Json, +> = { + implementation: HandlerMiddlewareFunction; + hookNames: HookNames; +} & BaseHandlerExport; diff --git a/packages/permission-controller/src/rpc-methods/getPermissions.test.ts b/packages/permission-controller/src/rpc-methods/getPermissions.test.ts index 5bfac06baa..7b0b129fe6 100644 --- a/packages/permission-controller/src/rpc-methods/getPermissions.test.ts +++ b/packages/permission-controller/src/rpc-methods/getPermissions.test.ts @@ -1,4 +1,4 @@ -import { JsonRpcEngine } from 'json-rpc-engine'; +import { JsonRpcEngine } from '@metamask/json-rpc-engine'; import { getPermissionsHandler } from './getPermissions'; describe('getPermissions RPC method', () => { diff --git a/packages/permission-controller/src/rpc-methods/getPermissions.ts b/packages/permission-controller/src/rpc-methods/getPermissions.ts index d234c67bc6..78e651bcf6 100644 --- a/packages/permission-controller/src/rpc-methods/getPermissions.ts +++ b/packages/permission-controller/src/rpc-methods/getPermissions.ts @@ -1,8 +1,6 @@ -import type { - JsonRpcEngineEndCallback, - PendingJsonRpcResponse, - PermittedHandlerExport, -} from '@metamask/types'; +import { PermittedHandlerExport } from '@metamask/permission-controller'; +import { PendingJsonRpcResponse } from '@metamask/utils'; +import { JsonRpcEngineEndCallback } from '@metamask/json-rpc-engine'; import { MethodNames } from '../utils'; import type { PermissionConstraint } from '../Permission'; @@ -10,7 +8,7 @@ import type { SubjectPermissions } from '../PermissionController'; export const getPermissionsHandler: PermittedHandlerExport< GetPermissionsHooks, - void, + undefined, PermissionConstraint[] > = { methodNames: [MethodNames.getPermissions], diff --git a/packages/permission-controller/src/rpc-methods/requestPermissions.test.ts b/packages/permission-controller/src/rpc-methods/requestPermissions.test.ts index e1eff902cf..e6161f9ac6 100644 --- a/packages/permission-controller/src/rpc-methods/requestPermissions.test.ts +++ b/packages/permission-controller/src/rpc-methods/requestPermissions.test.ts @@ -1,5 +1,8 @@ -import { JsonRpcEngine, createAsyncMiddleware } from 'json-rpc-engine'; -import { ethErrors, serializeError } from 'eth-rpc-errors'; +import { + JsonRpcEngine, + createAsyncMiddleware, +} from '@metamask/json-rpc-engine'; +import { rpcErrors, serializeError } from '@metamask/rpc-errors'; import { requestPermissionsHandler } from './requestPermissions'; describe('requestPermissions RPC method', () => { @@ -38,10 +41,12 @@ describe('requestPermissions RPC method', () => { it('returns an error if requestPermissionsForOrigin rejects', async () => { const { implementation } = requestPermissionsHandler; + + const error = new Error('foo'); const mockRequestPermissionsForOrigin = jest .fn() .mockImplementationOnce(async () => { - throw new Error('foo'); + throw error; }); const engine = new JsonRpcEngine(); @@ -66,7 +71,7 @@ describe('requestPermissions RPC method', () => { }); expect(response.result).toBeUndefined(); - expect(response.error).toStrictEqual(serializeError(new Error('foo'))); + expect(response.error).toStrictEqual(serializeError(error)); expect(mockRequestPermissionsForOrigin).toHaveBeenCalledTimes(1); expect(mockRequestPermissionsForOrigin).toHaveBeenCalledWith({}, '1'); }); @@ -90,7 +95,7 @@ describe('requestPermissions RPC method', () => { params: [], // doesn't matter }; - const expectedError = ethErrors.rpc + const expectedError = rpcErrors .invalidRequest({ message: 'Invalid request: Must specify a valid id.', data: { request: { ...req } }, @@ -124,7 +129,7 @@ describe('requestPermissions RPC method', () => { params: invalidParams, }; - const expectedError = ethErrors.rpc + const expectedError = rpcErrors .invalidParams({ data: { request: { ...req } }, }) diff --git a/packages/permission-controller/src/rpc-methods/requestPermissions.ts b/packages/permission-controller/src/rpc-methods/requestPermissions.ts index 5509d1ebfb..c38abd3d64 100644 --- a/packages/permission-controller/src/rpc-methods/requestPermissions.ts +++ b/packages/permission-controller/src/rpc-methods/requestPermissions.ts @@ -1,11 +1,8 @@ -import { ethErrors } from 'eth-rpc-errors'; -import type { - JsonRpcEngineEndCallback, - JsonRpcRequest, - PendingJsonRpcResponse, - PermittedHandlerExport, -} from '@metamask/types'; +import { rpcErrors } from '@metamask/rpc-errors'; import { isPlainObject } from '@metamask/controller-utils'; +import { JsonRpcRequest, PendingJsonRpcResponse } from '@metamask/utils'; +import { JsonRpcEngineEndCallback } from '@metamask/json-rpc-engine'; +import { PermittedHandlerExport } from '@metamask/permission-controller'; import { MethodNames } from '../utils'; import { invalidParams } from '../errors'; import type { PermissionConstraint, RequestedPermissions } from '../Permission'; @@ -58,7 +55,7 @@ async function requestPermissionsImplementation( (typeof id === 'string' && !id) ) { return end( - ethErrors.rpc.invalidRequest({ + rpcErrors.invalidRequest({ message: 'Invalid request: Must specify a valid id.', data: { request: req }, }), diff --git a/yarn.lock b/yarn.lock index cada8fdd24..9a57b6e8c8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -415,13 +415,6 @@ __metadata: languageName: node linkType: hard -"@chainsafe/as-sha256@npm:^0.3.1": - version: 0.3.1 - resolution: "@chainsafe/as-sha256@npm:0.3.1" - checksum: 58ea733be1657b0e31dbf48b0dba862da0833df34a81c1460c7352f04ce90874f70003cbf34d0afb9e5e53a33ee2d63a261a8b12462be85b2ba0a6f7f13d6150 - languageName: node - linkType: hard - "@chainsafe/as-sha256@npm:^0.4.1": version: 0.4.1 resolution: "@chainsafe/as-sha256@npm:0.4.1" @@ -429,15 +422,6 @@ __metadata: languageName: node linkType: hard -"@chainsafe/persistent-merkle-tree@npm:^0.4.2": - version: 0.4.2 - resolution: "@chainsafe/persistent-merkle-tree@npm:0.4.2" - dependencies: - "@chainsafe/as-sha256": ^0.3.1 - checksum: f9cfcb2132a243992709715dbd28186ab48c7c0c696f29d30857693cca5526bf753974a505ef68ffd5623bbdbcaa10f9083f4dd40bf99eb6408e451cc26a1a9e - 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" @@ -448,17 +432,6 @@ __metadata: languageName: node linkType: hard -"@chainsafe/ssz@npm:0.9.4": - version: 0.9.4 - resolution: "@chainsafe/ssz@npm:0.9.4" - dependencies: - "@chainsafe/as-sha256": ^0.3.1 - "@chainsafe/persistent-merkle-tree": ^0.4.2 - case: ^1.6.3 - checksum: c6eaedeae9e5618b3c666ff4507a27647f665a8dcf17d5ca86da4ed4788c5a93868f256d0005467d184fdf35ec03f323517ec2e55ec42492d769540a2ec396bc - languageName: node - linkType: hard - "@chainsafe/ssz@npm:^0.11.1": version: 0.11.1 resolution: "@chainsafe/ssz@npm:0.11.1" @@ -573,18 +546,7 @@ __metadata: languageName: node linkType: hard -"@ethereumjs/util@npm:^8.0.0, @ethereumjs/util@npm:^8.0.2": - version: 8.0.5 - resolution: "@ethereumjs/util@npm:8.0.5" - dependencies: - "@chainsafe/ssz": 0.9.4 - "@ethereumjs/rlp": ^4.0.1 - ethereum-cryptography: ^1.1.2 - checksum: 318386785295b4584289b1aa576d2621392b3a918d127890db62d3f74184f3377694dd9e951e19bfb9ab80e8dc9e38e180236cac2651dead26097d10963731f9 - languageName: node - linkType: hard - -"@ethereumjs/util@npm:^8.0.6": +"@ethereumjs/util@npm:^8.0.0, @ethereumjs/util@npm:^8.0.2, @ethereumjs/util@npm:^8.0.6": version: 8.0.6 resolution: "@ethereumjs/util@npm:8.0.6" dependencies: @@ -1621,7 +1583,7 @@ __metadata: languageName: node linkType: hard -"@metamask/eth-sig-util@npm:5.0.2, @metamask/eth-sig-util@npm:^5.0.1, @metamask/eth-sig-util@npm:^5.0.2": +"@metamask/eth-sig-util@npm:5.0.2": version: 5.0.2 resolution: "@metamask/eth-sig-util@npm:5.0.2" dependencies: @@ -1635,7 +1597,7 @@ __metadata: languageName: node linkType: hard -"@metamask/eth-sig-util@npm:^5.0.3": +"@metamask/eth-sig-util@npm:^5.0.1, @metamask/eth-sig-util@npm:^5.0.2, @metamask/eth-sig-util@npm:^5.0.3": version: 5.0.3 resolution: "@metamask/eth-sig-util@npm:5.0.3" dependencies: @@ -1692,6 +1654,17 @@ __metadata: languageName: unknown linkType: soft +"@metamask/json-rpc-engine@npm:^7.0.0": + version: 7.0.0 + resolution: "@metamask/json-rpc-engine@npm:7.0.0" + dependencies: + "@metamask/rpc-errors": ^5.0.0 + "@metamask/safe-event-emitter": ^2.0.0 + "@metamask/utils": ^5.0.1 + checksum: d22347ee4597bc72cdc34e65a27872ed8e77de188c5b95fed9133c25289cd4abd7aafa72e3e326d8a7449a857c275ed6435642727c30c51319f5d97a579c5f49 + languageName: node + linkType: hard + "@metamask/keyring-controller@workspace:packages/keyring-controller": version: 0.0.0-use.local resolution: "@metamask/keyring-controller@workspace:packages/keyring-controller" @@ -1820,16 +1793,15 @@ __metadata: "@metamask/auto-changelog": ^3.1.0 "@metamask/base-controller": "workspace:^" "@metamask/controller-utils": "workspace:^" - "@metamask/types": ^1.1.0 + "@metamask/json-rpc-engine": ^7.0.0 + "@metamask/rpc-errors": ^5.0.0 "@metamask/utils": ^5.0.2 "@types/deep-freeze-strict": ^1.1.0 "@types/jest": ^27.4.1 deep-freeze-strict: ^1.1.1 deepmerge: ^4.2.2 - eth-rpc-errors: ^4.0.2 immer: ^9.0.6 jest: ^27.5.1 - json-rpc-engine: ^6.1.0 nanoid: ^3.1.31 ts-jest: ^27.1.4 typedoc: ^0.22.15 @@ -1897,6 +1869,16 @@ __metadata: languageName: unknown linkType: soft +"@metamask/rpc-errors@npm:^5.0.0": + version: 5.0.0 + resolution: "@metamask/rpc-errors@npm:5.0.0" + dependencies: + "@metamask/utils": ^5.0.0 + fast-safe-stringify: ^2.0.6 + checksum: fbcb21ad1460883ee9d28f487d9cf5de7d3c6ac819024a1b6e5732d5da9a9f275f19fa0b4daa06dbd9b45d89c9e50ab9f281d597324e36d3324fbc9ebfdf0ba8 + 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" @@ -1952,13 +1934,6 @@ __metadata: languageName: unknown linkType: soft -"@metamask/types@npm:^1.1.0": - version: 1.1.0 - resolution: "@metamask/types@npm:1.1.0" - checksum: 500e8c076a2b0a6d688c8c465101256f090547d99c9a5585efe3d1db7a494b6b2712b127043fb316912caffc4fff78976b9a7780780abb68305e8a3bf812689c - languageName: node - linkType: hard - "@metamask/utils@npm:^3.0.3, @metamask/utils@npm:^3.2.0": version: 3.3.1 resolution: "@metamask/utils@npm:3.3.1" @@ -1970,7 +1945,7 @@ __metadata: languageName: node linkType: hard -"@metamask/utils@npm:^5.0.2": +"@metamask/utils@npm:^5.0.0, @metamask/utils@npm:^5.0.1, @metamask/utils@npm:^5.0.2": version: 5.0.2 resolution: "@metamask/utils@npm:5.0.2" dependencies: @@ -2290,15 +2265,6 @@ __metadata: languageName: node linkType: hard -"@types/bn.js@npm:^4.11.3": - version: 4.11.6 - resolution: "@types/bn.js@npm:4.11.6" - dependencies: - "@types/node": "*" - checksum: 7f66f2c7b7b9303b3205a57184261974b114495736b77853af5b18d857c0b33e82ce7146911e86e87a87837de8acae28986716fd381ac7c301fd6e8d8b6c811f - languageName: node - linkType: hard - "@types/bn.js@npm:^5.1.0": version: 5.1.1 resolution: "@types/bn.js@npm:5.1.1" @@ -3192,6 +3158,24 @@ __metadata: languageName: node linkType: hard +"bindings@npm:^1.2.1, bindings@npm:^1.5.0": + version: 1.5.0 + resolution: "bindings@npm:1.5.0" + dependencies: + file-uri-to-path: 1.0.0 + checksum: 65b6b48095717c2e6105a021a7da4ea435aa8d3d3cd085cb9e85bcb6e5773cf318c4745c3f7c504412855940b585bdf9b918236612a1c7a7942491de176f1ae7 + languageName: node + linkType: hard + +"bip66@npm:^1.1.5": + version: 1.1.5 + resolution: "bip66@npm:1.1.5" + dependencies: + safe-buffer: ^5.0.1 + checksum: 956cff6e51d7206571ef8ce875bc5fa61b5c181589790b9155799b7edcae4b20dbb3eed43b188ff3eec27cdbe98e0b7e0ec9f1cb2e4f5370c119028b248ad859 + languageName: node + linkType: hard + "blakejs@npm:^1.1.0": version: 1.1.0 resolution: "blakejs@npm:1.1.0" @@ -3262,7 +3246,7 @@ __metadata: languageName: node linkType: hard -"browserify-aes@npm:^1.2.0": +"browserify-aes@npm:^1.0.6, browserify-aes@npm:^1.2.0": version: 1.2.0 resolution: "browserify-aes@npm:1.2.0" dependencies: @@ -3434,13 +3418,6 @@ __metadata: languageName: node linkType: hard -"case@npm:^1.6.3": - version: 1.6.3 - resolution: "case@npm:1.6.3" - checksum: febe73278f910b0d28aab7efd6f51c235f9aa9e296148edb56dfb83fd58faa88308c30ce9a0122b6e53e0362c44f4407105bd5ef89c46860fc2b184e540fd68d - languageName: node - linkType: hard - "caseless@npm:~0.12.0": version: 0.12.0 resolution: "caseless@npm:0.12.0" @@ -3962,6 +3939,17 @@ __metadata: languageName: node linkType: hard +"drbg.js@npm:^1.0.1": + version: 1.0.1 + resolution: "drbg.js@npm:1.0.1" + dependencies: + browserify-aes: ^1.0.6 + create-hash: ^1.1.2 + create-hmac: ^1.1.4 + checksum: f8df5cdd4fb792e548d6187cbc446fbd0afd8f1ef7fa486e1c286c2adee55a687183ce48ab178e9f24965c2deabb6e2ba7a7ee2d675264b951356480eb042476 + languageName: node + linkType: hard + "ecc-jsbn@npm:~0.1.1": version: 0.1.2 resolution: "ecc-jsbn@npm:0.1.2" @@ -3979,7 +3967,7 @@ __metadata: languageName: node linkType: hard -"elliptic@npm:6.5.4, elliptic@npm:^6.4.0, elliptic@npm:^6.5.2, elliptic@npm:^6.5.4": +"elliptic@npm:6.5.4, elliptic@npm:^6.4.0, elliptic@npm:^6.4.1, elliptic@npm:^6.5.2, elliptic@npm:^6.5.4": version: 6.5.4 resolution: "elliptic@npm:6.5.4" dependencies: @@ -4757,17 +4745,17 @@ __metadata: linkType: hard "ethereumjs-util@npm:^6.0.0": - version: 6.2.1 - resolution: "ethereumjs-util@npm:6.2.1" + version: 6.1.0 + resolution: "ethereumjs-util@npm:6.1.0" dependencies: - "@types/bn.js": ^4.11.3 bn.js: ^4.11.0 create-hash: ^1.1.2 - elliptic: ^6.5.2 - ethereum-cryptography: ^0.1.3 ethjs-util: 0.1.6 - rlp: ^2.2.3 - checksum: e3cb4a2c034a2529281fdfc21a2126fe032fdc3038863f5720352daa65ddcc50fc8c67dbedf381a882dc3802e05d979287126d7ecf781504bde1fd8218693bde + keccak: ^1.0.2 + rlp: ^2.0.0 + safe-buffer: ^5.1.1 + secp256k1: ^3.0.1 + checksum: 76c87c2be9e380608e5bed21979483ad4d09c0aa9f9e3c9c913fbeff5610581631b661d6411c390556d8d47e56d7039861ae9c2821a54493cfab7fc88756315c languageName: node linkType: hard @@ -5136,6 +5124,13 @@ __metadata: languageName: node linkType: hard +"file-uri-to-path@npm:1.0.0": + version: 1.0.0 + resolution: "file-uri-to-path@npm:1.0.0" + checksum: b648580bdd893a008c92c7ecc96c3ee57a5e7b6c4c18a9a09b44fb5d36d79146f8e442578bc0e173dc027adf3987e254ba1dfd6e3ec998b7c282873010502144 + languageName: node + linkType: hard + "fill-range@npm:^7.0.1": version: 7.0.1 resolution: "fill-range@npm:7.0.1" @@ -6892,6 +6887,19 @@ __metadata: languageName: node linkType: hard +"keccak@npm:^1.0.2": + version: 1.4.0 + resolution: "keccak@npm:1.4.0" + dependencies: + bindings: ^1.2.1 + inherits: ^2.0.3 + nan: ^2.2.1 + node-gyp: latest + safe-buffer: ^5.1.0 + checksum: 236ba4183d64e1118566c4f123d812cc8fa5fb0fa477b6743bc398aced42595816f46a322bf0240a6a7589eff932aa1540066a30db2367e4049436d9fa30f537 + languageName: node + linkType: hard + "keccak@npm:^3.0.0": version: 3.0.1 resolution: "keccak@npm:3.0.1" @@ -7410,6 +7418,15 @@ __metadata: languageName: node linkType: hard +"nan@npm:^2.14.0, nan@npm:^2.2.1": + version: 2.17.0 + resolution: "nan@npm:2.17.0" + dependencies: + node-gyp: latest + checksum: ec609aeaf7e68b76592a3ba96b372aa7f5df5b056c1e37410b0f1deefbab5a57a922061e2c5b369bae9c7c6b5e6eecf4ad2dac8833a1a7d3a751e0a7c7f849ed + languageName: node + linkType: hard + "nanoid@npm:^3.1.31": version: 3.1.31 resolution: "nanoid@npm:3.1.31" @@ -8428,7 +8445,7 @@ __metadata: languageName: node linkType: hard -"rlp@npm:^2.0.0, rlp@npm:^2.2.3, rlp@npm:^2.2.4, rlp@npm:^2.2.6": +"rlp@npm:^2.0.0, rlp@npm:^2.2.4, rlp@npm:^2.2.6": version: 2.2.7 resolution: "rlp@npm:2.2.7" dependencies: @@ -8508,6 +8525,23 @@ __metadata: languageName: node linkType: hard +"secp256k1@npm:^3.0.1": + version: 3.7.1 + resolution: "secp256k1@npm:3.7.1" + dependencies: + bindings: ^1.5.0 + bip66: ^1.1.5 + bn.js: ^4.11.8 + create-hash: ^1.2.0 + drbg.js: ^1.0.1 + elliptic: ^6.4.1 + nan: ^2.14.0 + node-gyp: latest + safe-buffer: ^5.1.2 + checksum: fd3b17157c598296602bbb9d75a42d57a17d38f95035e58b08d3d996e232423a1363449cc4d372cb4219d9df0baefdae8711fa05034e054cf0eb523f2f4f5d05 + languageName: node + linkType: hard + "secp256k1@npm:^4.0.0, secp256k1@npm:^4.0.1": version: 4.0.3 resolution: "secp256k1@npm:4.0.3" From ecd9c6abf065659fcf9a99c2c0df215f0cd4adeb Mon Sep 17 00:00:00 2001 From: Maarten Zuidhoorn Date: Wed, 19 Apr 2023 13:59:32 +0200 Subject: [PATCH 2/4] Update "@metamask/rpc-errors" to version "^5.1.1" This commit updates "@metamask/rpc-errors" package to version "^5.1.1" in package.json and yarn.lock files. This will resolve an issue related to an internal error in PermissionController caused by this dependency. --- packages/permission-controller/package.json | 3 +-- .../permission-controller/src/PermissionController.ts | 4 ++-- packages/permission-controller/src/errors.ts | 3 ++- yarn.lock | 11 +++++------ 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/packages/permission-controller/package.json b/packages/permission-controller/package.json index 492e3eb930..448309c3b6 100644 --- a/packages/permission-controller/package.json +++ b/packages/permission-controller/package.json @@ -33,8 +33,7 @@ "@metamask/base-controller": "workspace:^", "@metamask/controller-utils": "workspace:^", "@metamask/json-rpc-engine": "^7.0.0", - "@metamask/rpc-errors": "^5.0.0", - "@metamask/utils": "^5.0.2", + "@metamask/rpc-errors": "^5.1.1", "@types/deep-freeze-strict": "^1.1.0", "deep-freeze-strict": "^1.1.1", "immer": "^9.0.6", diff --git a/packages/permission-controller/src/PermissionController.ts b/packages/permission-controller/src/PermissionController.ts index 7874910d9b..a7972be288 100644 --- a/packages/permission-controller/src/PermissionController.ts +++ b/packages/permission-controller/src/PermissionController.ts @@ -2128,7 +2128,7 @@ export class PermissionController< ); } catch (error) { throw internalError('Unexpected error in side-effects', { - error: serializeError(error), + cause: error, }); } } @@ -2220,7 +2220,7 @@ export class PermissionController< /* istanbul ignore next: should never happen */ throw internalError('Unrecognized error type', { - error: serializeError(error), + cause: error, }); } } diff --git a/packages/permission-controller/src/errors.ts b/packages/permission-controller/src/errors.ts index 6f17d34b1f..88d4e76ab4 100644 --- a/packages/permission-controller/src/errors.ts +++ b/packages/permission-controller/src/errors.ts @@ -3,6 +3,7 @@ import { rpcErrors, providerErrors, JsonRpcError, + DataWithOptionalCause, } from '@metamask/rpc-errors'; import { Json } from '@metamask/utils'; import { PermissionType } from './Permission'; @@ -79,7 +80,7 @@ export function userRejectedRequest>( * @param data - Optional data to add extra context * @returns The built error */ -export function internalError>( +export function internalError( message: string, data?: Data, ): JsonRpcError { diff --git a/yarn.lock b/yarn.lock index 9a57b6e8c8..a4f31dd2cf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1794,8 +1794,7 @@ __metadata: "@metamask/base-controller": "workspace:^" "@metamask/controller-utils": "workspace:^" "@metamask/json-rpc-engine": ^7.0.0 - "@metamask/rpc-errors": ^5.0.0 - "@metamask/utils": ^5.0.2 + "@metamask/rpc-errors": ^5.1.1 "@types/deep-freeze-strict": ^1.1.0 "@types/jest": ^27.4.1 deep-freeze-strict: ^1.1.1 @@ -1869,13 +1868,13 @@ __metadata: languageName: unknown linkType: soft -"@metamask/rpc-errors@npm:^5.0.0": - version: 5.0.0 - resolution: "@metamask/rpc-errors@npm:5.0.0" +"@metamask/rpc-errors@npm:^5.0.0, @metamask/rpc-errors@npm:^5.1.1": + version: 5.1.1 + resolution: "@metamask/rpc-errors@npm:5.1.1" dependencies: "@metamask/utils": ^5.0.0 fast-safe-stringify: ^2.0.6 - checksum: fbcb21ad1460883ee9d28f487d9cf5de7d3c6ac819024a1b6e5732d5da9a9f275f19fa0b4daa06dbd9b45d89c9e50ab9f281d597324e36d3324fbc9ebfdf0ba8 + checksum: ccd1b24da66af3ae63960b79c04b86efb8b96acb89ca6f7e0bbfe636d23ba5cddeba533c0692eafb87c44ec6f840085372d0f21b39e05df9a80700ff61538a30 languageName: node linkType: hard From 327803b02933b93e42a417172f730fb1f3311cf0 Mon Sep 17 00:00:00 2001 From: Maarten Zuidhoorn Date: Wed, 19 Apr 2023 14:01:14 +0200 Subject: [PATCH 3/4] Update packages/permission-controller/src/permitted-handler.ts Co-authored-by: legobeat <109787230+legobeat@users.noreply.github.com> --- packages/permission-controller/src/permitted-handler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/permission-controller/src/permitted-handler.ts b/packages/permission-controller/src/permitted-handler.ts index 0a5c3c9eb3..96479b9c80 100644 --- a/packages/permission-controller/src/permitted-handler.ts +++ b/packages/permission-controller/src/permitted-handler.ts @@ -29,7 +29,7 @@ type BaseHandlerExport = { * We use a mapped object type in order to create a type that requires the * presence of the names of all hooks for the given handler. * This can then be used to select only the necessary hooks whenever a method - * is called for purposes of POLA. + * is called for purposes of PoLP. */ export type HookNames = { [Property in keyof Type]: true; From f05f318faa51adf57b79864361caab72247ef40f Mon Sep 17 00:00:00 2001 From: Maarten Zuidhoorn Date: Wed, 19 Apr 2023 14:53:45 +0200 Subject: [PATCH 4/4] Add a 'cause' field to error objects in tests Some error objects should have a 'cause' field that will capture inner causes of particular errors. --- .../src/PermissionController.test.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/permission-controller/src/PermissionController.test.ts b/packages/permission-controller/src/PermissionController.test.ts index a39200f59b..a5226b3e53 100644 --- a/packages/permission-controller/src/PermissionController.test.ts +++ b/packages/permission-controller/src/PermissionController.test.ts @@ -5452,7 +5452,11 @@ describe('PermissionController', () => { }; const expectedError = errors.unauthorized({ - data: { origin, method: PermissionNames.wallet_getSecretArray }, + data: { + origin, + method: PermissionNames.wallet_getSecretArray, + cause: null, + }, }); const { error }: any = await engine.handle(request); @@ -5472,7 +5476,10 @@ describe('PermissionController', () => { method: 'wallet_foo', }; - const expectedError = errors.methodNotFound('wallet_foo', { origin }); + const expectedError = errors.methodNotFound('wallet_foo', { + origin, + cause: null, + }); const { error }: any = await engine.handle(request); expect(error).toMatchObject(expect.objectContaining(expectedError)); @@ -5512,7 +5519,7 @@ describe('PermissionController', () => { const expectedError = errors.internalError( `Request for method "${PermissionNames.wallet_doubleNumber}" returned undefined result.`, - { request: { ...request } }, + { request: { ...request }, cause: null }, ); const { error }: any = await engine.handle(request);