rindexer icon indicating copy to clipboard operation
rindexer copied to clipboard

[Feature request] Extended events filtering

Open serzhiio opened this issue 4 months ago • 6 comments

Summary

Index all ERC-20 Transfer events related to each pool that is discovered from a factory’s PoolCreated events—in a single table, without duplication—and be able to filter those transfers by source/destination address (e.g., only rows where from==pool or to==pool). Today I can subscribe to tokens by address, but I can’t easily constrain results to transfers involving the pool, nor merge token0+token1 streams into one sink table cleanly.

Motivation / Use case

For Uniswap/HyperSwap-like DEXes, each pool has two tokens (token0/token1) and a pool address. TVL/flow analytics require:

  • listing all transfers where the pool is from (withdrawal) or to (deposit),
  • across both token0 and token1,
  • deduplicated and stored in one table with a uniform schema.

Right now, it’s clunky to:

discover tokens via PoolCreated and then subscribe to their ERC-20 Transfer events, filtered only to rows where from/to == the discovered pool;

merge token0+token1 results into a single table without building custom plumbing.

Feature request (two parts)

1. Discovered address filters Allow event subscriptions to reference addresses discovered upstream (factory outputs) and then apply field-level filters on downstream events.

2. Union multiple subscriptions into one sink table Let multiple “child” subscriptions (e.g., token0 and token1 ERC-20 streams) write into one table with a shared schema, with built-in dedup on (tx_hash, log_index) and optional unique_keys override.

Current implementation allows to catch only one input per factory and create separate table for each input captured. So we will have two identical transfers tables for token0 and token1 and we can't filter out Transfers by TX src/destination:

- name: HyperswapV3TransfersToken0 
  details:
    - network: hyperevm
      start_block: 11648
      end_block: 100000
      factory:
        name: HyperswapV3Factory
        address: 0xb1c0fa0b789320044a6f623cfe5ebda9562602e3
        abi: ./abis/HyperswapV3Factory.abi.json
        event_name: PoolCreated
        input_name: "token0"
  abi: ./abis/ERC20.abi.json
  include_events:
  - Transfer
- name: HyperswapV3TransfersToken1
  details:
    - network: hyperevm
      start_block: 11648
      end_block: 100000
      factory:
        name: HyperswapV3Factory
        address: 0xb1c0fa0b789320044a6f623cfe5ebda9562602e3
        abi: ./abis/HyperswapV3Factory.abi.json
        event_name: PoolCreated
        input_name: "token1"
  abi: ./abis/ERC20.abi.json
  include_events:
  - Transfer

Suggested YAML design, the idea:

  - name: HyperswapV3Transfers
    details:
      - network: hyperevm
        start_block: 11648
        end_block: 100000
        factory:
          name: HyperswapV3Factory
          address: 0xb1c0fa0b789320044a6f623cfe5ebda9562602e3
          abi: ./abis/HyperswapV3Factory.abi.json
          event_name: PoolCreated
          input_name:
          - "token0"
          - "token1"
    abi: ./abis/ERC20.abi.json
    include_events:
    - name: Transfer
      # constrain contract to discovered tokens
      filter_by_contract:
        address: input_name
      # keep only rows involving the discovered pool addresses
      filter_by_fields:
        any:
          - eq: { field: "from", value: "pool_created.pool" }
          - eq: { field: "to",   value: "pool_created.pool" }

serzhiio avatar Aug 15 '25 10:08 serzhiio

Hello @serzhiio, thank you for such a thorough and well-thought-out feature request.

I’ll take a look tomorrow at adding support for passing an array of values as input_name, as that seems like a very reasonable enhancement.

Regarding the second request, it feels like a strong candidate for a bit of custom Rust code that filters events based on specific values. Since Rindexer already generates all the Rust scaffolding, you’d just need to plug in your own business logic — for example, only tracking when both from and to match the pool’s created tokens.

I’ll leave the final decision on the second request to @joshstevens19.

desfero avatar Aug 15 '25 18:08 desfero

Hello @serzhiio, thank you for such a thorough and well-thought-out feature request.

I’ll take a look tomorrow at adding support for passing an array of values as input_name, as that seems like a very reasonable enhancement.

Regarding the second request, it feels like a strong candidate for a bit of custom Rust code that filters events based on specific values. Since Rindexer already generates all the Rust scaffolding, you’d just need to plug in your own business logic — for example, only tracking when both from and to match the pool’s created tokens.

I’ll leave the final decision on the second request to @joshstevens19.

The point of second request is to make some simple filter logic able to no-code users. Ofc this is entirely depends on developer view of project positioning.

serzhiio avatar Aug 16 '25 08:08 serzhiio

@joshstevens19 @serzhiio have a PR that allows input_name to be an array #287

desfero avatar Aug 17 '25 14:08 desfero

I think the other feature is fine we can use what we do on streams with the latest updates https://github.com/joshstevens19/rindexer/pull/233 @isSerge we could add this filtering on the main indexing as well to filter out more before it writes - what do you think?

joshstevens19 avatar Aug 18 '25 10:08 joshstevens19

I am tracking several contracts events with same factory, is it necessary to maintain two identical tables for factory with same data? Image

Is it possible to optimize it to use only one table?

serzhiio avatar Aug 20 '25 11:08 serzhiio

@serzhiio we did it that way to support having different start_block and end_block given that's defined on the event to which factory filter is assigned. We could optimise that in case we see same start_block and end_block values in the configuration but not priority right now. Does that cause any specific issues your side?

desfero avatar Sep 11 '25 16:09 desfero