Skip to main content

Documentation Index

Fetch the complete documentation index at: https://voltaire.tevm.sh/llms.txt

Use this file to discover all available pages before exploring further.

Try it Live

Run Chain examples in the interactive playground

Usage Patterns

Practical patterns for chain ID validation, network detection, and multi-chain applications.

Chain Identification

Network Detection

import * as Chain from 'tevm/Chain';

// Detect network from chain ID
function getNetworkName(chainId: BrandedChain): string {
  const metadata = Chain.getMetadata(chainId);
  return metadata?.name ?? `Unknown (${chainId})`;
}

// Check if mainnet
function isMainnet(chainId: BrandedChain): boolean {
  return Chain.equals(chainId, Chain(1));
}

// Check if testnet
function isTestnet(chainId: BrandedChain): boolean {
  const testnets = [5, 11155111, 17000]; // Goerli, Sepolia, Holesky
  return testnets.some(id => Chain.equals(chainId, Chain(id)));
}

Multi-chain Support

// Supported chains configuration
const SUPPORTED_CHAINS = [
  1,      // Ethereum Mainnet
  10,     // Optimism
  137,    // Polygon
  8453,   // Base
  42161,  // Arbitrum One
] as const;

function isSupportedChain(chainId: BrandedChain): boolean {
  return SUPPORTED_CHAINS.some(id =>
    Chain.equals(chainId, Chain(id))
  );
}

function requireSupportedChain(chainId: BrandedChain): void {
  if (!isSupportedChain(chainId)) {
    const supported = SUPPORTED_CHAINS.join(', ');
    throw new Error(
      `Unsupported chain ${chainId}. Supported: ${supported}`
    );
  }
}

Provider Configuration

Dynamic Provider Setup

interface ProviderConfig {
  rpcUrl: string;
  chainId: BrandedChain;
  name: string;
}

// Configure provider based on chain
function getProviderConfig(chainId: BrandedChain): ProviderConfig {
  const configs: Record<number, ProviderConfig> = {
    1: {
      rpcUrl: "https://eth.llamarpc.com",
      chainId: Chain(1),
      name: "Ethereum Mainnet"
    },
    137: {
      rpcUrl: "https://polygon.llamarpc.com",
      chainId: Chain(137),
      name: "Polygon"
    },
    // ... more chains
  };

  const config = configs[Number(chainId)];
  if (!config) {
    throw new Error(`No provider config for chain ${chainId}`);
  }

  return config;
}

// Create provider
async function createProvider(chainId: BrandedChain): Promise<Provider> {
  const config = getProviderConfig(chainId);
  const provider = new Provider(config.rpcUrl);

  // Verify chain ID
  const actualChainId = await provider.getChainId();
  if (!Chain.equals(Chain(actualChainId), chainId)) {
    throw new Error(
      `Chain ID mismatch. Expected: ${chainId}, Got: ${actualChainId}`
    );
  }

  return provider;
}

Chain-specific Configuration

// Get block explorer URL
function getExplorerUrl(chainId: BrandedChain): string {
  const explorers: Record<number, string> = {
    1: "https://etherscan.io",
    10: "https://optimistic.etherscan.io",
    137: "https://polygonscan.com",
    8453: "https://basescan.org",
    42161: "https://arbiscan.io"
  };

  return explorers[Number(chainId)] ?? "";
}

// Get native token symbol
function getNativeToken(chainId: BrandedChain): string {
  const tokens: Record<number, string> = {
    1: "ETH",
    137: "MATIC",
    56: "BNB",
    43114: "AVAX"
  };

  return tokens[Number(chainId)] ?? "ETH";
}

Transaction Routing

Chain-aware Transaction Builder

// Build transaction for specific chain
function buildTransaction(
  chainId: BrandedChain,
  params: {
    to: string;
    value: bigint;
    data: Uint8Array;
  }
): Transaction {
  const metadata = Chain.getMetadata(chainId);

  // Chain-specific gas settings
  const gasSettings = getGasSettings(chainId);

  return Transaction({
    chainId,
    to: params.to,
    value: params.value,
    data: params.data,
    ...gasSettings
  });
}

