UNPKG

ox

Version:

Ethereum Standard Library

348 lines 10.8 kB
// eslint-disable-next-line jsdoc-js/require-jsdoc export function from(response, options = {}) { const { request } = options; if (request) { const needsId = response.id === undefined; const needsJsonrpc = response.jsonrpc === undefined; if (!needsId && !needsJsonrpc) return response; if (needsId && needsJsonrpc) return { ...response, id: request.id, jsonrpc: request.jsonrpc }; if (needsId) return { ...response, id: request.id }; return { ...response, jsonrpc: request.jsonrpc }; } if (response.id === undefined || response.jsonrpc === undefined) throw new ParseError({ message: 'Invalid JSON-RPC response.' }); return response; } /** * A type-safe interface to parse a JSON-RPC response object as per the [JSON-RPC 2.0 specification](https://www.jsonrpc.org/specification#response_object), and extract the result. * * @example * ```ts twoslash * import { RpcRequest, RpcResponse } from 'ox' * * // 1. Create a request store. * const store = RpcRequest.createStore() * * // 2. Get a request object. * const request = store.prepare({ * method: 'eth_getBlockByNumber', * params: ['0x1', false] * }) * * // 3. Send the JSON-RPC request via HTTP. * const block = await fetch('https://1.rpc.thirdweb.com', { * body: JSON.stringify(request), * headers: { * 'Content-Type': 'application/json' * }, * method: 'POST' * }) * .then((response) => response.json()) * // 4. Parse the JSON-RPC response into a type-safe result. // [!code focus] * .then((response) => * RpcResponse.parse(response, { request }) * ) // [!code focus] * * block // [!code focus] * // ^? * ``` * * :::tip * * If you don't need the return type, you can omit the options entirely. * * ```ts twoslash * // @noErrors * import { RpcResponse } from 'ox' * * const block = await fetch('https://1.rpc.thirdweb.com', {}) * .then((response) => response.json()) * .then((response) => * RpcResponse.parse(response, { request }) * ) // [!code --] * .then(RpcResponse.parse) // [!code ++] * ``` * ::: * * @example * ### Raw Mode * * If `raw` is `true`, the response will be returned as an object with `result` and `error` properties instead of returning the `result` directly and throwing errors. * * ```ts twoslash * import { RpcRequest, RpcResponse } from 'ox' * * const store = RpcRequest.createStore() * * const request = store.prepare({ * method: 'eth_blockNumber' * }) * * const response = RpcResponse.parse( * {}, * { * request, * raw: true // [!code hl] * } * ) * * response.result * // ^? * * response.error * // ^? * ``` * * @param response - Opaque JSON-RPC response object. * @param options - Parsing options. * @returns Typed JSON-RPC result, or response object (if `raw` is `true`). */ export function parse(response, options = {}) { const { raw = false } = options; const response_ = response; if (!response_ || typeof response_ !== 'object' || response_.jsonrpc !== '2.0' || response_.id === undefined || (!('result' in response_) && !('error' in response_))) throw new ParseError({ message: 'Invalid JSON-RPC response.' }); if (raw) return response; if (response_.error) throw parseError(response_.error); return response_.result; } /** * Parses an error into a RPC Error instance. * * @example * ```ts twoslash * import { RpcResponse } from 'ox' * * const error = RpcResponse.parseError({ * code: -32000, * message: 'unsupported method' * }) * * error * // ^? * ``` * * @param error - Error. * @returns RPC Error instance. */ export function parseError(error) { const error_ = error; if (error_ instanceof BaseError) return error_; if (error_ instanceof Error && !('code' in error_)) return new InternalError({ cause: error_, data: error_, message: error_.message, stack: error_.stack, }); const code = error_.code; const Constructor = errorCodeMap[code]; if (Constructor) return new Constructor(error_); return new InternalError({ cause: error_ instanceof Error ? error_ : undefined, data: error_, message: error_.message, stack: error_ instanceof Error ? error_.stack : undefined, }); } /** Thrown when a JSON-RPC error has occurred. */ export class BaseError extends Error { name = 'RpcResponse.BaseError'; cause; stack; code; data; constructor(errorObject) { const { cause, code, message, data, stack } = errorObject; super(message, { cause }); this.cause = cause; this.code = code; this.data = data; this.stack = stack ?? ''; } } /** Thrown when the input to a JSON-RPC method is invalid. */ export class InvalidInputError extends BaseError { static code = -32000; code = -32000; name = 'RpcResponse.InvalidInputError'; constructor(parameters = {}) { super({ code: InvalidInputError.code, data: parameters.data, message: parameters.message ?? 'Missing or invalid parameters.', }); } } /** Thrown when a JSON-RPC resource is not found. */ export class ResourceNotFoundError extends BaseError { static code = -32001; code = -32001; name = 'RpcResponse.ResourceNotFoundError'; constructor(parameters = {}) { super({ code: ResourceNotFoundError.code, data: parameters.data, message: parameters.message ?? 'Requested resource not found.', }); } } /** Thrown when a JSON-RPC resource is unavailable. */ export class ResourceUnavailableError extends BaseError { static code = -32002; code = -32002; name = 'RpcResponse.ResourceUnavailableError'; constructor(parameters = {}) { super({ code: ResourceUnavailableError.code, data: parameters.data, message: parameters.message ?? 'Requested resource not available.', }); } } /** Thrown when a JSON-RPC transaction is rejected. */ export class TransactionRejectedError extends BaseError { static code = -32003; code = -32003; name = 'RpcResponse.TransactionRejectedError'; constructor(parameters = {}) { super({ code: TransactionRejectedError.code, data: parameters.data, message: parameters.message ?? 'Transaction creation failed.', }); } } /** Thrown when a JSON-RPC method is not supported. */ export class MethodNotSupportedError extends BaseError { static code = -32004; code = -32004; name = 'RpcResponse.MethodNotSupportedError'; constructor(parameters = {}) { super({ code: MethodNotSupportedError.code, data: parameters.data, message: parameters.message ?? 'Method is not implemented.', }); } } /** Thrown when a rate-limit is exceeded. */ export class LimitExceededError extends BaseError { static code = -32005; code = -32005; name = 'RpcResponse.LimitExceededError'; constructor(parameters = {}) { super({ code: LimitExceededError.code, data: parameters.data, message: parameters.message ?? 'Rate limit exceeded.', }); } } /** Thrown when a JSON-RPC version is not supported. */ export class VersionNotSupportedError extends BaseError { static code = -32006; code = -32006; name = 'RpcResponse.VersionNotSupportedError'; constructor(parameters = {}) { super({ code: VersionNotSupportedError.code, data: parameters.data, message: parameters.message ?? 'JSON-RPC version not supported.', }); } } /** Thrown when a JSON-RPC request is invalid. */ export class InvalidRequestError extends BaseError { static code = -32600; code = -32600; name = 'RpcResponse.InvalidRequestError'; constructor(parameters = {}) { super({ code: InvalidRequestError.code, data: parameters.data, message: parameters.message ?? 'Input is not a valid JSON-RPC request.', }); } } /** Thrown when a JSON-RPC method is not found. */ export class MethodNotFoundError extends BaseError { static code = -32601; code = -32601; name = 'RpcResponse.MethodNotFoundError'; constructor(parameters = {}) { super({ code: MethodNotFoundError.code, data: parameters.data, message: parameters.message ?? 'Method does not exist.', }); } } /** Thrown when the parameters to a JSON-RPC method are invalid. */ export class InvalidParamsError extends BaseError { static code = -32602; code = -32602; name = 'RpcResponse.InvalidParamsError'; constructor(parameters = {}) { super({ code: InvalidParamsError.code, data: parameters.data, message: parameters.message ?? 'Invalid method parameters.', }); } } /** Thrown when an internal JSON-RPC error has occurred. */ export class InternalError extends BaseError { static code = -32603; code = -32603; name = 'RpcResponse.InternalError'; constructor(parameters = {}) { super({ cause: parameters.cause, code: InternalError.code, data: parameters.data, message: parameters.message ?? 'Internal JSON-RPC error.', stack: parameters.stack, }); } } /** Thrown when a JSON-RPC response is invalid. */ export class ParseError extends BaseError { static code = -32700; code = -32700; name = 'RpcResponse.ParseError'; constructor(parameters = {}) { super({ code: ParseError.code, data: parameters.data, message: parameters.message ?? 'Failed to parse JSON-RPC response.', }); } } /** @internal */ const errorCodeMap = { [InternalError.code]: InternalError, [InvalidInputError.code]: InvalidInputError, [InvalidParamsError.code]: InvalidParamsError, [InvalidRequestError.code]: InvalidRequestError, [LimitExceededError.code]: LimitExceededError, [MethodNotFoundError.code]: MethodNotFoundError, [MethodNotSupportedError.code]: MethodNotSupportedError, [ParseError.code]: ParseError, [ResourceNotFoundError.code]: ResourceNotFoundError, [ResourceUnavailableError.code]: ResourceUnavailableError, [TransactionRejectedError.code]: TransactionRejectedError, [VersionNotSupportedError.code]: VersionNotSupportedError, }; //# sourceMappingURL=RpcResponse.js.map