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(),
¶ms.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
- 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
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
- 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
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
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- event topic does not match DepositInitiated signature
- failed to query chain ID from RPC endpoint
- invalid block header RLP
- receipt inclusion proof failed
- failed to decode receipt logs
AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22).
Data as JSON: /api/errors/622d514af3f6c607.
Report an issue: GitHub.