// Get chain-specific gas settings
function getGasSettings(chainId: BrandedChain): {
  maxFeePerGas?: bigint;
  maxPriorityFeePerGas?: bigint;
  gasPrice?: bigint;
} {
  // Use EIP-1559 for chains that support it
  const eip1559Chains = [1, 10, 137, 8453, 42161];

  if (eip1559Chains.some(id => Chain.equals(chainId, Chain(id)))) {
    return {
      maxFeePerGas: 50_000_000_000n,
      maxPriorityFeePerGas: 2_000_000_000n
    };
  }

  // Legacy gas price for others
  return {
    gasPrice: 20_000_000_000n
  };
}

Cross-chain Message Routing

interface CrossChainMessage {
  sourceChain: BrandedChain;
  targetChain: BrandedChain;
  data: Uint8Array;
}

// Route message between chains
async function routeCrossChainMessage(
  msg: CrossChainMessage
): Promise<void> {
  // Validate chains
  requireSupportedChain(msg.sourceChain);
  requireSupportedChain(msg.targetChain);

  // Get bridge contract for chain pair
  const bridge = getBridgeContract(msg.sourceChain, msg.targetChain);

  // Submit message
  await bridge.sendMessage(msg.data);
}

Validation

Input Validation

// Validate chain ID from user input
function validateChainId(input: string | number): BrandedChain {
  let chainId: number;

  if (typeof input === 'string') {
    chainId = parseInt(input, 10);
    if (isNaN(chainId)) {
      throw new Error(`Invalid chain ID: ${input}`);
    }
  } else {
    chainId = input;
  }

  if (chainId < 1 || chainId > 4294967295) {  // uint32 max
    throw new Error(`Chain ID out of range: ${chainId}`);
  }

  return Chain(chainId);
}

// Safe chain ID parsing
function safeParseChainId(input: unknown): BrandedChain | null {
  try {
    if (typeof input === 'number') {
      return Chain(input);
    }
    if (typeof input === 'string') {
      return validateChainId(input);
    }
    return null;
  } catch {
    return null;
  }
}

Transaction Validation

// Verify transaction chain ID
function verifyTransactionChain(
  tx: Transaction,
  expectedChainId: BrandedChain
): void {
  if (!Chain.equals(tx.chainId, expectedChainId)) {
    throw new Error(
      `Transaction chain ID mismatch. ` +
      `Expected: ${expectedChainId}, Got: ${tx.chainId}`
    );
  }
}

// Validate replay protection
function hasReplayProtection(tx: Transaction): boolean {
  // EIP-155 replay protection requires chain ID
  return tx.chainId !== 0n;
}

Metadata Access

Chain Information Display

// Display chain information
function displayChainInfo(chainId: BrandedChain): void {
  const metadata = Chain.getMetadata(chainId);

  if (metadata) {
    console.log(`Chain: ${metadata.name}`);
    console.log(`ID: ${chainId}`);
    console.log(`Native Token: ${metadata.nativeCurrency.symbol}`);
    console.log(`RPC: ${metadata.rpcUrls[0]}`);
    console.log(`Explorer: ${metadata.blockExplorers?.[0]}`);
  } else {
    console.log(`Chain ID: ${chainId} (unknown network)`);
  }
}

// Get chain selector for UI
function getChainSelector(): Array<{
  value: number;
  label: string;
  icon: string;
}> {
  return SUPPORTED_CHAINS.map(id => {
    const chainId = Chain(id);
    const metadata = Chain.getMetadata(chainId);

    return {
      value: id,
      label: metadata?.name ?? `Chain ${id}`,
      icon: metadata?.icon ?? ""
    };
  });
}

Testing

Mock Chain IDs

// Test chains
const TEST_CHAINS = {
  mainnet: Chain(1),
  sepolia: Chain(11155111),
  hardhat: Chain(31337),
  ganache: Chain(1337)
};

// Use in tests
function setupTestProvider(network: keyof typeof TEST_CHAINS): Provider {
  const chainId = TEST_CHAINS[network];
  return createTestProvider(chainId);
}

Chain Mocking

// Mock chain metadata for tests
function mockChainMetadata(chainId: number) {
  return {
    chainId: Chain(chainId),
    name: `Test Chain ${chainId}`,
    nativeCurrency: {
      name: "Test Ether",
      symbol: "TEST",
      decimals: 18
    },
    rpcUrls: [`http://localhost:8545`],
    blockExplorers: []
  };
}