bal-layout
solc storageLayout → slot arithmetic and typed decoding. Give it a path
like balances[0xabc…], totals.index, items[2] and it answers with a
slot, byte offset and size; give it a 32-byte word and it decodes the
value. Works the other way too: describe_slot names everything in a slot
except mapping entries (keccak is one-way).
use ;
use ;
let layout = from_json?;
// packed: `paused` is byte 0 of slot 1, `owner` bytes 1..21
let owner = layout.locate?;
assert_eq!;
// mapping key → slot = keccak(pad32(key) ‖ slot)
let bal = layout.locate?;
assert_ne!;
let word = B256from;
assert_eq!;
# Ok::
The layout comes from your compiler — forge inspect C storageLayout, or
extra_output = ["storageLayout"] and the whole artifact — not from the
ABI. typescript(name) emits a TypeScript interface for the same shape;
kind_of(path) says whether a path is a value, struct, mapping or array.
Part of balq.
Beyond one layout
-
ERC-7201 / Diamond.
Layout::mount(prefix, &other, base)adds another layout's variables as a struct atbase;Layout::erc7201_slot(id)computes the namespace slot. A manifest file does both declaratively:Layout::from_artifactrecognises it. The namespace layout is any solcstorageLayoutwhose top-level variables are the struct's members (a one-line contract declaring the struct as its state variable does). -
Dynamic
bytes/string.bytes_data_slots(loc, word)lists the extra slots a long value occupies;decode_bytes(loc, word, chunks)assembles it (Value::Str/Value::Bytes). -
Mapping keys.
describe_slot_with_keys(slot, probe, keys)namesbalances[0x…]when the key is among the candidates —keccakis one-way, but one keccak per guess is cheap.