UNPKG

zksync-sso

Version:
153 lines (126 loc) 4.1 kB
import { errorValues, standardErrorCodes } from "./constants.js"; const FALLBACK_MESSAGE = "Unspecified error message."; const JSON_RPC_SERVER_ERROR_MESSAGE = "Unspecified server error."; type ErrorValueKey = keyof typeof errorValues; /** * Gets the message for a given code, or a fallback message if the code has * no corresponding message. */ export function getMessageFromCode( code: number | undefined, fallbackMessage: string = FALLBACK_MESSAGE, ): string { if (code && Number.isInteger(code)) { const codeString = code.toString(); if (hasKey(errorValues, codeString)) { return errorValues[codeString as ErrorValueKey].message; } if (isJsonRpcServerError(code)) { return JSON_RPC_SERVER_ERROR_MESSAGE; } } return fallbackMessage; } /** * Returns whether the given code is valid. * A code is only valid if it has a message. */ export function isValidCode(code: number): boolean { if (!Number.isInteger(code)) { return false; } const codeString = code.toString(); if (errorValues[codeString as ErrorValueKey]) { return true; } if (isJsonRpcServerError(code)) { return true; } return false; } /** * Returns the error code from an error object. */ export function getErrorCode(error: unknown): number | undefined { if (typeof error === "number") { return error; } else if (isErrorWithCode(error)) { return error.code ?? error.errorCode; } return undefined; } interface ErrorWithCode { code?: number; errorCode?: number; } function isErrorWithCode(error: unknown): error is ErrorWithCode { return ( typeof error === "object" && error !== null && (typeof (error as ErrorWithCode).code === "number" || typeof (error as ErrorWithCode).errorCode === "number") ); } /** * Serializes the given error to an Ethereum JSON RPC-compatible error object. * Merely copies the given error's values if it is already compatible. * If the given error is not fully compatible, it will be preserved on the * returned object's data.originalError property. */ export interface SerializedEthereumRpcError { code: number; // must be an integer message: string; data?: unknown; stack?: string; } export function serialize( error: unknown, { shouldIncludeStack = false } = {}, ): SerializedEthereumRpcError { const serialized: Partial<SerializedEthereumRpcError> = {}; if ( error && typeof error === "object" && !Array.isArray(error) && hasKey(error as Record<string, unknown>, "code") && isValidCode((error as SerializedEthereumRpcError).code) ) { const _error = error as Partial<SerializedEthereumRpcError>; serialized.code = _error.code; if (_error.message && typeof _error.message === "string") { serialized.message = _error.message; if (hasKey(_error, "data")) { serialized.data = _error.data; } } else { serialized.message = getMessageFromCode((serialized as SerializedEthereumRpcError).code); serialized.data = { originalError: assignOriginalError(error) }; } } else { serialized.code = standardErrorCodes.rpc.internal; serialized.message = hasStringProperty(error, "message") ? error.message : FALLBACK_MESSAGE; serialized.data = { originalError: assignOriginalError(error) }; } if (shouldIncludeStack) { serialized.stack = hasStringProperty(error, "stack") ? error.stack : undefined; } return serialized as SerializedEthereumRpcError; } // Internal function isJsonRpcServerError(code: number): boolean { return code >= -32099 && code <= -32000; } function assignOriginalError(error: unknown): unknown { if (error && typeof error === "object" && !Array.isArray(error)) { return Object.assign({}, error); } return error; } function hasKey(obj: Record<string, unknown>, key: string) { return Object.prototype.hasOwnProperty.call(obj, key); } function hasStringProperty<T>(obj: unknown, prop: keyof T): obj is T { return ( typeof obj === "object" && obj !== null && prop in obj && typeof (obj as T)[prop] === "string" ); }