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.addressView on GitHub (pinned to 18893faf8b)
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
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
- 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
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
- Burn event data is too short
- Finalized transaction {} emitted {} Swap logs; expected exac
- Missing tickLower in topic2 when parsing burn event
- Failed to decode burn event data: {e}
- Missing tickLower in topic2 when parsing collect event
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/7350d7b5b630dcd0.
Report an issue: GitHub.