Skip to main content

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: []
  };
}