Skip to content

Commit

Permalink
Port rpc errors from metamask
Browse files Browse the repository at this point in the history
  • Loading branch information
arch1995 committed Aug 14, 2024
1 parent 1e91538 commit dfa557e
Show file tree
Hide file tree
Showing 14 changed files with 1,401 additions and 164 deletions.
163 changes: 3 additions & 160 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,6 @@
},
"dependencies": {
"@ethereumjs/util": "^9.0.3",
"@metamask/rpc-errors": "6.2.1",
"@toruslabs/constants": "^14.0.0",
"@toruslabs/eccrypto": "^5.0.4",
"@toruslabs/ffjavascript": "^3.0.0",
Expand All @@ -135,6 +134,7 @@
"enc-utils": "^3.0.0",
"end-of-stream": "^1.4.4",
"eventemitter3": "^5.0.1",
"fast-safe-stringify": "^2.1.1",
"json-stable-stringify": "^1.1.1",
"loglevel": "^1.9.1",
"once": "^1.4.0",
Expand Down
133 changes: 133 additions & 0 deletions src/jrpc/errors/error-classes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import safeStringify from "fast-safe-stringify";

import type { JRPCError as SerializedJRPCError, Json, OptionalDataWithOptionalCause } from "../interfaces";
import { dataHasCause, isPlainObject, serializeCause } from "./utils";

/**
* Check if the given code is a valid JSON-RPC error code.
*
* @param code - The code to check.
* @returns Whether the code is valid.
*/
function isValidEthProviderCode(code: number): boolean {
return Number.isInteger(code) && code >= 1000 && code <= 4999;
}

/**
* A JSON replacer function that omits circular references.
*
* @param _ - The key being replaced.
* @param value - The value being replaced.
* @returns The value to use in place of the original value.
*/
function stringifyReplacer(_: unknown, value: unknown): unknown {
if (value === "[Circular]") {
return undefined;
}

return value;
}

/**
* Error subclass implementing JSON RPC 2.0 errors and Ethereum RPC errors
* per EIP-1474.
*
* Permits any integer error code.
*/
export class JsonRpcError<Data extends OptionalDataWithOptionalCause> extends Error {
// The `cause` definition can be removed when tsconfig lib and/or target have changed to >=es2022
public cause?: unknown;

public code: number;

public data?: Data;

constructor(code: number, message: string, data?: Data) {
if (!Number.isInteger(code)) {
throw new Error('"code" must be an integer.');
}

if (!message || typeof message !== "string") {
throw new Error('"message" must be a non-empty string.');
}

if (dataHasCause(data)) {
super(message, { cause: data.cause });

// Browser backwards-compatibility fallback
if (!Object.hasOwn(this, "cause")) {
Object.assign(this, { cause: data.cause });
}
} else {
super(message);
}

if (data !== undefined) {
this.data = data;
}

this.code = code;
this.cause = (data as { cause?: unknown })?.cause;
}

/**
* Get the error as JSON-serializable object.
*
* @returns A plain object with all public class properties.
*/
serialize(): SerializedJRPCError {
const serialized: SerializedJRPCError = {
code: this.code,
message: this.message,
};

if (this.data !== undefined) {
// `this.data` is not guaranteed to be a plain object, but this simplifies
// the type guard below. We can safely cast it because we know it's a
// JSON-serializable value.
serialized.data = this.data as { [key: string]: Json };

if (isPlainObject(this.data)) {
serialized.data.cause = serializeCause((this.data as { cause?: unknown }).cause);
}
}

if (this.stack) {
serialized.stack = this.stack;
}

return serialized;
}

/**
* Get a string representation of the serialized error, omitting any circular
* references.
*
* @returns A string representation of the serialized error.
*/
toString(): string {
return safeStringify(this.serialize(), stringifyReplacer, 2);
}
}

/**
* Error subclass implementing Ethereum Provider errors per EIP-1193.
* Permits integer error codes in the [ 1000 <= 4999 ] range.

Check warning on line 115 in src/jrpc/errors/error-classes.ts

View workflow job for this annotation

GitHub Actions / test (20.x, ubuntu-latest)

tsdoc-malformed-html-name: Invalid HTML element: Expecting an HTML name
*/
export class EthereumProviderError<Data extends OptionalDataWithOptionalCause> extends JsonRpcError<Data> {
/**
* Create an Ethereum Provider JSON-RPC error.
*
* @param code - The JSON-RPC error code. Must be an integer in the
* `1000 <= n <= 4999` range.
* @param message - The JSON-RPC error message.
* @param data - Optional data to include in the error.
*/
constructor(code: number, message: string, data?: Data) {
if (!isValidEthProviderCode(code)) {
throw new Error('"code" must be an integer such that: 1000 <= code <= 4999');
}

super(code, message, data);
}
}
Loading

0 comments on commit dfa557e

Please sign in to comment.