Skip to main content
TypeScript-first: In Zig, build JSON payloads with std.json and POST via std.http.Client. Use primitives.EventSignature and primitives.EventLog.parseEventLog to work with topics and decode logs.
Query historical event logs and create filters to monitor new blocks, transactions, and events.

Log Query

eth_getLogs

Query event logs matching filter criteria.
// {"method":"eth_getLogs","params":[{"fromBlock":"earliest","toBlock":"latest","address":"0x...","topics":["0xddf252ad..."]}]}
Parameters:
  • fromBlock: BlockTag - Starting block (default: ‘latest’)
  • toBlock: BlockTag - Ending block (default: ‘latest’)
  • address?: Address | Address[] - Contract address(es) to filter
  • topics?: Array<Hash | Hash[] | null> - Event topic filters
Usage patterns:
  • Query historical events from contracts
  • Filter by event signature (topic[0])
  • Filter by indexed parameters (topic[1-3])
  • Search across multiple contracts

Filter Creation

eth_newFilter

Create a log filter for event monitoring.
// {"method":"eth_newFilter","params":[{"fromBlock":"latest","toBlock":"latest","address":"0x...","topics":[]}]} 
// → returns filter id (hex)
Parameters: Same as eth_getLogs Returns: Filter ID for polling

eth_newBlockFilter

Create a filter for new block hashes.
// {"method":"eth_newBlockFilter","params":[]} → filter id
Monitor new blocks by polling for changes.

eth_newPendingTransactionFilter

Create a filter for pending transaction hashes.
// {"method":"eth_newPendingTransactionFilter","params":[]} → filter id
Monitor mempool activity by polling for new transactions.

Filter Polling

eth_getFilterChanges

Get new entries for a filter since last poll.
// {"method":"eth_getFilterChanges","params":["0xFILTER_ID"]}
Returns:
  • Log[] - For log filters
  • Hash[] - For block/transaction filters
Note: Resets the filter’s internal state - subsequent calls only return new changes.

eth_getFilterLogs

Get all logs matching a filter (does not reset state).
// {"method":"eth_getFilterLogs","params":["0xFILTER_ID"]}
Note: Only works with log filters, not block/transaction filters.

Filter Cleanup

eth_uninstallFilter

Remove a filter and free resources.
// {"method":"eth_uninstallFilter","params":["0xFILTER_ID"]}
Always uninstall filters when done to prevent resource leaks.

Usage Example

const std = @import("std");
const primitives = @import("primitives");

// Topic0 for Transfer(address,address,uint256)
const topic0 = primitives.EventSignature.fromSignature(
    "Transfer(address,address,uint256)",
);

// After fetching logs (JSON-RPC), decode one Transfer log
pub fn decodeTransfer(
    allocator: std.mem.Allocator,
    log: primitives.EventLog.EventLog,
) !struct { from: primitives.Address, to: primitives.Address, value: u256 } {
    const sig = primitives.EventLog.EventSignature{
        .name = "Transfer",
        .inputs = &[_]primitives.EventLog.EventInput{
            .{ .name = "from", .type = .address, .indexed = true },
            .{ .name = "to", .type = .address, .indexed = true },
            .{ .name = "value", .type = .uint256, .indexed = false },
        },
    };

    const vals = try primitives.EventLog.parseEventLog(allocator, log, sig);
    defer allocator.free(vals);
    return .{ .from = vals[0].address, .to = vals[1].address, .value = vals[2].uint256 };
}

Filter Lifecycle

  1. Create - Use eth_newFilter, eth_newBlockFilter, or eth_newPendingTransactionFilter
  2. Poll - Call eth_getFilterChanges periodically to get new entries
  3. Query - Optionally use eth_getFilterLogs to re-query all matches
  4. Cleanup - Call eth_uninstallFilter when done
Filters expire after 5 minutes of inactivity on most nodes. Poll regularly to keep them alive.