Skip to content

Commit d92428b

Browse files
Potential implementation
1 parent 0cb71a7 commit d92428b

File tree

3 files changed

+43
-60
lines changed

3 files changed

+43
-60
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@
3737
},
3838
"dependencies": {
3939
"@metamask/utils": "^3.2.0",
40-
"fast-safe-stringify": "^2.0.6"
40+
"fast-safe-stringify": "^2.0.6",
41+
"superstruct": "^0.16.6"
4142
},
4243
"devDependencies": {
4344
"@lavamoat/allow-scripts": "^2.0.3",

src/utils.ts

Lines changed: 39 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
1-
import { hasProperty, isPlainObject, Json } from '@metamask/utils';
1+
import {
2+
hasProperty,
3+
JsonRpcErrorStruct,
4+
isValidJson,
5+
isObject,
6+
} from '@metamask/utils';
7+
import { is, create } from 'superstruct';
28
import { errorCodes, errorValues } from './error-constants';
39
import { EthereumRpcError, SerializedEthereumRpcError } from './classes';
410

511
const FALLBACK_ERROR_CODE = errorCodes.rpc.internal;
6-
const FALLBACK_MESSAGE = 'Invalid internal error. See "data.originalError" for original value. Please report this bug.';
12+
const FALLBACK_MESSAGE =
13+
'Invalid internal error. See "data.originalError" for original value. Please report this bug.';
714
const FALLBACK_ERROR: SerializedEthereumRpcError = {
815
code: FALLBACK_ERROR_CODE,
916
message: getMessageFromCode(FALLBACK_ERROR_CODE),
@@ -70,11 +77,7 @@ export function serializeError(
7077
error: unknown,
7178
{ fallbackError = FALLBACK_ERROR, shouldIncludeStack = false } = {},
7279
): SerializedEthereumRpcError {
73-
if (
74-
!fallbackError ||
75-
!isValidCode(fallbackError.code) ||
76-
typeof fallbackError.message !== 'string'
77-
) {
80+
if (!is(fallbackError, JsonRpcErrorStruct)) {
7881
throw new Error(
7982
'Must provide fallback error with integer number code and string message.',
8083
);
@@ -84,49 +87,40 @@ export function serializeError(
8487
return error.serialize();
8588
}
8689

87-
const serialized: Partial<SerializedEthereumRpcError> = {};
90+
const serialized = buildError(
91+
error,
92+
fallbackError as SerializedEthereumRpcError,
93+
);
8894

89-
if (
90-
error &&
91-
isPlainObject(error) &&
92-
hasProperty(error, 'code') &&
93-
isValidCode((error as SerializedEthereumRpcError).code)
94-
) {
95-
const _error = error as Partial<SerializedEthereumRpcError>;
96-
serialized.code = _error.code as number;
97-
98-
if (_error.message && typeof _error.message === 'string') {
99-
serialized.message = _error.message;
100-
101-
if (hasProperty(_error, 'data')) {
102-
serialized.data = _error.data ?? null;
103-
}
104-
} else {
105-
serialized.message = getMessageFromCode(
106-
(serialized as SerializedEthereumRpcError).code,
107-
);
108-
109-
// TODO: Verify that the original error is serializable.
110-
serialized.data = { originalError: assignOriginalError(error) } as Json;
111-
}
112-
} else {
113-
serialized.code = fallbackError.code;
114-
115-
const message = (error as any)?.message;
116-
117-
serialized.message =
118-
message && typeof message === 'string' ? message : fallbackError.message;
119-
120-
// TODO: Verify that the original error is serializable.
121-
serialized.data = { originalError: assignOriginalError(error) } as Json;
95+
if (!shouldIncludeStack) {
96+
delete serialized.stack;
12297
}
12398

124-
const stack = (error as any)?.stack;
99+
return serialized as SerializedEthereumRpcError;
100+
}
125101

126-
if (shouldIncludeStack && error && stack && typeof stack === 'string') {
127-
serialized.stack = stack;
102+
/**
103+
* Constructs a JSON serializable object given an error and a fallbackError.
104+
*
105+
* @param error - The error in question.
106+
* @param fallbackError - The fallback error.
107+
* @returns A JSON serializable error object.
108+
*/
109+
function buildError(error: unknown, fallbackError: SerializedEthereumRpcError) {
110+
if (is(error, JsonRpcErrorStruct)) {
111+
return create(error, JsonRpcErrorStruct);
128112
}
129-
return serialized as SerializedEthereumRpcError;
113+
// If the original error is an object, we make a copy of it, this should also copy class properties to an object
114+
const originalError = isObject(error) ? Object.assign({}, error) : error;
115+
const fallbackWithOriginal = {
116+
...fallbackError,
117+
data: { originalError },
118+
};
119+
// We only allow returning originalError if it turns out to be valid JSON
120+
if (isValidJson(fallbackWithOriginal)) {
121+
return fallbackWithOriginal;
122+
}
123+
return fallbackError;
130124
}
131125

132126
/**
@@ -138,16 +132,3 @@ export function serializeError(
138132
function isJsonRpcServerError(code: number): boolean {
139133
return code >= -32099 && code <= -32000;
140134
}
141-
142-
/**
143-
* Create a copy of the given value if it's an object, and not an array.
144-
*
145-
* @param error - The value to copy.
146-
* @returns The copied value, or the original value if it's not an object.
147-
*/
148-
function assignOriginalError(error: unknown): unknown {
149-
if (error && typeof error === 'object' && !Array.isArray(error)) {
150-
return Object.assign({}, error);
151-
}
152-
return error;
153-
}

yarn.lock

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -911,6 +911,7 @@ __metadata:
911911
jest-it-up: ^2.0.2
912912
prettier: ^2.7.1
913913
prettier-plugin-packagejson: ^2.2.18
914+
superstruct: ^0.16.6
914915
ts-jest: ^28.0.7
915916
typedoc: ^0.23.19
916917
typescript: ~4.7.4
@@ -5554,7 +5555,7 @@ __metadata:
55545555
languageName: node
55555556
linkType: hard
55565557

5557-
"superstruct@npm:^0.16.5":
5558+
"superstruct@npm:^0.16.5, superstruct@npm:^0.16.6":
55585559
version: 0.16.7
55595560
resolution: "superstruct@npm:0.16.7"
55605561
checksum: c8c855ff6945da8a41048c6d236de7b1af5d4d9c31742b3ee54d65647c31597488620281f65e095d5efc9e2fbdaad529b8c8f2506c12569d428467c835a21477

0 commit comments

Comments
 (0)