nautechsystems/nautilus_trader · error

Missing tickUpper in topic3 when parsing burn event

Error message

Missing tickUpper in topic3 when parsing burn event

What it means

This error is raised by parse_burn_event_hypersync when a Uniswap V3 Burn log fetched via HyperSync has no topic at index 3. The Burn event signature is Burn(address,int24,int24,uint128,uint128) with topics [sig, owner, tickLower, tickUpper], so topic3 must carry the 32-byte padded tickUpper. Without it the event cannot be parsed, so the parser bails out early instead of producing a wrong tick range.

Source

Thrown at crates/adapters/blockchain/src/exchanges/parsing/uniswap_v3/burn.rs:75

    let owner = extract_address_from_topic(log, 1, "owner")?;

    // Extract int24 tickLower from topic2 (stored as a 32-byte padded value)
    let tick_lower = match log.topics.get(2).and_then(|t| t.as_ref()) {
        Some(topic) => {
            let tick_lower_bytes: [u8; 32] = topic.as_ref().try_into()?;
            i32::from_be_bytes(tick_lower_bytes[28..32].try_into()?)
        }
        None => anyhow::bail!("Missing tickLower in topic2 when parsing burn event"),
    };

    // Extract int24 tickUpper from topic3 (stored as a 32-byte padded value)
    let tick_upper = match log.topics.get(3).and_then(|t| t.as_ref()) {
        Some(topic) => {
            let tick_upper_bytes: [u8; 32] = topic.as_ref().try_into()?;
            i32::from_be_bytes(tick_upper_bytes[28..32].try_into()?)
        }
        None => anyhow::bail!("Missing tickUpper in topic3 when parsing burn event"),
    };

    if let Some(data) = &log.data {
        let data_bytes = data.as_ref();

        // Validate if data contains 3 parameters of 32 bytes each
        if data_bytes.len() < 3 * 32 {
            anyhow::bail!("Burn event data is too short");
        }

        // Decode the data using the BurnEventData struct
        let decoded = match <BurnEventData as SolType>::abi_decode(data_bytes) {
            Ok(decoded) => decoded,
            Err(e) => anyhow::bail!("Failed to decode burn event data: {e}"),
        };

        let pool_address = Address::from_slice(
            log.address

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Verify the topic0 filter used in the HyperSync query matches exactly the Burn event signature hash so only Burn logs are passed to the parser
  2. Add a guard that checks log.topics.len() >= 4 and filters out or logs-and-skips non-conforming logs before calling parse_burn_event_hypersync
  3. Check the HyperSync query/schema version; confirm log.topics is deserialized as Vec<Option<FixedBytes<32>>> and that the fourth element exists for the affected log
  4. Regenerate or fix the test fixture so the Burn log includes all four topics

Example fix

// before
let tick_upper = match log.topics.get(3).and_then(|t| t.as_ref()) {
    Some(topic) => { /* decode */ }
    None => anyhow::bail!("Missing tickUpper in topic3 when parsing burn event"),
};
// after (caller-side pre-filter)
if log.topics.len() < 4 {
    tracing::warn!(tx = ?log.transaction_hash, "skipping non-Burn log: missing topic3");
    return Ok(None);
}
let tick_upper = parse_burn_event_hypersync(log)?;
Defensive patterns

Strategy: validation

Validate before calling

fn has_burn_topics(log: &HyperSyncLog) -> bool {
    log.topics.len() >= 4 && log.topics[0].is_some()
}
// only call parse_burn_event_hypersync when has_burn_topics(&log)

Type guard

fn burn_log_guard(log: &HyperSyncLog) -> Option<(&FixedBytes<32>, &FixedBytes<32>)> {
    match (log.topics.get(2), log.topics.get(3)) {
        (Some(Some(lo)), Some(Some(hi))) => Some((lo, hi)),
        _ => None,
    }
}

Try / catch

match parse_burn_event_hypersync(&log) {
    Ok(ev) => handle(ev),
    Err(e) if e.to_string().contains("Missing tickUpper in topic3") => {
        tracing::warn!("skipping malformed burn log"); // treat as skippable
    }
    Err(e) => return Err(e.into()),
}

Prevention

When it happens

Trigger: Calling parse_burn_event_hypersync with a HyperSync log whose topics array has fewer than 4 entries — e.g. the log is not actually a Burn event (wrong topic0 filter), the topics array was truncated, or an anonymous/malformed event was captured by an overly broad topic filter.

Common situations: Subscribing to a too-broad topic0 filter so non-Burn events reach the parser; a HyperSync API/schema change returning logs with missing topic entries; hand-crafted test fixtures omitting topic3; a pool emitted a non-standard or synthetic Burn-like event.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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