{"record":{"id":"7350d7b5b630dcd0","repo":"nautechsystems/nautilus_trader","slug":"missing-tickupper-in-topic3-when-parsing-burn-even","errorCode":null,"errorMessage":"Missing tickUpper in topic3 when parsing burn event","messagePattern":"Missing tickUpper in topic3 when parsing burn event","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/exchanges/parsing/uniswap_v3/burn.rs","lineNumber":75,"sourceCode":"\n    let owner = extract_address_from_topic(log, 1, \"owner\")?;\n\n    // Extract int24 tickLower from topic2 (stored as a 32-byte padded value)\n    let tick_lower = match log.topics.get(2).and_then(|t| t.as_ref()) {\n        Some(topic) => {\n            let tick_lower_bytes: [u8; 32] = topic.as_ref().try_into()?;\n            i32::from_be_bytes(tick_lower_bytes[28..32].try_into()?)\n        }\n        None => anyhow::bail!(\"Missing tickLower in topic2 when parsing burn event\"),\n    };\n\n    // Extract int24 tickUpper from topic3 (stored as a 32-byte padded value)\n    let tick_upper = match log.topics.get(3).and_then(|t| t.as_ref()) {\n        Some(topic) => {\n            let tick_upper_bytes: [u8; 32] = topic.as_ref().try_into()?;\n            i32::from_be_bytes(tick_upper_bytes[28..32].try_into()?)\n        }\n        None => anyhow::bail!(\"Missing tickUpper in topic3 when parsing burn event\"),\n    };\n\n    if let Some(data) = &log.data {\n        let data_bytes = data.as_ref();\n\n        // Validate if data contains 3 parameters of 32 bytes each\n        if data_bytes.len() < 3 * 32 {\n            anyhow::bail!(\"Burn event data is too short\");\n        }\n\n        // Decode the data using the BurnEventData struct\n        let decoded = match <BurnEventData as SolType>::abi_decode(data_bytes) {\n            Ok(decoded) => decoded,\n            Err(e) => anyhow::bail!(\"Failed to decode burn event data: {e}\"),\n        };\n\n        let pool_address = Address::from_slice(\n            log.address","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/exchanges/parsing/uniswap_v3/burn.rs#L57-L93","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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","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","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","Regenerate or fix the test fixture so the Burn log includes all four topics"],"exampleFix":"// before\nlet tick_upper = match log.topics.get(3).and_then(|t| t.as_ref()) {\n    Some(topic) => { /* decode */ }\n    None => anyhow::bail!(\"Missing tickUpper in topic3 when parsing burn event\"),\n};\n// after (caller-side pre-filter)\nif log.topics.len() < 4 {\n    tracing::warn!(tx = ?log.transaction_hash, \"skipping non-Burn log: missing topic3\");\n    return Ok(None);\n}\nlet tick_upper = parse_burn_event_hypersync(log)?;","handlingStrategy":"validation","validationCode":"fn has_burn_topics(log: &HyperSyncLog) -> bool {\n    log.topics.len() >= 4 && log.topics[0].is_some()\n}\n// only call parse_burn_event_hypersync when has_burn_topics(&log)","typeGuard":"fn burn_log_guard(log: &HyperSyncLog) -> Option<(&FixedBytes<32>, &FixedBytes<32>)> {\n    match (log.topics.get(2), log.topics.get(3)) {\n        (Some(Some(lo)), Some(Some(hi))) => Some((lo, hi)),\n        _ => None,\n    }\n}","tryCatchPattern":"match parse_burn_event_hypersync(&log) {\n    Ok(ev) => handle(ev),\n    Err(e) if e.to_string().contains(\"Missing tickUpper in topic3\") => {\n        tracing::warn!(\"skipping malformed burn log\"); // treat as skippable\n    }\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Filter HyperSync queries on the exact Burn event topic0 hash","Always check topics length (>= 4) before parsing indexed-event logs","Keep test fixtures generated from real logs, never hand-trimmed","Pin and test the HyperSync client/schema version the parser was built against"],"tags":["blockchain","uniswap-v3","abi-parsing","events"],"backgroundTag":"missing-required-argument","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}