Skip to main content
Looking for Contributors! This Skill needs an implementation.Contributing a Skill involves:
  1. Writing a reference implementation with full functionality
  2. Adding comprehensive tests
  3. Writing documentation with usage examples
See the ethers-provider Skill for an example of a complete Skill implementation.Interested? Open an issue or PR at github.com/evmts/voltaire.
Skill — Copyable reference implementation. Use as-is or customize. See Skills Philosophy.
Real-time Ethereum subscriptions over WebSocket. Subscribe to new blocks, pending transactions, and log events without polling.

Why WebSocket?

HTTP providers poll for updates. WebSocket providers receive push notifications:
// HTTP - polling (inefficient)
setInterval(async () => {
  const block = await httpProvider.getBlockNumber();
}, 12000);

// WebSocket - push (efficient)
wsProvider.subscribe('newHeads', (block) => {
  console.log('New block:', block.number);
});
WebSocket is essential for:
  • Real-time trading UIs
  • Live transaction feeds
  • Instant event notifications
  • Mempool monitoring

Planned Implementation

Basic Subscriptions

const provider = WebSocketProvider('wss://eth-mainnet.g.alchemy.com/v2/KEY');

// Subscribe to new blocks
const unsubBlocks = await provider.subscribe('newHeads', (block) => {
  console.log('Block:', block.number, 'Hash:', block.hash);
});

// Subscribe to pending transactions
const unsubPending = await provider.subscribe('newPendingTransactions', (txHash) => {
  console.log('Pending tx:', txHash);
});

// Subscribe to logs (events)
const unsubLogs = await provider.subscribe('logs', {
  address: USDC_ADDRESS,
  topics: [TRANSFER_TOPIC]
}, (log) => {
  console.log('Transfer:', log);
});

// Cleanup
unsubBlocks();
unsubPending();
unsubLogs();

Async Iterator Pattern

// Use for-await for cleaner code
for await (const block of provider.blocks()) {
  console.log('Block:', block.number);
  if (shouldStop) break;
}

// Filter specific events
for await (const log of provider.logs({ address: USDC })) {
  const decoded = decodeTransferLog(log);
  console.log(`${decoded.from} -> ${decoded.to}: ${decoded.value}`);
}

Reconnection Handling

const provider = WebSocketProvider({
  url: 'wss://...',
  reconnect: {
    auto: true,
    maxAttempts: 10,
    delay: 1000,
    maxDelay: 30000,
  },
  onReconnect: () => {
    console.log('Reconnected, resubscribing...');
  },
  onDisconnect: (error) => {
    console.log('Disconnected:', error);
  }
});

Subscription Management

// Get active subscriptions
const subs = provider.getSubscriptions();
// [{ id: '0x1', type: 'newHeads' }, ...]

// Unsubscribe all
await provider.unsubscribeAll();

// Check connection status
provider.isConnected(); // true/false

Use Cases

Live Price Feed

const provider = WebSocketProvider('wss://...');

// Watch Uniswap V3 pool for price updates
await provider.subscribe('logs', {
  address: UNISWAP_POOL,
  topics: [SWAP_TOPIC]
}, (log) => {
  const { sqrtPriceX96 } = decodeSwapLog(log);
  const price = calculatePrice(sqrtPriceX96);
  updatePriceDisplay(price);
});

Transaction Confirmation

async function waitForConfirmation(txHash, confirmations = 1) {
  let confirmedBlock = null;

  for await (const block of provider.blocks()) {
    if (!confirmedBlock) {
      const receipt = await provider.getTransactionReceipt(txHash);
      if (receipt) confirmedBlock = receipt.blockNumber;
    }

    if (confirmedBlock && block.number >= confirmedBlock + confirmations) {
      return { confirmed: true, confirmations };
    }
  }
}

Mempool Monitoring

// Watch for pending transactions to specific address
await provider.subscribe('newPendingTransactions', async (txHash) => {
  const tx = await provider.getTransaction(txHash);
  if (tx?.to === MY_CONTRACT) {
    console.log('Incoming tx:', tx);
  }
});