Skip to main content

Try it Live

Run EventLog examples in the interactive playground

    Filter Fields

    Address Filter

    Single address or array (OR logic):
    import { EventLog, Address } from 'tevm';
    
    // Single address
    const matches1 = log.matchesFilter({
      address: usdcAddress,
    });
    
    // Multiple addresses
    const matches2 = log.matchesFilter({
      address: [usdcAddress, daiAddress, wethAddress],
    });
    
    // Undefined: matches all addresses
    const matches3 = log.matchesFilter({
      topics: [TRANSFER_SIG],
    });
    

    Topics Filter

    Array with null wildcards and OR logic:
    import { EventLog, Hash } from 'tevm';
    
    const TRANSFER_SIG = Hash('0xddf252ad...');
    const userHash = Hash('0x000...user');
    
    const matches = log.matchesFilter({
      topics: [
        TRANSFER_SIG,  // topic0: exact match
        null,          // topic1: any value
        userHash,      // topic2: exact match
      ],
    });
    

    Block Range Filter

    Inclusive bounds on block number:
    import { EventLog } from 'tevm';
    
    // Blocks 18M to 18.5M (inclusive)
    const matches = log.matchesFilter({
      fromBlock: 18000000n,
      toBlock: 18500000n,
    });
    
    // Only requires log.blockNumber to be defined
    

    Block Hash Filter

    Exact block match:
    import { EventLog, Hash } from 'tevm';
    
    const blockHash = Hash('0xabc...');
    
    const matches = log.matchesFilter({
      blockHash: blockHash,
    });
    
    // Only requires log.blockHash to be defined
    

    Filter Combinations

    All specified filters use AND logic:
    import { EventLog, Address, Hash } from 'tevm';
    
    // Must match ALL conditions
    const matches = log.matchesFilter({
      address: [usdc, dai],              // AND address is USDC or DAI
      topics: [TRANSFER_SIG, null, userHash],  // AND is Transfer to user
      fromBlock: 18000000n,               // AND block >= 18M
      toBlock: 18500000n,                 // AND block <= 18.5M
    });
    

    Usage Patterns

    Complete RPC-Style Filter

    import { EventLog, Address, Hash } from 'tevm';
    
    const TRANSFER_SIG = Hash('0xddf252ad...');
    const userHash = Hash('0x000...user');
    
    // Equivalent to eth_getLogs filter
    const filtered = allLogs.filter(log =>
      log.matchesFilter({
        address: [
          Address('0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'), // USDC
          Address('0x6B175474E89094C44Da98b954EedeAC495271d0F'), // DAI
        ],
        topics: [
          TRANSFER_SIG,      // Transfer events
          null,              // from any
          userHash,          // to user
        ],
        fromBlock: 18000000n,
        toBlock: 18500000n,
      })
    );
    
    console.log(`Found ${filtered.length} matching logs`);
    

    Partial Filters

    Omit unnecessary fields:
    import { EventLog } from 'tevm';
    
    // Only address filter
    const byAddress = log.matchesFilter({
      address: tokenAddress,
    });
    
    // Only topics filter
    const byTopics = log.matchesFilter({
      topics: [TRANSFER_SIG, null, userHash],
    });
    
    // Only block range
    const byBlock = log.matchesFilter({
      fromBlock: 18000000n,
      toBlock: 18500000n,
    });
    

    Block Hash vs Block Range

    Block hash is alternative to from/to block:
    import { EventLog, Hash } from 'tevm';
    
    // Option 1: Block range
    const range = log.matchesFilter({
      fromBlock: 18000000n,
      toBlock: 18000000n,  // Same block
    });
    
    // Option 2: Block hash (more precise)
    const hash = log.matchesFilter({
      blockHash: Hash('0xabc...'),
    });
    
    // Don't mix: blockHash takes precedence
    const mixed = log.matchesFilter({
      blockHash: Hash('0xabc...'),
      fromBlock: 18000000n,  // Ignored
      toBlock: 18500000n,    // Ignored
    });
    

    Multi-Stage Filtering

    import { EventLog, Address, Hash } from 'tevm';
    
    // Stage 1: Coarse filter (address + block range)
    const coarse = allLogs.filter(log =>
      log.matchesFilter({
        address: [usdc, dai, weth],
        fromBlock: 18000000n,
        toBlock: 18500000n,
      })
    );
    
    // Stage 2: Refined filter (specific event + parameters)
    const refined = coarse.filter(log =>
      log.matchesFilter({
        topics: [TRANSFER_SIG, null, userHash],
      })
    );
    
    console.log(`Coarse: ${coarse.length}, Refined: ${refined.length}`);
    

    Validating Log Completeness

    import { EventLog } from 'tevm';
    
    function requiresBlockNumber(filter: Filter): boolean {
      return filter.fromBlock !== undefined || filter.toBlock !== undefined;
    }
    
    function requiresBlockHash(filter: Filter): boolean {
      return filter.blockHash !== undefined;
    }
    
    // Check if log has required fields
    const filter = { fromBlock: 18000000n };
    
    if (requiresBlockNumber(filter) && log.blockNumber === undefined) {
      console.error('Log missing blockNumber for filter');
    }
    

    Batch Filtering

    For multiple logs, use filterLogs instead:
    import { EventLog } from 'tevm';
    
    // Efficient: Single pass with optimizations
    const filtered = EventLog.filterLogs(allLogs, {
      address: [usdc, dai],
      topics: [TRANSFER_SIG, null, userHash],
      fromBlock: 18000000n,
      toBlock: 18500000n,
    });
    
    // Less efficient: Multiple passes
    const filtered2 = allLogs.filter(log =>
      log.matchesFilter({
        address: [usdc, dai],
        topics: [TRANSFER_SIG, null, userHash],
        fromBlock: 18000000n,
        toBlock: 18500000n,
      })
    );
    

    Missing Optional Fields

    Filter checks only apply if log has required fields:
    import { EventLog } from 'tevm';
    
    const pendingLog = EventLog({
      address: contractAddress,
      topics: [TRANSFER_SIG],
      data: Bytes(),
      // No blockNumber, blockHash, etc.
    });
    
    // Block range filter ignored (no blockNumber)
    const matches1 = pendingLog.matchesFilter({
      fromBlock: 18000000n,
      toBlock: 18500000n,
    });
    console.log(matches1); // true (block filters skipped)
    
    // Block hash filter ignored (no blockHash)
    const matches2 = pendingLog.matchesFilter({
      blockHash: Hash('0xabc...'),
    });
    console.log(matches2); // true (block hash filter skipped)
    

    See Also