{"record":{"id":"e8fd89554ce60483","repo":"nautechsystems/nautilus_trader","slug":"failed-to-decode-swap-event-data-e","errorCode":null,"errorMessage":"Failed to decode swap event data: {e}","messagePattern":"Failed to decode swap event data: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/exchanges/parsing/pancakeswap_v3/swap.rs","lineNumber":76,"sourceCode":"/// # Panics\n///\n/// Panics if the contract address is not set in the log.\npub fn parse_swap_event_hypersync(dex: SharedDex, log: &HypersyncLog) -> anyhow::Result<SwapEvent> {\n    validate_event_signature_hash(\"SwapEvent\", SWAP_EVENT_SIGNATURE_HASH, log)?;\n\n    let sender = extract_address_from_topic(log, 1, \"sender\")?;\n    let recipient = extract_address_from_topic(log, 2, \"recipient\")?;\n\n    if let Some(data) = &log.data {\n        let data_bytes = data.as_ref();\n\n        if data_bytes.len() < 7 * 32 {\n            anyhow::bail!(\"Swap event data is too short\");\n        }\n\n        let decoded = match <SwapEventData as SolType>::abi_decode(data_bytes) {\n            Ok(decoded) => decoded,\n            Err(e) => anyhow::bail!(\"Failed to decode swap event data: {e}\"),\n        };\n        let pool_address = Address::from_slice(\n            log.address\n                .clone()\n                .expect(\"Contract address should be set in logs\")\n                .as_ref(),\n        );\n        let pool_identifier = PoolIdentifier::Address(Ustr::from(&pool_address.to_string()));\n        Ok(SwapEvent::new(\n            dex,\n            pool_identifier,\n            extract_block_number(log)?,\n            extract_transaction_hash(log)?,\n            extract_transaction_index(log)?,\n            extract_log_index(log)?,\n            sender,\n            recipient,\n            decoded.amount0,","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/exchanges/parsing/pancakeswap_v3/swap.rs#L58-L94","documentation":"After the length pre-check passes, parse_swap_event_hypersync decodes the log data with `<SwapEventData as SolType>::abi_decode`. If alloy's ABI decoder rejects the payload (wrong word count for dynamic decoding semantics, non-canonical encoding, or malformed hex-derived bytes), the error is wrapped as 'Failed to decode swap event data: {e}' with the underlying decoder message.","triggerScenarios":"Calling parse_swap_event_hypersync with a PancakeSwap-topic log whose data is at least 224 bytes but is not a valid ABI encoding of (int256, int256, uint160, uint128, int24, uint128, uint128) — e.g. extra trailing bytes, non-numeric hex, or bytes corrupted in transit/storage.","commonSituations":"Logs fetched from a non-canonical or modified fork of PancakeSwap V3; custom indexing pipelines that uppercase/alter the data hex incorrectly; hand-edited test fixtures; decoding data that was sliced or padded at a non-32-byte boundary.","solutions":["Inspect the wrapped alloy decoder message ({e}) — it usually states the exact word-count or byte-offset mismatch; correct the data source accordingly","Confirm the data is exactly 0x + 448 hex chars for a PancakeSwap V3 Swap (7 words); strip or regenerate any extra/missing bytes at the source","Re-fetch the log from HyperSync — storage/transit corruption of the data field is the most common cause","If the pool is a fork with a different Swap layout, write a dedicated parser with the fork's field order instead of reusing SwapEventData"],"exampleFix":"// before: decoding whatever bytes arrived without normalization\nlet decoded = <SwapEventData as SolType>::abi_decode(data_bytes)?;\n\n// after: validate the exact word layout before decoding\nlet hex = data.as_ref();\nif hex.len() % 32 != 0 {\n    anyhow::bail!(\"data is not a whole number of 32-byte words\");\n}\nif hex.len() != 7 * 32 {\n    anyhow::bail!(\"expected exactly 7 words for PancakeSwap V3 Swap, got {}\", hex.len() / 32);\n}\nlet decoded = <SwapEventData as SolType>::abi_decode(hex)\n    .map_err(|e| anyhow::anyhow!(\"Failed to decode swap event data: {e}\"))?;","handlingStrategy":"try-catch","validationCode":"fn has_valid_swap_hex(data: Option<&HyperSyncData>) -> bool {\n    data.map(|d| {\n        let b = d.as_ref();\n        b.len() == 7 * 32 && b.iter().all(|byte| true) // exact 7-word layout\n    }).unwrap_or(false)\n}","typeGuard":"fn is_exact_seven_word_data(log: &HypersyncLog) -> bool {\n    log.data.as_ref().map(|d| d.as_ref().len() == 7 * 32).unwrap_or(false)\n}","tryCatchPattern":"match parse_swap_event_hypersync(dex, &log) {\n    Ok(event) => store(event),\n    Err(e) if e.to_string().contains(\"Failed to decode\") => {\n        tracing::error!(err = %e, \"ABI decode failed — inspect decoder message, re-fetch log from HyperSync\");\n        quarantine(log, e);\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Log the wrapped alloy decoder message — it names the exact structural mismatch","Reject data whose byte length is not a multiple of 32 before decoding","Re-fetch suspect logs directly from HyperSync to rule out corruption","Write dedicated parsers for forked DEXes with different Swap layouts instead of reusing SwapEventData"],"tags":["blockchain","abi-decoding","hypersync","pancakeswap-v3"],"backgroundTag":"schema-validation-failed","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"}