Skip to content
Logo

Ethereum

The Ethereum protocol variants provide pre-configured transports for communicating with Ethereum JSON-RPC nodes (Geth, Erigon, Infura, Alchemy, public nodes, etc.).

Overview

Base transports (@rpckit/websocket, @rpckit/http) are protocol-agnostic. The Ethereum subpath variants add:

  • Subscription Routing - eth_subscription notifications are routed to the correct callback by subscription ID
  • Automatic Cleanup - eth_unsubscribe is called automatically when unsubscribing
  • ID Suppression - Subscription IDs are handled internally and not passed to callbacks

Installation

npm i @rpckit/core @rpckit/websocket @rpckit/http

Usage

Import from the /ethereum subpath:

import { webSocket } from '@rpckit/websocket/ethereum'
import { http } from '@rpckit/http/ethereum'

WebSocket

import type { EthereumSchema } from '@rpckit/core/ethereum'
import { webSocket } from '@rpckit/websocket/ethereum'
 
const transport = webSocket<EthereumSchema>('wss://ethereum-rpc.publicnode.com', {
  timeout: 30000,
})
 
const blockNumber = await transport.request('eth_blockNumber')
console.log(blockNumber) // '0x...'
 
const balance = await transport.request(
  'eth_getBalance',
  '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
  'latest'
)

HTTP

import { http } from '@rpckit/http/ethereum'
 
const transport = http('https://ethereum-rpc.publicnode.com', {
  timeout: 30000,
})
 
const chainId = await transport.request('eth_chainId')
const gasPrice = await transport.request('eth_gasPrice')

Subscriptions

Ethereum subscriptions use the eth_subscribe method with a subscription type as the first parameter. The Ethereum variant handles the subscription ID routing automatically:

New Blocks

import { webSocket } from '@rpckit/websocket/ethereum'
 
const transport = webSocket('wss://ethereum-rpc.publicnode.com')
 
const unsub = await transport.subscribe(
  'eth_subscribe',
  'newHeads',
  (header) => {
    console.log('New block:', header.number, header.hash)
  }
)
 
// Later: unsubscribe (calls eth_unsubscribe automatically)
await unsub()

Pending Transactions

const unsub = await transport.subscribe(
  'eth_subscribe',
  'newPendingTransactions',
  (txHash) => {
    console.log('Pending tx:', txHash)
  }
)

Log Events

Subscribe to contract events with filters:

// Subscribe to USDC Transfer events
const USDC_ADDRESS = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'
const TRANSFER_TOPIC = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'
 
const unsub = await transport.subscribe(
  'eth_subscribe',
  'logs',
  {
    address: USDC_ADDRESS,
    topics: [TRANSFER_TOPIC]
  },
  (log) => {
    console.log('Transfer:', {
      from: log.topics[1],
      to: log.topics[2],
      block: log.blockNumber
    })
  }
)

Syncing Status

const unsub = await transport.subscribe(
  'eth_subscribe',
  'syncing',
  (status) => {
    if (status === false) {
      console.log('Node is synced')
    } else {
      console.log('Syncing:', status.currentBlock, '/', status.highestBlock)
    }
  }
)

Type Safety

Use EthereumSchema for full type inference:

import type { EthereumSchema } from '@rpckit/core/ethereum'
import { webSocket } from '@rpckit/websocket/ethereum'
 
const transport = webSocket<EthereumSchema>('wss://...')
 
// TypeScript knows the return types
const chainId = await transport.request('eth_chainId')        // string
const blockNumber = await transport.request('eth_blockNumber') // string
const gasPrice = await transport.request('eth_gasPrice')       // string
 
// TypeScript validates parameters
const balance = await transport.request(
  'eth_getBalance',
  '0x...', // address
  'latest' // block tag
)
 
const block = await transport.request(
  'eth_getBlockByNumber',
  'latest',
  false // include transactions?
)

Subscription ID Handling

Unlike the base WebSocket transport, the Ethereum variant handles subscription IDs internally:

// Base WebSocket - you receive the subscription ID
const unsub = await baseTransport.subscribe('eth_subscribe', 'newHeads', (data) => {
  // First call: data is the subscription ID ('0x...')
  // Subsequent calls: data is the notification
})
 
// Ethereum variant - subscription ID is hidden
const unsub = await ethereumTransport.subscribe('eth_subscribe', 'newHeads', (header) => {
  // Every call: header is the block header object
  // Subscription ID is managed internally
})

URL Parsing

Use the Ethereum-specific parse function:

import { parse } from '@rpckit/core/ethereum'
 
// Creates ethereum variant transports
const ws = await parse('wss://ethereum-rpc.publicnode.com?timeout=30000')
const http = await parse('https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY')
 
// Fallback
const fb = await parse('fallback(wss://node1.example.com,wss://node2.example.com)')

Common Methods

The Ethereum JSON-RPC API includes:

CategoryMethods
Chaineth_chainId, eth_blockNumber, eth_gasPrice, eth_feeHistory
Accountseth_getBalance, eth_getTransactionCount, eth_getCode, eth_getStorageAt
Blockseth_getBlockByNumber, eth_getBlockByHash, eth_getBlockReceipts
Transactionseth_getTransactionByHash, eth_getTransactionReceipt, eth_sendRawTransaction
Callseth_call, eth_estimateGas, eth_createAccessList
Logseth_getLogs, eth_newFilter, eth_getFilterChanges
Subscriptionseth_subscribe (newHeads, logs, newPendingTransactions, syncing)
Networknet_version, net_listening, net_peerCount
Debugdebug_traceTransaction, debug_traceCall, debug_traceBlockByNumber

See the Ethereum JSON-RPC Specification for the complete method list.

Example: Monitor Token Transfers

import type { EthereumSchema } from '@rpckit/core/ethereum'
import { webSocket } from '@rpckit/websocket/ethereum'
 
const transport = webSocket<EthereumSchema>('wss://ethereum-rpc.publicnode.com')
 
// ERC-20 Transfer event signature
const TRANSFER_SIG = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'
 
// Token addresses
const TOKENS = {
  USDC: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
  USDT: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
  DAI: '0x6B175474E89094C44Da98b954EescdeCB5BE3830',
}
 
// Subscribe to transfers for multiple tokens
const unsub = await transport.subscribe(
  'eth_subscribe',
  'logs',
  {
    address: Object.values(TOKENS),
    topics: [TRANSFER_SIG]
  },
  (log) => {
    const token = Object.entries(TOKENS).find(
      ([, addr]) => addr.toLowerCase() === log.address.toLowerCase()
    )?.[0] ?? 'Unknown'
 
    console.log(`${token} transfer in block ${log.blockNumber}`)
  }
)
 
// Cleanup on shutdown
process.on('SIGINT', async () => {
  await unsub()
  await transport.close()
})