linera-io/linera-protocol · error

failed to parse DepositInitiated event

Error message

failed to parse DepositInitiated event

What it means

After the receipt proof validates, process_deposit parses the indexed log with proof::parse_deposit_event, matching it against the DepositInitiated event signature and the registered bridge contract address. The expect panics when the log at log_index is not a DepositInitiated event or was emitted by a different contract, aborting the transaction. This prevents minting wrapped tokens from unrelated or spoofed events.

Source

Thrown at linera-bridge/contracts/evm-bridge/src/contract.rs:248

        // 32-bit, so an unchecked `as usize` cast would truncate — letting
        // `log_index` and `log_index + 2^32` select the same log while hashing
        // to different `DepositKey`s (replay-guard bypass → double mint). A
        // checked cast rejects any value that does not fit `usize`; the full
        // u64 is preserved for the `DepositKey` below.
        let log_index_usize = usize::try_from(log_index).expect("log_index out of range");
        assert!(
            log_index_usize < logs.len(),
            "log_index {} out of range (receipt has {} logs)",
            log_index,
            logs.len()
        );
        let bridge_contract_bytes =
            self.state.bridge_contract_address.get().expect(
                "bridge contract address not registered — call RegisterFungibleBridge first",
            );
        let bridge_contract = alloy_primitives::Address::from(bridge_contract_bytes);
        let deposit = proof::parse_deposit_event(&logs[log_index_usize], bridge_contract)
            .expect("failed to parse DepositInitiated event");

        // 4. Validate deposit fields against bridge parameters
        assert_eq!(
            deposit.source_chain_id.as_limbs()[0],
            params.source_chain_id,
            "source chain ID mismatch"
        );
        assert_eq!(
            deposit.token.as_slice(),
            &params.token_address,
            "token address mismatch"
        );

        // 5. Replay protection
        let deposit_key = DepositKey {
            source_chain_id: params.source_chain_id,
            block_hash,
            tx_index,

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Select the log by topic hash: scan receipt.logs for topic0 == keccak256("DepositInitiated(...)"), do not guess the index
  2. Verify the emitter address equals the registered bridge_contract_address before submitting
  3. Keep the relayer's ABI in sync with the deployed source contract; re-generate event types after upgrades
  4. Log the failing receipt hash and inspected log locally to confirm which event you actually pointed at

Example fix

// before
let log_index = receipt.logs.len() - 1; // wrong: last log is not the deposit
submit(ProcessDeposit { log_index, .. }); // aborts: failed to parse DepositInitiated event

// after
let sig = keccak256("DepositInitiated(uint256,uint256,...)"); // exact signature from the contract
let log_index = receipt.logs.iter().rposition(|l| {
    l.topics.first().map(|t| *t == sig).unwrap_or(false) && l.address == bridge_contract
}).ok_or_else(|| anyhow!("no DepositInitiated log from bridge contract"))?;
submit(ProcessDeposit { log_index, .. });
Defensive patterns

Strategy: validation

Validate before calling

// Find the deposit log by signature and emitter, not by guessed index:
let sig = keccak256("DepositInitiated(uint256,uint64,bytes,bytes,uint64,uint8,bytes)"); // exact ABI
let idx = receipt.logs.iter().rposition(|l|
    l.topics.first().map(|t| *t == sig).unwrap_or(false) && l.address == bridge_contract);
let Some(log_index) = idx else { return Err(anyhow!("no deposit event in receipt")) };

Type guard

fn is_deposit_log(log: &Log, sig: B256, bridge: Address) -> bool {
    log.topics.first().map(|t| *t == sig).unwrap_or(false) && log.address == bridge
}

Try / catch

// On contract rejection, dump the offending log's topics/address and diff
// against the expected DepositInitiated signature; fix the relayer's event
// selection and re-derive log_index before resubmitting.

Prevention

When it happens

Trigger: log_index points at a different event in the same receipt (transfers, approvals); the deposit was emitted by an imposter contract at another address; the event signature in the relayer's ABI differs from the contract's (e.g. after a contract upgrade changed the event); log_index is off by one or two.

Common situations: Relayers that take the last log instead of scanning topics by signature; source-chain contract upgrades that alter the event layout while old relayer code keeps running; receipts containing multiple token transfers where the deposit is not at index 0.

Understand the failure class

Related errors


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