Factory Contracts

Dynamically discover and index child contracts as they're deployed.

Configuration

Instead of a static address, define a [contracts.factory] block that tells Sieve how to find child contracts.

sieve.toml
[[contracts]]
name = "UniswapV3Pool"
abi = "abis/uniswap_v3_pool.json"

# start_block belongs here, children inherit it
[contracts.factory]
address = "0x1F98431c8aD98523631AE4a59f267346ea31F984"
event = "PoolCreated"
param = "pool"
start_block = 12_369_621

[[contracts.events]]
name = "Swap"
table = "uniswap_v3_swaps"
address · required the factory contract address
event · required creation event name (e.g. PoolCreated, PairCreated)
param · required event parameter that holds the child contract address
start_block · required block to start scanning for factory events. Goes on the factory block, not on [[contracts]]
abi · optional factory ABI (defaults to the parent contract's ABI)

How it works

Sieve scans the factory contract for creation events starting from start_block. Each time a new child address is found, it's registered dynamically. All events and calls defined on the parent contract are then indexed for every discovered child.

A contract_address column is automatically added to every table so you can distinguish which child contract emitted each event.

Discovery is sequenced ahead of the parallel block workers, so events a child emits in or shortly after its own creation block are never missed.

Coverage tracking

Sieve records which block ranges each factory was active for. A factory that was not watched over some range would silently miss the children created in it, and every event those children emitted, so Sieve refuses to start instead of indexing a partial table.

The refusals cover adding a factory to a database already indexed past its start_block, lowering an existing start_block, changing a factory's creation event or param, starting a sync past the uncovered range, and re-adding a factory that was removed while indexing continued. The fix is a fresh database or sieve reset.

Upgrading a database created before coverage tracking is the one exception. Run once with --assume-factory-coverage to record the current state as covered. It applies only to factories with no coverage record at all, asserting they were configured continuously. Every other refusal still stands.

terminal
sieve --assume-factory-coverage

Key rules

The parent contract must NOT have an address field. A contract is either static (has address) or factory-based (has factory block), never both.
The event must exist in the factory ABI, and param must be a parameter in that event.
Children inherit the parent's events and calls config. You define them once on the parent.

Next steps

Streaming push indexed events to webhooks or RabbitMQ