linera-io/linera-protocol · error

log emitter {:?} does not match expected bridge contract {:?

Error message

log emitter {:?} does not match expected bridge contract {:?}

What it means

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.

Source

Thrown at linera-bridge/src/proof/mod.rs:425

    );

    let mut logs_data = &data[..logs_header.payload_length];
    let mut logs = Vec::new();
    while !logs_data.is_empty() {
        logs.push(decode_log(&mut logs_data)?);
    }

    Ok(logs)
}

/// Parses a `DepositInitiated` event from a receipt log.
///
/// Verifies that `topic[0]` matches the event signature, that the log was emitted by
/// the `expected_emitter` (bridge contract address), and ABI-decodes the data fields.
/// The `depositor` field is indexed (stored in `topics[1]`); all other parameters are
/// non-indexed and encoded in the log data.
pub fn parse_deposit_event(log: &ReceiptLog, expected_emitter: Address) -> Result<DepositEvent> {
    ensure!(
        log.address == expected_emitter,
        "log emitter {:?} does not match expected bridge contract {:?}",
        log.address,
        expected_emitter
    );
    ensure!(
        log.topics.first() == Some(&deposit_event_signature()),
        "event topic does not match DepositInitiated signature"
    );
    ensure!(
        log.topics.len() == 2,
        "expected exactly 2 topics (signature + indexed depositor), got {}",
        log.topics.len()
    );
    ensure!(
        log.data.len() == 224,
        "expected 224 bytes of event data (7 x 32), got {}",
        log.data.len()

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Check the two addresses in the message: the log emitter vs expected — then correct the configured bridge contract address
  2. Filter logs by the bridge contract address at query time (eth_getLogs address parameter) instead of post-filtering
  3. For proxy contracts, use the proxy address that events are attributed to, not the logic contract

Example fix

// before
for log in all_logs { if let Ok(ev) = parse_deposit_event(&log, BRIDGE) { ... } }

// after
let bridge_logs = rpc.get_logs(address = BRIDGE, topics = [SIG]);
for log in bridge_logs { let ev = parse_deposit_event(&log, BRIDGE)?; ... }
Defensive patterns

Strategy: validation

Validate before calling

// Filter before parsing: only the bridge contract's logs are candidates
if log.address != bridge_address {
    continue; // or skip at RPC level with an address filter
}
let event = parse_deposit_event(&log, bridge_address)?;

Try / catch

match parse_deposit_event(&log, expected) {
    Ok(ev) => Some(ev),
    Err(e) if e.to_string().contains("does not match expected bridge contract") => {
        tracing::debug!(addr = ?log.address, "ignoring non-bridge log"); None
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22). Data as JSON: /api/errors/7efa04246dfa60f8. Report an issue: GitHub.