A pool_price_block row per block with a price event, plus an empty heartbeat row every heartbeat_blocks blocks.
Output
sf.substreams.sink.database.v1.DatabaseChanges
Inputs

Package ref
uniswap-database-changes-pool-price-bsc@v0.0.2Run package
CLI
Run db_out from the command line.
substreams run uniswap-database-changes-pool-price-bsc@v0.0.2 db_out -e bscsubstreams auth or directly on thegraph.market (see docs).README
The last spot price of every Uniswap pool, a pool_price_block row for each
block with a price event plus an empty heartbeat row about once a minute, for
consumers that only need prices. Schema:
proto/evm/uniswap/pool_price/pool-price.proto (PoolPriceBlock, oneof 2002).
Every Uniswap price change has an event that carries the post-state price:
v3/v4 price only moves on Swap and starts at Initialize, and v2 emits Sync
on every reserve change. For each pool, the event with the highest ordinal in
the block sets the row's price. Only events are read, so extended and base
blocks give the same rows.
Each entry in pools has the field names and types of block_digest's
PoolChangeset: version, pool_address, pool_id (v4), last_ordinal,
sqrt_price_x96 / tick (v3/v4) or reserve0 / reserve1 (v2), plus what set
the price:
swap_count > 0: a swap.initialized set and swap_count = 0: the starting price from
Initialize, before any trade. Anyone can initialize a pool at any price, so
weigh these by liquidity before trusting them.swap_count > 0: the pair traded in the block. A mint, burn or sync
after the swap can still set the final reserves.swap_count = 0: only a mint, burn or sync moved the reserves.v3/v4 entries a swap set also carry liquidity, the active liquidity the last
Swap reports. It's liquidity at that swap, not at the end of the block, since a
Mint, Burn or ModifyLiquidity later in the block changes it without an event
this package reads. v2 and Initialize-only entries have none.
Prices are raw units, since token decimals aren't in the stream.
map_pool_prices: the digest (evm.uniswap.pool_price.Block) from the pools
package's uniswap_v{2,3,4}:map_events, imported unchanged from the deployed
pools spkg so the parents are served from cache. Those map_events already
filter to Uniswap pools (v2/v3 on store_pools, v4 on the PoolManager
address).db_out: the pool_price_block row, for a block with a price event or a
heartbeat block (block_num % heartbeat_blocks == 0, an empty row). It also
takes the block, unused, so it runs on blocks with no Uniswap events to write
their heartbeats (Substreams skips a module whose map inputs are all empty).
Params ordering_key (pool_price:<chain_id> in generated manifests) and
heartbeat_blocks (about one minute of blocks per chain in chains.json, 0
writes every block). A gap between rows means no Uniswap price moved in those
blocks, and a gap longer than heartbeat_blocks means the stream is behind.PoolPriceBlock is GCS-delivered: the sink uploads each row to the payload
bucket and publishes a GcsPayloadRef pointer, one object per block.
make pack NETWORK=base # ../local/uniswap-labs-database-changes-pool-price-base.spkg
make run NETWORK=base
make test
scripts/build.sh resolves the pools version the prod sink streams for the
chain (pool-state's resolve-pools-version.py, POOLS_VERSION / POOLS_SPKG
override it) and fails unless the imported parents hash like the deployed ones.
chains.json has every chain with a prod pools sink (the chains we compute
realtime prices for), each with its chain id, the sink's primary Substreams
endpoint and a recent start block: 2026-10-27 for base and mainnet, 2026-11-02
for the rest. A pool's first price arrives with its first event after that
block.
On 200 Base blocks from 53,150,000, against pool-state's db_out changeset
(whose prices come from storage on these extended blocks):
pool_count right in each, pools sorted.swap_count and initialized equal the
changeset's in every one, except five zero-amount v4 swaps. Those left
slot0 untouched, so the changeset has no price, and the event's unchanged
price is correct.slot0 rewrites with no price event (reentrancy lock, fee
update; 32 v3, 3 v4) have no entry, as intended.liquidity on all 2,054 v3/v4 swap entries. Where the changeset has an
end-of-block liquidity (84 entries) it's equal in 73. The other 11 all had a
Mint, Burn or ModifyLiquidity in the same block.db_out's 4.0 MiB.Modules
A pool_price_block row per block with a price event, plus an empty heartbeat row every heartbeat_blocks blocks.
Output
sf.substreams.sink.database.v1.DatabaseChanges
Inputs
The last price per pool from the block's v2 Sync and v3/v4 Initialize / Swap events. Parents imported unchanged from https://spkg.io/v1/packages/uniswap-database-changes-pools-bsc/v0.0.3.
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