linera-io/linera-protocol · error
invalid ABI encoding: address padding bytes (128..140) must
Error message
invalid ABI encoding: address padding bytes (128..140) must be zero
What it means
After the 224-byte length check, parse_deposit_event treats data word 4 (bytes 128..160) as the `token` address, which ABI-encodes as a 32-byte word left-padded with 12 zero bytes. The ensure at linera-bridge/src/proof/mod.rs:457 fails when bytes 128..140 are non-zero, meaning that word is not a left-padded address — typically because the field is actually a bytes32 in the emitting contract, or the word offsets have shifted due to an ABI change.
Source
Thrown at linera-bridge/src/proof/mod.rs:457
);
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];
account_owner_bytes.copy_from_slice(&d[96..128]);
Ok(DepositEvent {
source_chain_id: U256::from_be_slice(&d[0..32]),
target_chain_id: ChainId(CryptoHash::from(chain_id_bytes)),
target_application_id: ApplicationId::new(CryptoHash::from(application_id_bytes)),
target_account_owner: AccountOwner::from(account_owner_bytes),
depositor,
token: Address::from_slice(&d[140..160]),View on GitHub (pinned to 6c226ddcb3)
Solutions
- Confirm from the contract ABI that field 5 (offset 128) is an address; if it is bytes32, remove the padding check and read the full word.
- If the ABI reordered fields, recompute every offset (0, 32, 64, 96, 128, 160, 192) — do not patch just this one check.
- Reject the receipt as invalid rather than masking: a non-zero-padded address word in a signature-matched log means the emitter is not the contract the parser was written for.
- Add a regression test that decodes a known-good mainnet receipt so ABI drift is caught before release.
Example fix
// before ensure!(d[128..140] == [0u8; 12], "invalid ABI encoding: address padding bytes (128..140) must be zero"); // after (ABI changed: token is bytes32 at 128..160, amount moved to 160..192) // remove the padding check for this word and read it whole: let token = B256::from_slice(&d[128..160]);
Defensive patterns
Strategy: validation
Validate before calling
fn token_word_is_padded_address(data: &[u8]) -> bool {
data.len() == 224 && data[128..140] == [0u8; 12]
}
if !token_word_is_padded_address(&log.data) {
tracing::warn!("data word 4 is not a left-padded address; ABI mismatch");
continue;
} Try / catch
match parse_deposit_event(&log, bridge_addr) {
Ok(ev) => Some(ev),
Err(e) if e.to_string().contains("address padding bytes (128..140)") => {
tracing::warn!("token field not address-encoded; skipping log");
None
}
Err(e) => { tracing::error!(error = %e, "deposit parse failed"); None }
} Prevention
- Treat padding-check failures as ABI drift alarms; audit the full field layout before changing any offset.
- Lock parser and contract versions; add a canary test decoding one real mainnet deposit receipt per release.
- Keep the offset constants (0,32,64,96,128,160,192) in one place so ABI edits stay coherent.
When it happens
Trigger: The token field in the contract's event is declared bytes32 rather than address; an earlier field's size changed so all following offsets shifted and word 4 now covers different data; a forged or corrupt receipt whose data section is not valid ABI for the expected layout.
Common situations: Contract redeployed changing token from address to bytes32 (or vice versa) without updating the parser; parser offsets edited for a new field order but the padding check still hard-codes 128..140; decoding receipts from a mismatched chain fork where the event layout differs.
Related errors
- invalid ABI encoding: depositor topic padding bytes (0..12)
- expected exactly 2 topics (signature + indexed depositor), g
- expected 224 bytes of event data (7 x 32), got {}
- transaction {tx_hash} reverted (receipt status 0)
- transaction {tx_hash} not confirmed within {RECEIPT_TIMEOUT:
AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22).
Data as JSON: /api/errors/a36d848308ecbd60.
Report an issue: GitHub.