Although this is an EVM walkthrough, it shares most of the content with the future Solana version (TBA)
pipes/evm/liquidity/cli.ts. The pipeline is: Portal source β composite decoders β transform pipe β ClickHouse target.
1. Config and ClickHouse
Config comes from env (e.g.NETWORK, DB_PATH, PORTAL_CACHE_DB_PATH). The CLI creates a ClickHouse client, ensures tables exist, then builds the pipe:
pipes/evm/config.ts for network/portal URLs and liquidity.sql for schema.
2. Portal source
portal_source.ts wraps the EVM Portal with optional metrics and a SQLite cache so responses can be reused:
3. Composite decoders
evm_decoder.ts defines one decoder per protocol (Uniswap V2/V3/V4, Aerodrome Basic/Slipstream). Each uses evmDecoder with factory() + a factory SQLite DB to discover pools from factory events, then subscribes to swap/mint/burn/sync/collect (and protocol-specific) events:
pipeComposite({ ...decoders }) so the Portal stream is decoded into multiple typed streams (one per protocol).
4. Transform pipe
raw_liquidity_event_pipe.ts takes the decoded composite and converts each protocolβs events into a single DbLiquidityEvent shape (pool, tokens, amounts, tick, fee, block/transaction/log indices, etc.) via protocol-specific converters in converters/, then sorts by block/tx/log index:
5. ClickHouse target
clickhouse_target.ts uses the Pipes clickhouseTarget and inserts batches into liquidity_events_raw with retry logic:
6. Materialized view transforms in ClickHouse
After rows land inliquidity_events_raw, liquidity.sql (from line 87 onward) defines materialized views that maintain derived tables in ClickHouse. For example, current_balances_mv aggregates balances_history into current_balances The aggregation runs incrementally as new data is inserted into liquidity_events_raw.
This approach is commonly used for stateful data transforms / aggregations at SQD. It is elegant and convenient, but its scalability has its limits.
Putting it together
The full pipeline incli.ts:

