{"record":{"id":"f72053b96437d552","repo":"linera-io/linera-protocol","slug":"invalid-abi-encoding-depositor-topic-padding-byte","errorCode":null,"errorMessage":"invalid ABI encoding: depositor topic padding bytes (0..12) must be zero","messagePattern":"invalid ABI encoding: depositor topic padding bytes \\(0\\.\\.12\\) must be zero","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"linera-bridge/src/proof/mod.rs","lineNumber":448,"sourceCode":"    );\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()\n    );\n\n    // Indexed `depositor` is in topics[1], left-padded to 32 bytes.\n    let depositor_topic = log.topics[1];\n    ensure!(\n        depositor_topic.as_slice()[..12] == [0u8; 12],\n        \"invalid ABI encoding: depositor topic padding bytes (0..12) must be zero\"\n    );\n    let depositor = Address::from_slice(&depositor_topic.as_slice()[12..32]);\n\n    let d = &log.data;\n\n    // ABI encodes addresses as left-padded 32-byte words; the first 12 bytes must be zero.\n    ensure!(\n        d[128..140] == [0u8; 12],\n        \"invalid ABI encoding: address padding bytes (128..140) must be zero\"\n    );\n\n    let mut chain_id_bytes = [0u8; 32];\n    chain_id_bytes.copy_from_slice(&d[32..64]);\n    let mut application_id_bytes = [0u8; 32];\n    application_id_bytes.copy_from_slice(&d[64..96]);\n    let mut account_owner_bytes = [0u8; 32];","sourceCodeStart":430,"sourceCodeEnd":466,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-bridge/src/proof/mod.rs#L430-L466","documentation":"In ABI encoding, an indexed address parameter is stored in topics as a 32-byte word that is zero-padded on the left 12 bytes. parse_deposit_event takes topics[1] as the depositor and enforces bytes 0..12 are zero before extracting the 20-byte address. The check at linera-bridge/src/proof/mod.rs:448 fails when those padding bytes are non-zero, i.e. the topic is a full 32-byte value (bytes32) rather than a left-padded address, or the log is malformed/forged.","triggerScenarios":"The contract (or a look-alike) declares the indexed parameter as bytes32 instead of address; a malicious emitter crafts a topic with dirty padding hoping the parser will truncate it; a buggy test fixture copies a hash into topics[1] instead of left-padding an address.","commonSituations":"Contract upgrade changed depositor from address to bytes32 while keeping the event signature (indexed types are not part of the topic[0] hash for non-canonical cases); decoding an impersonated event from a contract that reuses the signature; fixture-generation code in tests that fills topics with random 32-byte values.","solutions":["Verify the contract ABI: the indexed parameter behind topics[1] must be an address type.","Print topics[1] hex; if bytes 0..12 contain data, you are decoding a bytes32-valued event or a hostile log — do not truncate, reject the log.","If the ABI legitimately uses bytes32, change the parser to read the full word as the depositor identity instead of enforcing zero padding.","Fix test fixtures to build topics via left-padded address encoding (12 zero bytes + 20 address bytes)."],"exampleFix":"// before\nlet depositor_topic = log.topics[1];\nensure!(depositor_topic.as_slice()[..12] == [0u8; 12], \"invalid ABI encoding: depositor topic padding bytes (0..12) must be zero\");\n\n// after (fixture bug: was writing the raw hash)\nlet depositor_topic = log.topics[1];\nensure!(depositor_topic.as_slice()[..12] == [0u8; 12], \"depositor topic is not a left-padded address\");\n// fixture: topics[1] = B256::pad depositor address via 12 zero bytes + address bytes","handlingStrategy":"validation","validationCode":"fn depositor_topic_is_padded_address(topic: &B256) -> bool {\n    topic.as_slice()[..12] == [0u8; 12]\n}\n\nif log.topics.len() == 2 && !depositor_topic_is_padded_address(&log.topics[1]) {\n    tracing::warn!(topic = ?log.topics[1], \"topics[1] is not a left-padded address; rejecting log\");\n    continue;\n}","typeGuard":null,"tryCatchPattern":"match parse_deposit_event(&log, bridge_addr) {\n    Ok(ev) => Some(ev),\n    Err(e) if e.to_string().contains(\"depositor topic padding\") => {\n        // Not a valid address encoding; treat as hostile or foreign log.\n        tracing::warn!(\"rejecting log with dirty depositor padding\");\n        None\n    }\n    Err(e) => { tracing::error!(error = %e, \"deposit parse failed\"); None }\n}","preventionTips":["Never mask a padding violation by truncating topics[1]: reject the log.","Verify the indexed parameter type in the contract ABI is address during deployment checks.","Generate test topics with proper 12-byte zero padding plus the 20-byte address."],"tags":["ethereum","evm","abi","encoding","rust","linera-bridge"],"backgroundTag":"evm-event-abi-mismatch","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}