{"record":{"id":"622d514af3f6c607","repo":"linera-io/linera-protocol","slug":"failed-to-parse-depositinitiated-event","errorCode":null,"errorMessage":"failed to parse DepositInitiated event","messagePattern":"failed to parse DepositInitiated event","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"linera-bridge/contracts/evm-bridge/src/contract.rs","lineNumber":248,"sourceCode":"        // 32-bit, so an unchecked `as usize` cast would truncate — letting\n        // `log_index` and `log_index + 2^32` select the same log while hashing\n        // to different `DepositKey`s (replay-guard bypass → double mint). A\n        // checked cast rejects any value that does not fit `usize`; the full\n        // u64 is preserved for the `DepositKey` below.\n        let log_index_usize = usize::try_from(log_index).expect(\"log_index out of range\");\n        assert!(\n            log_index_usize < logs.len(),\n            \"log_index {} out of range (receipt has {} logs)\",\n            log_index,\n            logs.len()\n        );\n        let bridge_contract_bytes =\n            self.state.bridge_contract_address.get().expect(\n                \"bridge contract address not registered — call RegisterFungibleBridge first\",\n            );\n        let bridge_contract = alloy_primitives::Address::from(bridge_contract_bytes);\n        let deposit = proof::parse_deposit_event(&logs[log_index_usize], bridge_contract)\n            .expect(\"failed to parse DepositInitiated event\");\n\n        // 4. Validate deposit fields against bridge parameters\n        assert_eq!(\n            deposit.source_chain_id.as_limbs()[0],\n            params.source_chain_id,\n            \"source chain ID mismatch\"\n        );\n        assert_eq!(\n            deposit.token.as_slice(),\n            &params.token_address,\n            \"token address mismatch\"\n        );\n\n        // 5. Replay protection\n        let deposit_key = DepositKey {\n            source_chain_id: params.source_chain_id,\n            block_hash,\n            tx_index,","sourceCodeStart":230,"sourceCodeEnd":266,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-bridge/contracts/evm-bridge/src/contract.rs#L230-L266","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Select the log by topic hash: scan receipt.logs for topic0 == keccak256(\"DepositInitiated(...)\"), do not guess the index","Verify the emitter address equals the registered bridge_contract_address before submitting","Keep the relayer's ABI in sync with the deployed source contract; re-generate event types after upgrades","Log the failing receipt hash and inspected log locally to confirm which event you actually pointed at"],"exampleFix":"// before\nlet log_index = receipt.logs.len() - 1; // wrong: last log is not the deposit\nsubmit(ProcessDeposit { log_index, .. }); // aborts: failed to parse DepositInitiated event\n\n// after\nlet sig = keccak256(\"DepositInitiated(uint256,uint256,...)\"); // exact signature from the contract\nlet log_index = receipt.logs.iter().rposition(|l| {\n    l.topics.first().map(|t| *t == sig).unwrap_or(false) && l.address == bridge_contract\n}).ok_or_else(|| anyhow!(\"no DepositInitiated log from bridge contract\"))?;\nsubmit(ProcessDeposit { log_index, .. });","handlingStrategy":"validation","validationCode":"// Find the deposit log by signature and emitter, not by guessed index:\nlet sig = keccak256(\"DepositInitiated(uint256,uint64,bytes,bytes,uint64,uint8,bytes)\"); // exact ABI\nlet idx = receipt.logs.iter().rposition(|l|\n    l.topics.first().map(|t| *t == sig).unwrap_or(false) && l.address == bridge_contract);\nlet Some(log_index) = idx else { return Err(anyhow!(\"no deposit event in receipt\")) };","typeGuard":"fn is_deposit_log(log: &Log, sig: B256, bridge: Address) -> bool {\n    log.topics.first().map(|t| *t == sig).unwrap_or(false) && log.address == bridge\n}","tryCatchPattern":"// On contract rejection, dump the offending log's topics/address and diff\n// against the expected DepositInitiated signature; fix the relayer's event\n// selection and re-derive log_index before resubmitting.","preventionTips":["Always select logs by topic0 hash and emitter address","Regenerate relayer ABIs whenever the source contract upgrades","Add a canary deposit end-to-end test that exercises the real event signature"],"tags":["linera","bridge","ethereum","event-log","abi","deposit","panic"],"backgroundTag":"event-signature-mismatch","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}