Skip to main content

Utils

Tevm provides a collection of generic utilities essential for building robust Ethereum applications. These utilities handle common patterns like retry logic, rate limiting, polling, timeouts, and batch processing.

Why Utils?

Ethereum applications face unique challenges:
  • Transient failures: RPC nodes may be temporarily unavailable or rate-limited
  • Async operations: Transactions and state changes require polling for confirmation
  • Rate limits: Public RPC endpoints enforce strict rate limits
  • Long operations: Block production and transaction confirmation take time
  • Batch optimization: Multiple similar requests can be batched for efficiency
These utilities provide battle-tested solutions to these challenges.

Available Utilities

Retry with Exponential Backoff

Automatically retry failed operations with exponential backoff and jitter.
import { retryWithBackoff } from '@tevm/voltaire/utils';

const blockNumber = await retryWithBackoff(
  () => provider.eth_blockNumber(),
  {
    maxRetries: 5,
    initialDelay: 1000,
    factor: 2,
    jitter: true
  }
);
Learn more →

Rate Limiting

Throttle, debounce, and rate-limit function calls.
import { RateLimiter } from '@tevm/voltaire/utils';

const limiter = new RateLimiter({
  maxRequests: 10,
  interval: 1000,
  strategy: 'queue'
});

const balance = await limiter.execute(
  () => provider.eth_getBalance(address)
);
Learn more →

Polling

Poll operations until they succeed or timeout, with optional backoff.
import { pollForReceipt } from '@tevm/voltaire/utils';

const receipt = await pollForReceipt(
  txHash,
  (hash) => provider.eth_getTransactionReceipt(hash),
  { timeout: 120000 }
);
Learn more →

Timeout

Add timeouts to promises and async operations.
import { withTimeout } from '@tevm/voltaire/utils';

const result = await withTimeout(
  provider.eth_call({ to, data }),
  { ms: 5000, message: 'Call timeout' }
);
Learn more →

Batch Processing

Automatically batch similar operations for efficiency.
import { BatchQueue } from '@tevm/voltaire/utils';

const queue = new BatchQueue({
  maxBatchSize: 50,
  maxWaitTime: 100,
  processBatch: async (addresses) => {
    return Promise.all(
      addresses.map(addr => provider.eth_getBalance(addr))
    );
  }
});

const balance = await queue.add(address);
Learn more →

Installation

Utils are included with Tevm. No additional dependencies required.
npm install @tevm/voltaire
# or
bun add @tevm/voltaire

Design Principles

Vanilla TypeScript

All utilities use standard Promise-based APIs. No framework dependencies.

Type Safe

Full TypeScript support with generic types for maximum type safety.

Tree Shakeable

Import only what you need. Each utility is independently importable.

Zero Runtime Overhead

Minimal abstractions. Direct, efficient implementations.

Common Patterns

Resilient RPC Calls

Combine retry and timeout for robust RPC calls:
import { retryWithBackoff, withTimeout } from '@tevm/voltaire/utils';

const resilientCall = async <T>(fn: () => Promise<T>): Promise<T> => {
  return retryWithBackoff(
    () => withTimeout(fn(), { ms: 10000 }),
    {
      maxRetries: 3,
      shouldRetry: (error) => {
        // Don't retry on timeout
        return error.name !== 'TimeoutError';
      }
    }
  );
};

const blockNumber = await resilientCall(
  () => provider.eth_blockNumber()
);

Rate-Limited Batch Processing

Combine rate limiting and batching:
import { RateLimiter, BatchQueue } from '@tevm/voltaire/utils';

const limiter = new RateLimiter({
  maxRequests: 10,
  interval: 1000
});

const queue = new BatchQueue({
  maxBatchSize: 50,
  maxWaitTime: 100,
  processBatch: async (addresses) => {
    return limiter.execute(() =>
      Promise.all(
        addresses.map(addr => provider.eth_getBalance(addr))
      )
    );
  }
});

Poll with Timeout and Retry

Combine polling, timeout, and retry:
import { poll, withTimeout, retryWithBackoff } from '@tevm/voltaire/utils';

const waitForBlock = async (blockNumber: bigint): Promise<Block> => {
  return withTimeout(
    poll(
      () => retryWithBackoff(
        () => provider.eth_getBlockByNumber(blockNumber)
      ),
      {
        interval: 1000,
        validate: (block) => block !== null
      }
    ),
    { ms: 60000 }
  );
};

Next Steps