Collection of utility functions used in web3.js for Ethereum dApp development
—
Abstract base classes for implementing EIP-1193 compatible providers and socket-based connections with automatic reconnection support. These classes provide the foundation for building Web3 providers.
Abstract implementation of the EIP-1193 provider standard.
/**
* Abstract EIP-1193 provider implementation
* Extends Web3BaseProvider with EIP-1193 specific functionality
*/
abstract class Eip1193Provider<API extends Web3APISpec = EthExecutionAPI> extends Web3BaseProvider<API> {
/**
* Called when provider connects
* Should be implemented by concrete providers
*/
protected _onConnect(): void;
/**
* Called when provider disconnects
* Should be implemented by concrete providers
* @param code - Disconnection code
* @param data - Optional disconnection data
*/
protected _onDisconnect(code: number, data?: unknown): void;
}Abstract socket-based provider with reconnection capabilities.
/**
* Abstract socket-based provider for WebSocket, IPC, etc.
* Provides reconnection logic and socket management
*/
abstract class SocketProvider<
MessageEvent,
CloseEvent,
ErrorEvent,
API extends Web3APISpec = EthExecutionAPI
> extends Eip1193Provider<API> {
/**
* Socket connection accessor
* @returns Current socket connection
*/
get SocketConnection(): unknown;
/**
* Creates new socket provider
* @param socketPath - Path or URL for socket connection
* @param socketOptions - Socket-specific options
* @param reconnectOptions - Reconnection configuration
*/
constructor(
socketPath: string,
socketOptions?: unknown,
reconnectOptions?: Partial<ReconnectOptions>
);
/**
* Establishes socket connection
*/
connect(): void;
/**
* Closes socket connection
* @param code - Close code
* @param data - Close reason
*/
disconnect(code?: number, data?: string): void;
/**
* Safely disconnects with timeout
* @param code - Close code
* @param data - Close reason
* @param forceDisconnect - Force disconnect after timeout
* @param ms - Timeout in milliseconds
* @returns Promise that resolves when disconnected
*/
safeDisconnect(code?: number, data?: string, forceDisconnect?: boolean, ms?: number): Promise<void>;
/**
* Resets provider state and connection
*/
reset(): void;
/**
* Gets pending request queue size
* @returns Number of pending requests
*/
getPendingRequestQueueSize(): number;
/**
* Gets sent requests queue size
* @returns Number of sent requests awaiting response
*/
getSentRequestsQueueSize(): number;
/**
* Checks if provider supports subscriptions
* @returns true if subscriptions are supported
*/
supportsSubscriptions(): boolean;
/**
* Clears request queues
* @param event - Optional connection event context
*/
clearQueues(event?: ConnectionEvent): void;
/**
* Sends JSON-RPC request
* @param request - Web3 API payload
* @returns Promise resolving to JSON-RPC response
*/
request<Method, ResultType>(
request: Web3APIPayload<API, Method>
): Promise<JsonRpcResponseWithResult<ResultType>>;
// Event listener methods inherited from EventEmitter
on(event: string, listener: Function): this;
once(event: string, listener: Function): this;
removeListener(event: string, listener: Function): this;
removeAllListeners(event?: string): this;
}/**
* Configuration for automatic reconnection
*/
type ReconnectOptions = {
/**
* Whether to automatically reconnect on connection loss
*/
autoReconnect: boolean;
/**
* Delay between reconnection attempts in milliseconds
*/
delay: number;
/**
* Maximum number of reconnection attempts
*/
maxAttempts: number;
};import { SocketProvider, ReconnectOptions } from "web3-utils";
class CustomWebSocketProvider extends SocketProvider<MessageEvent, CloseEvent, ErrorEvent> {
private ws: WebSocket | null = null;
constructor(url: string, options?: WebSocketOptions, reconnectOptions?: Partial<ReconnectOptions>) {
super(url, options, reconnectOptions);
}
// Implement required abstract methods
get SocketConnection() {
return this.ws;
}
connect(): void {
if (this.ws?.readyState === WebSocket.OPEN) {
return;
}
this.ws = new WebSocket(this.socketPath);
this.ws.onopen = (event) => {
this._onConnect();
this.emit('connect');
};
this.ws.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
this.emit('message', data);
} catch (error) {
this.emit('error', error);
}
};
this.ws.onclose = (event) => {
this._onDisconnect(event.code, event.reason);
this.emit('disconnect', event.code, event.reason);
};
this.ws.onerror = (event) => {
this.emit('error', new Error('WebSocket error'));
};
}
disconnect(code?: number, data?: string): void {
if (this.ws) {
this.ws.close(code, data);
this.ws = null;
}
}
// Implement request sending
protected sendRequest(request: any): void {
if (this.ws?.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify(request));
} else {
throw new Error('WebSocket not connected');
}
}
}
// Usage
const provider = new CustomWebSocketProvider('ws://localhost:8546', {}, {
autoReconnect: true,
delay: 5000,
maxAttempts: 5
});
provider.on('connect', () => {
console.log('Connected to WebSocket');
});
provider.on('disconnect', (code, reason) => {
console.log(`Disconnected: ${code} - ${reason}`);
});
provider.on('error', (error) => {
console.error('Provider error:', error);
});
provider.connect();import { Eip1193Provider } from "web3-utils";
class CustomHttpProvider extends Eip1193Provider {
private url: string;
private headers: Record<string, string>;
constructor(url: string, options?: { headers?: Record<string, string> }) {
super();
this.url = url;
this.headers = options?.headers || {};
}
protected _onConnect(): void {
// HTTP providers are always "connected"
this.emit('connect');
}
protected _onDisconnect(code: number, data?: unknown): void {
// HTTP providers don't really disconnect
this.emit('disconnect', code, data);
}
async request<Method, ResultType>(
request: Web3APIPayload<any, Method>
): Promise<JsonRpcResponseWithResult<ResultType>> {
try {
const response = await fetch(this.url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...this.headers
},
body: JSON.stringify(request)
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const jsonResponse = await response.json();
if (jsonResponse.error) {
throw new Error(`RPC Error ${jsonResponse.error.code}: ${jsonResponse.error.message}`);
}
return jsonResponse;
} catch (error) {
this.emit('error', error);
throw error;
}
}
supportsSubscriptions(): boolean {
return false; // HTTP doesn't support subscriptions
}
}
// Usage
const httpProvider = new CustomHttpProvider('https://mainnet.infura.io/v3/YOUR_PROJECT_ID', {
headers: {
'Authorization': 'Bearer your-token'
}
});
// Make requests
const result = await httpProvider.request({
method: 'eth_getBalance',
params: ['0x742E4C5b469F50A4a8b399D4915C1fc93d15651B', 'latest']
});import { SocketProvider } from "web3-utils";
class ManagedSocketProvider extends SocketProvider<MessageEvent, CloseEvent, ErrorEvent> {
constructor(socketPath: string) {
super(socketPath, {}, {
autoReconnect: true,
delay: 2000,
maxAttempts: 10
});
// Monitor queue sizes
setInterval(() => {
const pending = this.getPendingRequestQueueSize();
const sent = this.getSentRequestsQueueSize();
if (pending > 100 || sent > 50) {
console.warn(`High queue sizes: pending=${pending}, sent=${sent}`);
}
}, 10000);
}
// Override to add queue management
async request<Method, ResultType>(
request: Web3APIPayload<any, Method>
): Promise<JsonRpcResponseWithResult<ResultType>> {
// Check queue size before adding request
if (this.getPendingRequestQueueSize() > 1000) {
throw new Error('Request queue full');
}
return super.request(request);
}
// Custom method to clear queues when needed
emergencyClearQueues(): void {
console.log('Emergency queue clear initiated');
this.clearQueues();
}
// Graceful shutdown
async shutdown(): Promise<void> {
console.log('Shutting down provider...');
// Wait for queues to empty or timeout
const maxWait = 30000; // 30 seconds
const startTime = Date.now();
while (
(this.getPendingRequestQueueSize() > 0 || this.getSentRequestsQueueSize() > 0) &&
(Date.now() - startTime) < maxWait
) {
await new Promise(resolve => setTimeout(resolve, 100));
}
await this.safeDisconnect(1000, 'Normal shutdown', true, 5000);
console.log('Provider shutdown complete');
}
}import { SocketProvider, Eip1193Provider } from "web3-utils";
type ProviderConfig = {
type: 'http' | 'websocket' | 'ipc';
url: string;
options?: any;
reconnectOptions?: Partial<ReconnectOptions>;
};
function createProvider(config: ProviderConfig): Eip1193Provider {
switch (config.type) {
case 'http':
return new CustomHttpProvider(config.url, config.options);
case 'websocket':
return new CustomWebSocketProvider(config.url, config.options, config.reconnectOptions);
case 'ipc':
return new CustomIPCProvider(config.url, config.options, config.reconnectOptions);
default:
throw new Error(`Unsupported provider type: ${config.type}`);
}
}
// Usage
const provider = createProvider({
type: 'websocket',
url: 'ws://localhost:8546',
reconnectOptions: {
autoReconnect: true,
delay: 3000,
maxAttempts: 5
}
});// Provider-related types
type ConnectionEvent = 'connect' | 'disconnect' | 'error';
interface Web3APISpec {
[method: string]: {
Parameters: any[];
ReturnType: any;
};
}
interface Web3APIPayload<API extends Web3APISpec, Method extends keyof API> {
method: Method;
params: API[Method]['Parameters'];
}
interface Web3BaseProvider<API extends Web3APISpec> {
request<Method extends keyof API>(
request: Web3APIPayload<API, Method>
): Promise<API[Method]['ReturnType']>;
}Install with Tessl CLI
npx tessl i tessl/npm-web3-utils