Collection of utility functions used in web3.js for Ethereum dApp development
—
Complete JSON-RPC protocol utilities including request/response validation, batch operations, and comprehensive type guards. These functions provide robust JSON-RPC protocol handling for Web3 applications.
/**
* Checks if response has valid RPC error code
* @param rpcError - JSON-RPC response with error
* @returns true if error code is valid
*/
function isResponseRpcError(rpcError: JsonRpcResponseWithError): boolean;/**
* Type guard for response with result
* @param response - JSON-RPC response to check
* @returns true if response contains result
*/
function isResponseWithResult<Result, Error>(
response: JsonRpcResponse<Result, Error>
): response is JsonRpcResponseWithResult<Result>;
/**
* Type guard for response with error
* @param response - JSON-RPC response to check
* @returns true if response contains error
*/
function isResponseWithError<Error, Result>(
response: JsonRpcResponse<Result, Error>
): response is JsonRpcResponseWithError<Error>;
/**
* Type guard for notification response
* @param response - Response to check
* @returns true if response is a notification
*/
function isResponseWithNotification<Result>(
response: JsonRpcNotification<Result> | JsonRpcSubscriptionResult
): response is JsonRpcNotification<Result>;
/**
* Type guard for subscription result
* @param response - Response to check
* @returns true if response is a subscription result
*/
function isSubscriptionResult<Result>(
response: JsonRpcNotification<Result> | JsonRpcSubscriptionResult
): response is JsonRpcSubscriptionResult;/**
* Validates JSON-RPC response format
* @param response - JSON-RPC response to validate
* @returns true if response is valid
*/
function validateResponse<Result, Error>(
response: JsonRpcResponse<Result, Error>
): boolean;
/**
* Checks if single/batch response is valid
* @param response - JSON-RPC response to check
* @returns true if response format is valid
*/
function isValidResponse<Result, Error>(
response: JsonRpcResponse<Result, Error>
): boolean;/**
* Type guard for batch response
* @param response - Response to check
* @returns true if response is a batch response
*/
function isBatchResponse<Result, Error>(
response: JsonRpcResponse<Result, Error>
): response is JsonRpcBatchResponse<Result, Error>;
/**
* Type guard for batch request
* @param request - Request to check
* @returns true if request is a batch request
*/
function isBatchRequest(
request: JsonRpcBatchRequest | JsonRpcRequest<unknown> | JsonRpcOptionalRequest<unknown>
): request is JsonRpcBatchRequest;/**
* Converts requests to batch payload
* @param requests - Array of JSON-RPC requests
* @returns Batch request payload
*/
function toBatchPayload(requests: JsonRpcOptionalRequest<unknown>[]): JsonRpcBatchRequest;/**
* Sets starting number for request IDs
* @param start - Starting ID number (undefined for auto-increment)
*/
function setRequestIdStart(start: number | undefined): void;/**
* Converts request to payload format
* @param request - JSON-RPC request (with optional ID)
* @returns Complete JSON-RPC payload with ID
*/
function toPayload<ParamType>(
request: JsonRpcOptionalRequest<ParamType>
): JsonRpcPayload<ParamType>;import {
toPayload, validateResponse, isResponseWithResult,
isResponseWithError, toBatchPayload
} from "web3-utils";
// Create single request payload
const request = {
method: "eth_getBalance",
params: ["0x742E4C5b469F50A4a8b399D4915C1fc93d15651B", "latest"]
};
const payload = toPayload(request);
// Result: { jsonrpc: "2.0", id: 1, method: "eth_getBalance", params: [...] }
// Validate and handle response
async function handleJsonRpcResponse(response: any) {
if (!validateResponse(response)) {
throw new Error('Invalid JSON-RPC response format');
}
if (isResponseWithResult(response)) {
console.log('Success result:', response.result);
return response.result;
} else if (isResponseWithError(response)) {
console.error('RPC Error:', response.error);
throw new Error(`RPC Error ${response.error.code}: ${response.error.message}`);
}
}
// Usage with fetch
const response = await fetch('/rpc', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
const jsonResponse = await response.json();
const result = await handleJsonRpcResponse(jsonResponse);import { toBatchPayload, isBatchResponse, isValidResponse } from "web3-utils";
// Create batch request
const requests = [
{ method: "eth_getBalance", params: ["0x742E4C5b469F50A4a8b399D4915C1fc93d15651B", "latest"] },
{ method: "eth_getTransactionCount", params: ["0x742E4C5b469F50A4a8b399D4915C1fc93d15651B", "latest"] },
{ method: "eth_gasPrice", params: [] }
];
const batchPayload = toBatchPayload(requests);
// Result: Array of payloads with unique IDs
// Handle batch response
async function handleBatchResponse(response: any) {
if (!isValidResponse(response)) {
throw new Error('Invalid response format');
}
if (isBatchResponse(response)) {
const results = [];
for (const singleResponse of response) {
if (isResponseWithResult(singleResponse)) {
results.push(singleResponse.result);
} else if (isResponseWithError(singleResponse)) {
results.push({ error: singleResponse.error });
}
}
return results;
} else {
// Single response
return [await handleJsonRpcResponse(response)];
}
}
// Usage
const batchResponse = await fetch('/rpc', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(batchPayload)
});
const jsonBatchResponse = await batchResponse.json();
const results = await handleBatchResponse(jsonBatchResponse);import {
isResponseWithNotification, isSubscriptionResult
} from "web3-utils";
// WebSocket message handler
function handleWebSocketMessage(data: any) {
try {
const message = JSON.parse(data);
if (isResponseWithNotification(message)) {
console.log('Received notification:', message.method, message.params);
// Handle notification (e.g., new block, pending transaction)
} else if (isSubscriptionResult(message)) {
console.log('Subscription data:', message.params);
// Handle subscription data
} else {
// Regular JSON-RPC response
return handleJsonRpcResponse(message);
}
} catch (error) {
console.error('Failed to parse WebSocket message:', error);
}
}import { setRequestIdStart, toPayload } from "web3-utils";
// Set custom starting ID
setRequestIdStart(1000);
const request1 = toPayload({ method: "eth_blockNumber" });
console.log(request1.id); // 1000
const request2 = toPayload({ method: "eth_gasPrice" });
console.log(request2.id); // 1001
// Reset to auto-increment
setRequestIdStart(undefined);
const request3 = toPayload({ method: "eth_chainId" });
console.log(request3.id); // Auto-generatedimport {
isResponseRpcError, isResponseWithError, validateResponse
} from "web3-utils";
async function robustJsonRpcCall(request: any) {
const payload = toPayload(request);
try {
const response = await fetch('/rpc', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const jsonResponse = await response.json();
// Validate response format
if (!validateResponse(jsonResponse)) {
throw new Error('Invalid JSON-RPC response format');
}
// Check for RPC errors
if (isResponseWithError(jsonResponse)) {
if (isResponseRpcError(jsonResponse)) {
// Standard RPC error
const { code, message, data } = jsonResponse.error;
throw new Error(`RPC Error ${code}: ${message}${data ? ` (${JSON.stringify(data)})` : ''}`);
} else {
// Non-standard error
throw new Error(`Invalid RPC error: ${JSON.stringify(jsonResponse.error)}`);
}
}
if (isResponseWithResult(jsonResponse)) {
return jsonResponse.result;
}
throw new Error('Response contains neither result nor error');
} catch (error) {
console.error('JSON-RPC call failed:', error);
throw error;
}
}The JSON-RPC utilities work with standard JSON-RPC type definitions:
// Basic JSON-RPC structures
interface JsonRpcRequest<T> {
jsonrpc: "2.0";
id: number | string;
method: string;
params?: T;
}
interface JsonRpcOptionalRequest<T> {
method: string;
params?: T;
id?: number | string;
}
interface JsonRpcPayload<T> {
jsonrpc: "2.0";
id: number | string;
method: string;
params?: T;
}
interface JsonRpcResponseWithResult<T> {
jsonrpc: "2.0";
id: number | string;
result: T;
}
interface JsonRpcResponseWithError<T = any> {
jsonrpc: "2.0";
id: number | string;
error: {
code: number;
message: string;
data?: T;
};
}
type JsonRpcResponse<Result, Error> =
| JsonRpcResponseWithResult<Result>
| JsonRpcResponseWithError<Error>;
type JsonRpcBatchRequest = JsonRpcRequest<unknown>[];
type JsonRpcBatchResponse<Result, Error> = JsonRpcResponse<Result, Error>[];Install with Tessl CLI
npx tessl i tessl/npm-web3-utils