{"record":{"id":"474dc6a504448ecc","repo":"nautechsystems/nautilus_trader","slug":"contract-address-should-be-set-in-logs-474dc6","errorCode":null,"errorMessage":"Contract address should be set in logs","messagePattern":"Contract address should be set in logs","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/exchanges/parsing/uniswap_v3/swap.rs","lineNumber":80,"sourceCode":"\n    if let Some(data) = &log.data {\n        let data_bytes = data.as_ref();\n\n        // Validate if data contains 5 parameters of 32 bytes each\n        if data_bytes.len() < 5 * 32 {\n            anyhow::bail!(\"Swap event data is too short\");\n        }\n\n        // Decode the data using the SwapEventData struct\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 _ = decoded.amount0;\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,\n            decoded.amount1,\n            decoded.sqrt_price_x96,\n            decoded.liquidity,\n            decoded.tick.as_i32(),\n        ))","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/exchanges/parsing/uniswap_v3/swap.rs#L62-L98","documentation":"In `parse_swap_event_hypersync`, the swap log's `address` field is `Option<Address>` and is unwrapped with `.expect(\"Contract address should be set in logs\")` to derive the pool identifier. Since every on-chain EVM log is emitted by a contract and therefore has an address, a `None` value signals that the input log is malformed or was built without the emitter address — the parser treats that as an unrecoverable input-contract violation and panics. It is not related to decoding the swap amounts or topics.","triggerScenarios":"Invoking `parse_swap_event_hypersync` with a `HyperSyncLog` where `address` is `None` — e.g. a test fixture constructed without `address`, or log data deserialized through a path that leaves the emitter address unset.","commonSituations":"Hand-written swap log fixtures in tests missing the `address` field; adapting logs from an RPC/other indexer into the hypersync log type and forgetting the emitter; a library upgrade changing the log struct so `address` became optional and defaults to `None`.","solutions":["Set `log.address` to the emitting Uniswap V3 pool contract address before calling the parser.","If logs are converted from another source, copy the emitter address into the `address` field of the hypersync log.","Handle the Option explicitly in your own code and surface an error rather than hitting the panic.","Confirm the hypersync client version/schema still deserializes the log address field."],"exampleFix":"// before\nlet pool_address = Address::from_slice(\n    log.address.clone().expect(\"Contract address should be set in logs\").as_ref(),\n);\n// after\nlet emitter = log.address.clone().ok_or_else(|| anyhow::anyhow!(\"swap log missing contract address\"))?;\nlet pool_address = Address::from_slice(emitter.as_ref());","handlingStrategy":"validation","validationCode":"fn require_emitter(log: &HyperSyncLog) -> Result<Address, String> {\n    log.address.clone().ok_or_else(|| \"swap log missing emitter address\".to_string())\n}","typeGuard":"fn log_has_emitter(log: &HyperSyncLog) -> bool {\n    log.address.is_some()\n}","tryCatchPattern":"// Unwrap via ok_or_else and propagate, not expect\nlet emitter = require_emitter(&log)?;","preventionTips":["When converting logs from other data sources, map the emitter field in one shared adapter function.","Unit-test the conversion layer to assert address round-trips.","Pin hypersync-client versions and review changelogs for log schema changes."],"tags":["panic","rust","hypersync","uniswap-v3","missing-field"],"backgroundTag":"null-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"}