ox
Version:
Ethereum Standard Library
348 lines • 10.8 kB
JavaScript
// 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