{"record":{"id":"7efa04246dfa60f8","repo":"linera-io/linera-protocol","slug":"log-emitter-does-not-match-expected-bridge-co","errorCode":null,"errorMessage":"log emitter {:?} does not match expected bridge contract {:?}","messagePattern":"log emitter (.+?) does not match expected bridge contract (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"linera-bridge/src/proof/mod.rs","lineNumber":425,"sourceCode":"    );\n\n    let mut logs_data = &data[..logs_header.payload_length];\n    let mut logs = Vec::new();\n    while !logs_data.is_empty() {\n        logs.push(decode_log(&mut logs_data)?);\n    }\n\n    Ok(logs)\n}\n\n/// Parses a `DepositInitiated` event from a receipt log.\n///\n/// Verifies that `topic[0]` matches the event signature, that the log was emitted by\n/// the `expected_emitter` (bridge contract address), and ABI-decodes the data fields.\n/// The `depositor` field is indexed (stored in `topics[1]`); all other parameters are\n/// non-indexed and encoded in the log data.\npub fn parse_deposit_event(log: &ReceiptLog, expected_emitter: Address) -> Result<DepositEvent> {\n    ensure!(\n        log.address == expected_emitter,\n        \"log emitter {:?} does not match expected bridge contract {:?}\",\n        log.address,\n        expected_emitter\n    );\n    ensure!(\n        log.topics.first() == Some(&deposit_event_signature()),\n        \"event topic does not match DepositInitiated signature\"\n    );\n    ensure!(\n        log.topics.len() == 2,\n        \"expected exactly 2 topics (signature + indexed depositor), got {}\",\n        log.topics.len()\n    );\n    ensure!(\n        log.data.len() == 224,\n        \"expected 224 bytes of event data (7 x 32), got {}\",\n        log.data.len()","sourceCodeStart":407,"sourceCodeEnd":443,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-bridge/src/proof/mod.rs#L407-L443","documentation":"parse_deposit_event first checks that the log's emitting contract address equals expected_emitter (the configured bridge contract). A log produced by any other contract — an impersonating token, a mock at a different address, or simply the wrong configured address — is rejected before topics are even examined.","triggerScenarios":"Scanning EVM logs with evm_scan_iteration and passing a DepositInitiated-shaped log emitted by a token or fake contract; configuring the bridge with the wrong contract address (testnet address on mainnet, wrong proxy/implementation address); scanning unfiltered logs instead of filtering by contract address.","commonSituations":"Bridge deployment config pointing at the implementation address while events come from the proxy (or vice versa); eth_getLogs without an address filter returning DepositInitiated-like events from other protocols; test contracts deployed at a fresh address while the verifier still expects the old one.","solutions":["Check the two addresses in the message: the log emitter vs expected — then correct the configured bridge contract address","Filter logs by the bridge contract address at query time (eth_getLogs address parameter) instead of post-filtering","For proxy contracts, use the proxy address that events are attributed to, not the logic contract"],"exampleFix":"// before\nfor log in all_logs { if let Ok(ev) = parse_deposit_event(&log, BRIDGE) { ... } }\n\n// after\nlet bridge_logs = rpc.get_logs(address = BRIDGE, topics = [SIG]);\nfor log in bridge_logs { let ev = parse_deposit_event(&log, BRIDGE)?; ... }","handlingStrategy":"validation","validationCode":"// Filter before parsing: only the bridge contract's logs are candidates\nif log.address != bridge_address {\n    continue; // or skip at RPC level with an address filter\n}\nlet event = parse_deposit_event(&log, bridge_address)?;","typeGuard":null,"tryCatchPattern":"match parse_deposit_event(&log, expected) {\n    Ok(ev) => Some(ev),\n    Err(e) if e.to_string().contains(\"does not match expected bridge contract\") => {\n        tracing::debug!(addr = ?log.address, \"ignoring non-bridge log\"); None\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Always pass the bridge contract address as an eth_getLogs filter, not just as a post-check","For proxy deployments, configure the proxy address (where events are attributed)","Fail startup if the configured address does not have code at the expected chain"],"tags":["ethereum","bridge","event-log","contract-address","security","rust"],"backgroundTag":"contract-address-mismatch","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}