Skip to main content

Try it Live

Run EventLog examples in the interactive playground

    Sort Order

    Primary: Block Number

    Logs sorted by block number ascending (earliest blocks first):
    import { EventLog } from 'tevm';
    
    const sorted = EventLog.sortLogs([
      { blockNumber: 18000003n, logIndex: 0 },
      { blockNumber: 18000001n, logIndex: 0 },
      { blockNumber: 18000002n, logIndex: 0 },
    ]);
    
    // Result:
    // 1. blockNumber: 18000001n
    // 2. blockNumber: 18000002n
    // 3. blockNumber: 18000003n
    

    Secondary: Log Index

    Within same block, sorted by log index ascending:
    import { EventLog } from 'tevm';
    
    const sorted = EventLog.sortLogs([
      { blockNumber: 18000000n, logIndex: 10 },
      { blockNumber: 18000000n, logIndex: 2 },
      { blockNumber: 18000000n, logIndex: 5 },
    ]);
    
    // Result:
    // 1. logIndex: 2
    // 2. logIndex: 5
    // 3. logIndex: 10
    

    Missing Values

    Logs without blockNumber or logIndex treated as 0:
    import { EventLog } from 'tevm';
    
    const sorted = EventLog.sortLogs([
      { blockNumber: 100n, logIndex: 5 },
      { /* no blockNumber, no logIndex */ },
      { blockNumber: 50n, logIndex: 2 },
    ]);
    
    // Result:
    // 1. No blockNumber (treated as 0)
    // 2. blockNumber: 50n, logIndex: 2
    // 3. blockNumber: 100n, logIndex: 5
    

    Usage Patterns

    Chronological Processing

    import { EventLog } from 'tevm';
    
    // Sort before processing
    const sorted = EventLog.sortLogs(allLogs);
    
    for (const log of sorted) {
      console.log(`Block ${log.blockNumber}, Log ${log.logIndex}`);
      // Process in blockchain order
    }
    

    Combining with Filtering

    import { EventLog, Address, Hash } from 'tevm';
    
    const TRANSFER_SIG = Hash('0xddf252ad...');
    
    // Filter then sort
    const transfers = EventLog.filterLogs(allLogs, {
      address: usdcAddress,
      topics: [TRANSFER_SIG],
    });
    
    const sorted = EventLog.sortLogs(transfers);
    
    console.log(`Found ${sorted.length} transfers in chronological order`);
    

    Finding Latest Log

    import { EventLog } from 'tevm';
    
    const sorted = EventLog.sortLogs(logs);
    const latest = sorted[sorted.length - 1];
    
    if (latest) {
      console.log(`Latest log: block ${latest.blockNumber}, index ${latest.logIndex}`);
    }
    

    Finding Earliest Log

    import { EventLog } from 'tevm';
    
    const sorted = EventLog.sortLogs(logs);
    const earliest = sorted[0];
    
    if (earliest) {
      console.log(`Earliest log: block ${earliest.blockNumber}, index ${earliest.logIndex}`);
    }
    

    Grouping by Block

    import { EventLog } from 'tevm';
    
    const sorted = EventLog.sortLogs(allLogs);
    
    // Group by block
    const byBlock = new Map<bigint, EventLog[]>();
    
    for (const log of sorted) {
      const blockNum = log.blockNumber ?? 0n;
      if (!byBlock.has(blockNum)) {
        byBlock.set(blockNum, []);
      }
      byBlock.get(blockNum)!.push(log);
    }
    
    // Process each block's logs (already sorted by logIndex)
    for (const [blockNumber, blockLogs] of byBlock) {
      console.log(`\nBlock ${blockNumber}: ${blockLogs.length} logs`);
      for (const log of blockLogs) {
        console.log(`  Log ${log.logIndex}: ${Address.toHex(log.address)}`);
      }
    }
    

    Time-Ordered Event Replay

    import { EventLog } from 'tevm';
    
    function replayEvents(logs: EventLog[]): void {
      const sorted = EventLog.sortLogs(logs);
    
      let currentBlock = 0n;
      for (const log of sorted) {
        if (log.blockNumber && log.blockNumber > currentBlock) {
          console.log(`\n=== Block ${log.blockNumber} ===`);
          currentBlock = log.blockNumber;
        }
    
        // Decode and replay event
        const sig = log.getTopic0();
        if (sig) {
          console.log(`  Event: ${Hash.toHex(sig).slice(0, 10)}...`);
          // ... decode and apply state changes
        }
      }
    }
    
    replayEvents(allLogs);
    

    Detecting Missing Logs

    import { EventLog } from 'tevm';
    
    function detectGaps(logs: EventLog[]): void {
      const sorted = EventLog.sortLogs(logs);
    
      for (let i = 1; i < sorted.length; i++) {
        const prev = sorted[i - 1];
        const curr = sorted[i];
    
        const prevBlock = prev.blockNumber ?? 0n;
        const currBlock = curr.blockNumber ?? 0n;
    
        if (currBlock === prevBlock) {
          // Same block: check log index
          const prevIndex = prev.logIndex ?? 0;
          const currIndex = curr.logIndex ?? 0;
    
          if (currIndex > prevIndex + 1) {
            console.log(`Gap in block ${currBlock}: missing log indices ${prevIndex + 1} to ${currIndex - 1}`);
          }
        }
      }
    }
    

    Pagination

    import { EventLog } from 'tevm';
    
    function paginateLogs(logs: EventLog[], page: number, pageSize: number) {
      const sorted = EventLog.sortLogs(logs);
      const start = page * pageSize;
      const end = start + pageSize;
    
      return {
        logs: sorted.slice(start, end),
        total: sorted.length,
        page,
        pageSize,
        hasMore: end < sorted.length,
        hasLess: start > 0,
      };
    }
    
    const result = paginateLogs(allLogs, 0, 100);
    console.log(`Page 1: ${result.logs.length} logs`);
    console.log(`Total: ${result.total} logs`);
    console.log(`Has more: ${result.hasMore}`);
    

    Implementation

    Sort algorithm:
    1. Compare block numbers (ascending)
    2. If equal, compare log indices (ascending)
    3. Treat undefined as 0
    // Equivalent implementation
    function sortLogs(logs: readonly EventLog[]): EventLog[] {
      return [...logs].sort((a, b) => {
        const blockA = a.blockNumber ?? 0n;
        const blockB = b.blockNumber ?? 0n;
    
        if (blockA !== blockB) {
          return blockA < blockB ? -1 : 1;
        }
    
        const indexA = a.logIndex ?? 0;
        const indexB = b.logIndex ?? 0;
        return indexA - indexB;
      });
    }
    

    Performance

    • Time complexity: O(n log n)
    • Space complexity: O(n) (creates new array)
    • Stable sort: Maintains relative order of equal elements
    For large arrays, sort once and reuse:
    import { EventLog } from 'tevm';
    
    // Sort once
    const sorted = EventLog.sortLogs(allLogs);
    
    // Reuse sorted array for multiple operations
    const first100 = sorted.slice(0, 100);
    const last100 = sorted.slice(-100);
    const middle = sorted.slice(1000, 2000);
    

    See Also