nautechsystems/nautilus_trader · error

PoolManager address should be set in logs

Error message

PoolManager address should be set in logs

What it means

In `parse_initialize_event_hypersync` (Uniswap V4), the pool is identified by the PoolManager contract that emitted the Initialize event, read from `log.address` via `.expect("PoolManager address should be set in logs")`. On-chain logs always carry the emitter address, so `None` means the input log is missing its emitter — an input-contract violation the parser treats as a hard panic. Unlike V3, the pool address here is the contract address itself, so this field is mandatory for building the `PoolIdentifier`.

Source

Thrown at crates/adapters/blockchain/src/exchanges/parsing/uniswap_v4/initialize.rs:81

/// ```
///
/// # Errors
///
/// Returns an error if the log parsing fails or if the event data is invalid.
///
/// # Panics
///
/// Panics if the log address is not set.
pub fn parse_initialize_event_hypersync(log: HypersyncLog) -> anyhow::Result<PoolCreatedEvent> {
    validate_event_signature_hash("InitializeEvent", INITIALIZE_EVENT_SIGNATURE_HASH, &log)?;

    let block_number = extract_block_number(&log)?;

    // The pool address for V4 is the PoolManager contract address (the event emitter)
    let pool_manager_address = Address::from_slice(
        log.address
            .clone()
            .expect("PoolManager address should be set in logs")
            .as_ref(),
    );

    // Extract currency0 and currency1 from topics
    // topics[0] = event signature
    // topics[1] = poolId (bytes32)
    // topics[2] = currency0 (indexed)
    // topics[3] = currency1 (indexed)
    let topics = &log.topics;
    if topics.len() < 4 {
        anyhow::bail!(
            "Initialize event missing topics: expected 4, was {}",
            topics.len()
        );
    }

    // Extract Pool ID from topics[1] - this is the unique identifier for V4 pools
    let pool_id_bytes = topics[1]

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Populate `log.address` with the PoolManager contract address before parsing.
  2. If converting logs from another source, ensure the emitter address is copied into the hypersync log.
  3. In your own code, unwrap the Option with a proper error message instead of relying on the panic.
  4. Verify the hypersync client version still fills the log address field on deserialization.

Example fix

// before
let pool_manager_address = Address::from_slice(
    log.address.clone().expect("PoolManager address should be set in logs").as_ref(),
);
// after
let emitter = log.address.clone().ok_or_else(|| anyhow::anyhow!("initialize log missing PoolManager address"))?;
let pool_manager_address = Address::from_slice(emitter.as_ref());
Defensive patterns

Strategy: validation

Validate before calling

fn require_pool_manager(log: &HyperSyncLog) -> Result<Address, String> {
    log.address.clone().ok_or_else(|| "V4 initialize log missing PoolManager address".to_string())
}

Type guard

fn has_pool_manager(log: &HyperSyncLog) -> bool {
    log.address.is_some()
}

Try / catch

// Propagate as error before parsing
let pool_manager = require_pool_manager(&log)?;

Prevention

When it happens

Trigger: Calling `parse_initialize_event_hypersync` with a `HyperSyncLog` whose `address` is `None`, e.g. a fixture built without `address` or log data that lost the emitter during deserialization/conversion.

Common situations: Test logs for V4 Initialize events built via default construction omitting the PoolManager address; adapting logs from another indexer into the hypersync log type; a hypersync client schema change making the address field optional.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/27160a7238200243. Report an issue: GitHub.