Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(deps): upgrade eth-json-rpc-filters, eth-json-rpc-engine #9274

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
6 changes: 3 additions & 3 deletions app/core/BackgroundBridge/BackgroundBridge.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable import/no-commonjs */
import URL from 'url-parse';
import { ChainId } from '@metamask/controller-utils';
import { JsonRpcEngine } from 'json-rpc-engine';
import { JsonRpcEngine } from '@metamask/json-rpc-engine';
import MobilePortStream from '../MobilePortStream';
import { setupMultiplex } from '../../util/streams';
import {
Expand All @@ -27,8 +27,8 @@ import snapMethodMiddlewareBuilder from '../Snaps/SnapsMethodMiddleware';
import { SubjectType } from '@metamask/permission-controller';
///: END:ONLY_INCLUDE_IF

const createFilterMiddleware = require('eth-json-rpc-filters');
const createSubscriptionManager = require('eth-json-rpc-filters/subscriptionManager');
const createFilterMiddleware = require('@metamask/eth-json-rpc-filters');
const createSubscriptionManager = require('@metamask/eth-json-rpc-filters/subscriptionManager');
const providerAsMiddleware = require('eth-json-rpc-middleware/providerAsMiddleware');
const pump = require('pump');
// eslint-disable-next-line import/no-nodejs-modules
Expand Down
49 changes: 31 additions & 18 deletions app/core/RPCMethods/RPCMethodMiddleware.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import {
JsonRpcEngine,
import { JsonRpcEngine, JsonRpcMiddleware } from '@metamask/json-rpc-engine';
import type { ProviderConfig } from '@metamask/network-controller';
import { providerErrors, rpcErrors } from '@metamask/rpc-errors';
import type { TransactionParams } from '@metamask/transaction-controller';
import type {
Json,
JsonRpcFailure,
JsonRpcMiddleware,
JsonRpcParams,
JsonRpcRequest,
JsonRpcResponse,
JsonRpcSuccess,
} from 'json-rpc-engine';
import type { TransactionParams } from '@metamask/transaction-controller';
import type { ProviderConfig } from '@metamask/network-controller';
import { providerErrors, rpcErrors } from '@metamask/rpc-errors';
} from '@metamask/utils';
import Engine from '../Engine';
import { store } from '../../store';
import { getPermittedAccounts } from '../Permissions';
Expand Down Expand Up @@ -93,8 +94,8 @@ const jsonrpc = '2.0' as const;
* @throws If the given value is not a valid {@link JsonRpcSuccess} object.
*/
function assertIsJsonRpcSuccess(
response: JsonRpcResponse<unknown>,
): asserts response is JsonRpcSuccess<unknown> {
response: JsonRpcResponse<Json>,
): asserts response is JsonRpcSuccess<Json> {
if ('error' in response) {
throw new Error(`Response failed with error '${JSON.stringify('error')}'`);
} else if (!('result' in response)) {
Expand Down Expand Up @@ -195,8 +196,8 @@ async function callMiddleware({
middleware,
request,
}: {
middleware: JsonRpcMiddleware<unknown, unknown>;
request: JsonRpcRequest<unknown>;
middleware: JsonRpcMiddleware<JsonRpcParams, Json>;
request: JsonRpcRequest<JsonRpcParams>;
}) {
const engine = new JsonRpcEngine();
engine.push(middleware);
Expand Down Expand Up @@ -984,7 +985,8 @@ describe('getRpcMethodMiddleware', () => {
it('returns a JSON-RPC error if an error is thrown when adding this transaction', async () => {
// Omit `from` and `chainId` here to skip validation for simplicity
// Downcast needed here because `from` is required by this type
const mockTransactionParameters = {} as TransactionParams;
const mockTransactionParameters = {} as (TransactionParams &
JsonRpcParams)[];
// Transaction fails before returning a result
mockAddTransaction.mockImplementation(async () => {
throw new Error('Failed to add transaction');
Expand All @@ -999,20 +1001,26 @@ describe('getRpcMethodMiddleware', () => {
method: 'eth_sendTransaction',
params: [mockTransactionParameters],
};
const expectedError = rpcErrors.internal('Failed to add transaction');
const expectedError = rpcErrors.internal('Internal JSON-RPC error.');
const expectedErrorCauseMessage = 'Failed to add transaction';

const response = await callMiddleware({ middleware, request });

expect((response as JsonRpcFailure).error.code).toBe(expectedError.code);
expect((response as JsonRpcFailure).error.message).toBe(
expectedError.message,
);
// @ts-expect-error - TODO: This should type
expect((response as JsonRpcFailure).error.data.cause.message).toBe(
expectedErrorCauseMessage,
);
});

it('returns a JSON-RPC error if an error is thrown after approval', async () => {
// Omit `from` and `chainId` here to skip validation for simplicity
// Downcast needed here because `from` is required by this type
const mockTransactionParameters = {} as TransactionParams;
const mockTransactionParameters = {} as (TransactionParams &
JsonRpcParams)[];
setupGlobalState({
addTransactionResult: Promise.reject(
new Error('Failed to process transaction'),
Expand All @@ -1028,14 +1036,19 @@ describe('getRpcMethodMiddleware', () => {
method: 'eth_sendTransaction',
params: [mockTransactionParameters],
};
const expectedError = rpcErrors.internal('Failed to process transaction');
const expectedError = rpcErrors.internal('Internal JSON-RPC error.');
const expectedErrorCauseMessage = 'Failed to process transaction';

const response = await callMiddleware({ middleware, request });

expect((response as JsonRpcFailure).error.code).toBe(expectedError.code);
expect((response as JsonRpcFailure).error.message).toBe(
expectedError.message,
);
// @ts-expect-error - TODO: This should type
expect((response as JsonRpcFailure).error.data.cause.message).toBe(
expectedErrorCauseMessage,
);
});
});

Expand Down Expand Up @@ -1101,7 +1114,7 @@ describe('getRpcMethodMiddleware', () => {
method: 'personal_ecRecover',
params: [helloWorldMessage],
};
const expectedError = rpcErrors.internal('Missing signature parameter');
const expectedError = rpcErrors.internal('Internal JSON-RPC error.');

const response = await callMiddleware({ middleware, request });

Expand All @@ -1120,9 +1133,9 @@ describe('getRpcMethodMiddleware', () => {
jsonrpc,
id: 1,
method: 'personal_ecRecover',
params: [undefined, helloWorldSignature],
params: [undefined, helloWorldSignature] as JsonRpcParams,
};
const expectedError = rpcErrors.internal('Missing data parameter');
const expectedError = rpcErrors.internal('Internal JSON-RPC error.');

const response = await callMiddleware({ middleware, request });

Expand Down
9 changes: 5 additions & 4 deletions app/core/RPCMethods/RPCMethodMiddleware.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Alert } from 'react-native';
import { getVersion } from 'react-native-device-info';
import { createAsyncMiddleware } from 'json-rpc-engine';
import { createAsyncMiddleware } from '@metamask/json-rpc-engine';
import { providerErrors, rpcErrors } from '@metamask/rpc-errors';
import {
EndFlowOptions,
Expand All @@ -15,6 +15,7 @@ import {
PermissionController,
permissionRpcMethods,
} from '@metamask/permission-controller';
import type { Hex } from '@metamask/utils';
import Networks, {
blockTagParamIndex,
getAllNetworks,
Expand Down Expand Up @@ -103,7 +104,7 @@ export const checkActiveAccountAndChainId = async ({
isWalletConnect,
}: {
address?: string;
chainId?: number;
chainId?: Hex;
channelId?: string;
hostname: string;
isWalletConnect: boolean;
Expand Down Expand Up @@ -208,7 +209,7 @@ const generateRawSignature = async ({
title: { current: string };
icon: { current: string | undefined };
analytics: { [key: string]: string | boolean };
chainId: number;
chainId: Hex;
isMMSDK: boolean;
channelId?: string;
getSource: () => string;
Expand Down Expand Up @@ -507,7 +508,7 @@ export const getRpcMethodMiddleware = ({
chainId,
}: {
from?: string;
chainId?: number;
chainId?: Hex;
}) => {
await checkActiveAccountAndChainId({
hostname,
Expand Down
35 changes: 25 additions & 10 deletions app/core/RPCMethods/eth_sendTransaction.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
// eslint-disable-next-line import/no-nodejs-modules
import { inspect } from 'util';
import type { JsonRpcRequest, PendingJsonRpcResponse } from 'json-rpc-engine';
import type {
Json,
JsonRpcParams,
JsonRpcRequest,
PendingJsonRpcResponse,
} from '@metamask/utils';
import type {
TransactionParams,
TransactionController,
Expand Down Expand Up @@ -39,14 +44,16 @@ jest.mock('../../util/transaction-controller', () => ({
* @param params - The request parameters.
* @returns The JSON-RPC request.
*/
function constructSendTransactionRequest(
params: unknown,
): JsonRpcRequest<unknown> & { method: 'eth_sendTransaction' } {
function constructSendTransactionRequest(params: Json[]): JsonRpcRequest<
[TransactionParams & JsonRpcParams]
> & {
method: 'eth_sendTransaction';
} {
return {
jsonrpc: '2.0',
id: 1,
method: 'eth_sendTransaction',
params,
params: params as any,
};
}

Expand All @@ -55,7 +62,7 @@ function constructSendTransactionRequest(
*
* @returns A pending JSON-RPC response.
*/
function constructPendingJsonRpcResponse(): PendingJsonRpcResponse<unknown> {
function constructPendingJsonRpcResponse(): PendingJsonRpcResponse<Json> {
return {
jsonrpc: '2.0',
id: 1,
Expand Down Expand Up @@ -136,13 +143,17 @@ function getMockAddTransaction({
describe('eth_sendTransaction', () => {
it('sends the transaction and returns the resulting hash', async () => {
const mockAddress = '0x0000000000000000000000000000000000000001';
const mockTransactionParameters = { from: mockAddress };
const mockTransactionParameters = {
from: mockAddress,
} as TransactionParams;
const expectedResult = 'fake-hash';
const pendingResult = constructPendingJsonRpcResponse();

await eth_sendTransaction({
hostname: 'example.metamask.io',
req: constructSendTransactionRequest([mockTransactionParameters]),
req: constructSendTransactionRequest([
mockTransactionParameters as unknown as JsonRpcParams,
]) as any,
res: pendingResult,
sendTransaction: getMockAddTransaction({
expectedTransaction: mockTransactionParameters,
Expand All @@ -164,7 +175,9 @@ describe('eth_sendTransaction', () => {
async () =>
await eth_sendTransaction({
hostname: 'example.metamask.io',
req: constructSendTransactionRequest(invalidParameter),
req: constructSendTransactionRequest(
invalidParameter as unknown as Json[],
),
res: constructPendingJsonRpcResponse(),
sendTransaction: getMockAddTransaction({
returnValue: 'fake-hash',
Expand All @@ -186,7 +199,9 @@ describe('eth_sendTransaction', () => {
async () =>
await eth_sendTransaction({
hostname: 'example.metamask.io',
req: constructSendTransactionRequest(invalidParameter),
req: constructSendTransactionRequest(
invalidParameter as unknown as Json[],
),
res: constructPendingJsonRpcResponse(),
sendTransaction: getMockAddTransaction({
returnValue: 'fake-hash',
Expand Down
25 changes: 18 additions & 7 deletions app/core/RPCMethods/eth_sendTransaction.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import type { JsonRpcRequest, PendingJsonRpcResponse } from 'json-rpc-engine';
import type {
Hex,
Json,
JsonRpcParams,
JsonRpcRequest,
PendingJsonRpcResponse,
} from '@metamask/utils';
import {
TransactionController,
TransactionParams,
WalletDevice,
} from '@metamask/transaction-controller';
import { rpcErrors } from '@metamask/rpc-errors';
Expand Down Expand Up @@ -46,6 +53,11 @@ const hasProperty = <
): objectToCheck is ObjectToCheck & Record<Property, unknown> =>
Object.hasOwnProperty.call(objectToCheck, name);

interface SendArgs {
from: string;
chainId?: Hex;
}

/**
* Handle a `eth_sendTransaction` request.
*
Expand All @@ -66,13 +78,12 @@ async function eth_sendTransaction({
validateAccountAndChainId,
}: {
hostname: string;
req: JsonRpcRequest<unknown> & { method: 'eth_sendTransaction' };
res: PendingJsonRpcResponse<unknown>;
req: JsonRpcRequest<[TransactionParams & JsonRpcParams]> & {
method: 'eth_sendTransaction';
};
res: PendingJsonRpcResponse<Json>;
sendTransaction: TransactionController['addTransaction'];
validateAccountAndChainId: (args: {
from: string;
chainId?: number;
}) => Promise<void>;
validateAccountAndChainId: (args: SendArgs) => Promise<void>;
}) {
if (
!Array.isArray(req.params) &&
Expand Down
2 changes: 1 addition & 1 deletion app/core/RPCMethods/wallet_watchAsset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
} from '../../constants/error';
import { selectChainId } from '../../selectors/networkController';
import { isValidAddress } from 'ethereumjs-util';
import { JsonRpcRequest, PendingJsonRpcResponse } from 'json-rpc-engine';
import { JsonRpcRequest, PendingJsonRpcResponse } from '@metamask/utils';

const wallet_watchAsset = async ({
req,
Expand Down
21 changes: 12 additions & 9 deletions app/core/SanitizationMiddleware.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { JsonRpcMiddleware, JsonRpcRequest } from 'json-rpc-engine';
import { JsonRpcMiddleware } from '@metamask/json-rpc-engine';
import type { Json, JsonRpcParams, JsonRpcRequest } from '@metamask/utils';
import { addHexPrefix } from 'ethereumjs-util';

// We use this to clean any custom params from the txParams
Expand Down Expand Up @@ -39,18 +40,20 @@ export const permittedKeys = [
* @param parameter - The parameter to sanitize.
* @returns The given parameter containing just permitted keys.
*/
function sanitizeRpcParameter(parameter: Record<PropertyKey, unknown>) {
return permittedKeys.reduce<Record<string, unknown>>((copy, permitted) => {
function sanitizeRpcParameter(
parameter: Record<PropertyKey, unknown>,
): Record<string, Json> {
return permittedKeys.reduce<Record<string, Json>>((copy, permitted) => {
if (permitted in parameter) {
const value = parameter[permitted];
if (Array.isArray(value)) {
copy[permitted] = value.map(sanitize);
copy[permitted] = value.map(sanitize) as Json;
} else {
copy[permitted] = sanitize(value);
copy[permitted] = sanitize(value) as Json;
}
}
return copy;
}, {});
}, {} as Record<string, Json>);
}

/**
Expand Down Expand Up @@ -80,10 +83,10 @@ function sanitize(value: unknown) {
* request along.
*/
export function createSanitizationMiddleware(): JsonRpcMiddleware<
unknown,
unknown
JsonRpcParams,
Json
> {
return (req: JsonRpcRequest<unknown>, _: any, next: () => any) => {
return (req: JsonRpcRequest<JsonRpcParams>, _: any, next: () => any) => {
if (!Array.isArray(req.params)) {
next();
return;
Expand Down
6 changes: 3 additions & 3 deletions app/core/Snaps/SnapBridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
createSwappableProxy,
createEventEmitterProxy,
} from '@metamask/swappable-obj-proxy';
import { JsonRpcEngine } from 'json-rpc-engine';
import { JsonRpcEngine } from '@metamask/json-rpc-engine';
import { createEngineStream } from 'json-rpc-middleware-stream';
import { NetworksChainId } from '@metamask/controller-utils';

Expand All @@ -22,8 +22,8 @@ import snapMethodMiddlewareBuilder from './SnapsMethodMiddleware';
import { SubjectType } from '@metamask/permission-controller';

const ObjectMultiplex = require('@metamask/object-multiplex');
const createFilterMiddleware = require('eth-json-rpc-filters');
const createSubscriptionManager = require('eth-json-rpc-filters/subscriptionManager');
const createFilterMiddleware = require('@metamask/eth-json-rpc-filters');
const createSubscriptionManager = require('@metamask/eth-json-rpc-filters/subscriptionManager');
const providerAsMiddleware = require('eth-json-rpc-middleware/providerAsMiddleware');
const pump = require('pump');

Expand Down
Loading
Loading