Output
sf.substreams.sink.database.v1.DatabaseChanges
Inputs

Package ref
uniswap-event-audit-unichain-mainnet@v0.0.1Run package
CLI
Run counts_out from the command line.
substreams run uniswap-event-audit-unichain-mainnet@v0.0.1 counts_out -e unichain-mainnetsubstreams auth or directly on thegraph.market (see docs).README
Per-block completeness digest of every event the pools package decodes, for a backend check that pulls it per chain and compares it with what landed in the warehouse. Stateless, no RPC, no sink and no dataset: the consumer streams the modules directly.
counts_out, digest_out and keys_out read the deployed uniswap-database-changes-pools-<chain> package's
uniswap_v{2,3,4}:map_events (imported by URL, so the parent hashes are the
live ones and the provider serves them from cache) and output flat
DatabaseChanges, one emission per block.
| Module | Table | Rows | When |
|---|---|---|---|
counts_out | block_digest_counts | an all row every block (empty blocks included) and one per event type present: event_type, event_count | every tick; 3.7 MiB per 1,000 Base blocks |
digest_out | block_digest_event_manifest | the same rows plus ordinals and block_indexes, roaring portable bitmaps (hex), empty on all | over a range whose counts disagree |
keys_out | event_key | one row per event: event_type, block_index, log_index, ordinal, transaction_index, tx_hash, pool_address (pool id for v4) | over a flagged bucket only |
final_head | (a Clock) | each block's clock, nothing else: no stores, no parents | once per tick, to learn the provider's final block (below) |
map_events outputs is an event type, named
<version>_<list> (44 today). A test reads the Events messages from the
protos, so a new list fails the build until it's covered here.associated_evm_transaction types are transactions, not logs:
their ordinals hold the tx begin ordinal and block_indexes the tx index.Row envelope on every table: block_num, block_hash, timestamp,
detail_level (BASE or EXTENDED), index, global_sequence
((block << 64) | row index, 39 digits) and ordering_key (the event type on
counts and manifest rows, the pool on key rows). chain_id isn't emitted: the
consumer tags it from the chain it streams.
These tables come from the pool-state package's digest_out / keys_out and
match them field for field apart from ordering_key, without the pool-state
sink's pool_changeset row (tests/digest.rs).
./scripts/build.sh <chain> <version> # local/uniswap-labs-database-changes-event-audit-<chain>.spkg
make counts NETWORK=base START_BLOCK=51990000 ENDPOINT=base-mainnet.streamingfast.io:443
The build resolves the pools version the prod sink streams for the chain
(infrastructure/config/sinks/prod, override with POOLS_VERSION or
POOLS_SPKG), records it in each module's doc (the parents' spkg URL), and
fails unless the
parent map_events and store_pools hash exactly like the deployed pools
package's. Publish with the "Build and Optionally Upload Database Changes
Substreams Package" workflow, event-audit. The package is
uniswap-event-audit-<chain>.
| Request | Processed blocks | Egress |
|---|---|---|
counts_out, 20 blocks, first request | 1,000 (one whole segment) | 62 KiB |
counts_out, 1 block, segment already cached | 0 | 2.8 KiB |
counts_out, 1,000 blocks, segment already cached | 0 | 3.7 MiB |
digest_out, 20 blocks | 1,000 | 99 KiB |
keys_out, 20 blocks | 1,000 | 1.5 MiB |
A request is billed at least a whole 1,000-block segment the first time, and cached segments meter egress only, so the consumer should ask for 1,000-block-aligned ranges. Counts are about 3.8 KiB per Base block, more than the 1 KB estimate, since each of a block's ~8 rows carries the full envelope.
There's no Substreams call that returns the final block, but every block
message carries final_block_height. Request final_head from -1 (relative
to the chain head) without final-blocks-only, read final_block_height from the
first block and close the stream: 1 processed block and about half a second on
Base (2026-10-02), with the provider's own idea of final (head − 200 on Base,
against head − 604 for the RPC's finalized). Don't add final-blocks-only:
-1 is relative to the chain head, so that waits for the head block to become
final (~400 s on Base). And don't use counts_out for it: from -1 it pays the
store catch-up since the segment started (1,400 processed blocks in the same
test).
Modules
Output
sf.substreams.sink.database.v1.DatabaseChanges
Inputs
Output
sf.substreams.sink.database.v1.DatabaseChanges
Inputs
Output
sf.substreams.v1.Clock
Inputs
Output
sf.substreams.sink.database.v1.DatabaseChanges
Inputs
Output
evm.uniswap.v2.EventsStore value
bigint
Update policy
set
Inputs
Output
evm.uniswap.v3.EventsStore value
bigint
Update policy
set
Inputs
Output
evm.uniswap.v4.EventsStore value
proto:evm.store.V4PoolStoreUpdate policy
set
Inputs