Skip to main content

Try it Live

Run EventLog examples in the interactive playground

    Filter Semantics

    Null Wildcard

    null at any position matches any topic value:
    import { EventLog, Hash } from 'tevm';
    
    const TRANSFER_SIG = Hash('0xddf252ad...');
    
    // Match Transfer events with any from/to addresses
    const matches = log.matchesTopics([
      TRANSFER_SIG,  // topic0 must be Transfer signature
      null,          // topic1 can be any address
      null,          // topic2 can be any address
    ]);
    

    Single Hash Match

    Specific hash must match exactly at that position:
    import { EventLog, Hash } from 'tevm';
    
    const TRANSFER_SIG = Hash('0xddf252ad...');
    const userHash = Hash('0x000000000000000000000000a1b2c3d4...');
    
    // Match Transfer events TO specific user
    const matches = log.matchesTopics([
      TRANSFER_SIG,  // Exact signature
      null,          // from: any
      userHash,      // to: specific user
    ]);
    

    Array of Hashes (OR Logic)

    Matches if log topic equals ANY hash in the array:
    import { EventLog, Hash } from 'tevm';
    
    const TRANSFER_SIG = Hash('0xddf252ad...');
    const APPROVAL_SIG = Hash('0x8c5be1e5...');
    
    // Match Transfer OR Approval events
    const matches = log.matchesTopics([
      [TRANSFER_SIG, APPROVAL_SIG],  // topic0 can be either
    ]);
    

    Empty Filter Array

    Empty filter matches all logs:
    import { EventLog } from 'tevm';
    
    // Matches all logs
    const matches = log.matchesTopics([]);
    console.log(matches); // true
    

    Usage Patterns

    ERC-20 Transfer Filtering

    import { EventLog, Address, Hash } from 'tevm';
    
    // Transfer(address indexed from, address indexed to, uint256 value)
    const TRANSFER_SIG = Hash('0xddf252ad...');
    const userAddress = Address('0xa1b2c3d4...');
    const userHash = Hash(
      new Uint8Array([...new Uint8Array(12).fill(0), ...userAddress])
    );
    
    // All transfers FROM user
    const outgoing = allLogs.filter(log =>
      log.matchesTopics([TRANSFER_SIG, userHash, null])
    );
    
    // All transfers TO user
    const incoming = allLogs.filter(log =>
      log.matchesTopics([TRANSFER_SIG, null, userHash])
    );
    
    // All transfers involving user (from OR to)
    const userTransfers = allLogs.filter(log =>
      log.matchesTopics([TRANSFER_SIG, userHash, null]) ||
      log.matchesTopics([TRANSFER_SIG, null, userHash])
    );
    
    console.log(`Outgoing: ${outgoing.length}`);
    console.log(`Incoming: ${incoming.length}`);
    console.log(`Total: ${userTransfers.length}`);
    

    Multiple Event Types

    import { EventLog, Hash } from 'tevm';
    
    const TRANSFER_SIG = Hash('0xddf252ad...');
    const APPROVAL_SIG = Hash('0x8c5be1e5...');
    const SWAP_SIG = Hash('0xd78ad95f...');
    
    // Match any of these event types
    const relevantLogs = allLogs.filter(log =>
      log.matchesTopics([[TRANSFER_SIG, APPROVAL_SIG, SWAP_SIG]])
    );
    
    console.log(`Found ${relevantLogs.length} relevant events`);
    

    Intra-Group Transfers

    import { EventLog, Hash } from 'tevm';
    
    const TRANSFER_SIG = Hash('0xddf252ad...');
    
    // Addresses in the group
    const groupHashes = [
      Hash('0x000...addr1'),
      Hash('0x000...addr2'),
      Hash('0x000...addr3'),
    ];
    
    // Transfers between any group members
    const intraGroupTransfers = allLogs.filter(log =>
      log.matchesTopics([
        TRANSFER_SIG,
        groupHashes,  // from: any group member
        groupHashes,  // to: any group member
      ])
    );
    
    console.log(`Intra-group transfers: ${intraGroupTransfers.length}`);
    

    DEX Swap Monitoring

    import { EventLog, Hash } from 'tevm';
    
    // Uniswap V2 Swap(address indexed sender, uint amount0In, uint amount1In, uint amount0Out, uint amount1Out, address indexed to)
    const SWAP_SIG = Hash('0xd78ad95f...');
    const routerHash = Hash('0x000...router');
    
    // Swaps initiated by router
    const routerSwaps = allLogs.filter(log =>
      log.matchesTopics([SWAP_SIG, routerHash])
    );
    

    NFT Transfer Tracking

    import { EventLog, Hash } from 'tevm';
    
    // Transfer(address indexed from, address indexed to, uint256 indexed tokenId)
    const TRANSFER_SIG = Hash('0xddf252ad...');
    const zeroHash = Hash('0x0000000000000000000000000000000000000000000000000000000000000000');
    
    // NFT mints (from zero address)
    const mints = allLogs.filter(log =>
      log.matchesTopics([TRANSFER_SIG, zeroHash, null])
    );
    
    // NFT burns (to zero address)
    const burns = allLogs.filter(log =>
      log.matchesTopics([TRANSFER_SIG, null, zeroHash])
    );
    
    console.log(`Mints: ${mints.length}`);
    console.log(`Burns: ${burns.length}`);
    

    Anonymous Event Handling

    import { EventLog } from 'tevm';
    
    // Anonymous events: topic0 is first indexed param, not signature
    const anonymousLog = EventLog({
      address: contractAddress,
      topics: [param1Hash, param2Hash, param3Hash],
      data: Bytes(),
    });
    
    // Match by first indexed parameter (NOT signature)
    const matches = anonymousLog.matchesTopics([
      param1Hash,  // topic0 is first param
      null,        // topic1 is second param (any)
      param3Hash,  // topic2 is third param
    ]);
    

    Advanced Patterns

    Complex Boolean Logic

    import { EventLog, Hash } from 'tevm';
    
    const TRANSFER_SIG = Hash('0xddf252ad...');
    const user1Hash = Hash('0x000...user1');
    const user2Hash = Hash('0x000...user2');
    const user3Hash = Hash('0x000...user3');
    
    // (from:user1 AND to:user2) OR (from:user2 AND to:user3)
    const complexFilter = allLogs.filter(log =>
      (log.matchesTopics([TRANSFER_SIG, user1Hash, user2Hash]) ||
       log.matchesTopics([TRANSFER_SIG, user2Hash, user3Hash]))
    );
    

    Partial Filter (Fewer Topics)

    Filter can have fewer topics than log:
    import { EventLog, Hash } from 'tevm';
    
    const log = EventLog({
      address: contractAddress,
      topics: [topic0, topic1, topic2, topic3], // 4 topics
      data: Bytes(),
    });
    
    // Match only first 2 topics
    const matches = log.matchesTopics([topic0, topic1]);
    console.log(matches); // true (remaining topics ignored)
    

    Empty Topics Check

    import { EventLog } from 'tevm';
    
    const log = EventLog({
      address: contractAddress,
      topics: [], // No topics
      data: Bytes(),
    });
    
    // Filter with topics fails
    const matches1 = log.matchesTopics([Hash('0x...')]);
    console.log(matches1); // false
    
    // Empty filter matches
    const matches2 = log.matchesTopics([]);
    console.log(matches2); // true
    

    Performance

    Uses constant-time hash comparison for security:
    import { EventLog } from 'tevm';
    
    // Comparison time doesn't leak which topics match
    const matches = log.matchesTopics([sig, null, userHash]);
    
    For large datasets, use filterLogs for optimized batch filtering:
    import { EventLog } from 'tevm';
    
    // Efficient batch filtering
    const filtered = EventLog.filterLogs(allLogs, {
      topics: [TRANSFER_SIG, null, userHash],
    });
    

    See Also