linera-io/linera-protocol · error

invalid ABI encoding: depositor topic padding bytes (0..12)

Error message

invalid ABI encoding: depositor topic padding bytes (0..12) must be zero

What it means

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.

Source

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

    );
    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()
    );

    // Indexed `depositor` is in topics[1], left-padded to 32 bytes.
    let depositor_topic = log.topics[1];
    ensure!(
        depositor_topic.as_slice()[..12] == [0u8; 12],
        "invalid ABI encoding: depositor topic padding bytes (0..12) must be zero"
    );
    let depositor = Address::from_slice(&depositor_topic.as_slice()[12..32]);

    let d = &log.data;

    // ABI encodes addresses as left-padded 32-byte words; the first 12 bytes must be zero.
    ensure!(
        d[128..140] == [0u8; 12],
        "invalid ABI encoding: address padding bytes (128..140) must be zero"
    );

    let mut chain_id_bytes = [0u8; 32];
    chain_id_bytes.copy_from_slice(&d[32..64]);
    let mut application_id_bytes = [0u8; 32];
    application_id_bytes.copy_from_slice(&d[64..96]);
    let mut account_owner_bytes = [0u8; 32];

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Verify the contract ABI: the indexed parameter behind topics[1] must be an address type.
  2. 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.
  3. If the ABI legitimately uses bytes32, change the parser to read the full word as the depositor identity instead of enforcing zero padding.
  4. Fix test fixtures to build topics via left-padded address encoding (12 zero bytes + 20 address bytes).

Example fix

// before
let depositor_topic = log.topics[1];
ensure!(depositor_topic.as_slice()[..12] == [0u8; 12], "invalid ABI encoding: depositor topic padding bytes (0..12) must be zero");

// after (fixture bug: was writing the raw hash)
let depositor_topic = log.topics[1];
ensure!(depositor_topic.as_slice()[..12] == [0u8; 12], "depositor topic is not a left-padded address");
// fixture: topics[1] = B256::pad depositor address via 12 zero bytes + address bytes
Defensive patterns

Strategy: validation

Validate before calling

fn depositor_topic_is_padded_address(topic: &B256) -> bool {
    topic.as_slice()[..12] == [0u8; 12]
}

if log.topics.len() == 2 && !depositor_topic_is_padded_address(&log.topics[1]) {
    tracing::warn!(topic = ?log.topics[1], "topics[1] is not a left-padded address; rejecting log");
    continue;
}

Try / catch

match parse_deposit_event(&log, bridge_addr) {
    Ok(ev) => Some(ev),
    Err(e) if e.to_string().contains("depositor topic padding") => {
        // Not a valid address encoding; treat as hostile or foreign log.
        tracing::warn!("rejecting log with dirty depositor padding");
        None
    }
    Err(e) => { tracing::error!(error = %e, "deposit parse failed"); None }
}

Prevention

When it happens

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

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

Related errors


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