Skip to main content

Crate bal_layout

Crate bal_layout 

Source
Expand description

§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 alloy_primitives::{B256, U256};
use bal_layout::{Layout, Value};

let layout = Layout::from_json(r#"{
  "storage": [
    {"label":"counter","slot":"0","offset":0,"type":"t_uint256"},
    {"label":"paused","slot":"1","offset":0,"type":"t_bool"},
    {"label":"owner","slot":"1","offset":1,"type":"t_address"},
    {"label":"balances","slot":"2","offset":0,"type":"t_mapping(t_address,t_uint256)"}
  ],
  "types": {
    "t_uint256": {"encoding":"inplace","label":"uint256","numberOfBytes":"32"},
    "t_bool":    {"encoding":"inplace","label":"bool","numberOfBytes":"1"},
    "t_address": {"encoding":"inplace","label":"address","numberOfBytes":"20"},
    "t_mapping(t_address,t_uint256)": {"encoding":"mapping","label":"mapping(address => uint256)",
        "numberOfBytes":"32","key":"t_address","value":"t_uint256"}
  }}"#)?;

// packed: `paused` is byte 0 of slot 1, `owner` bytes 1..21
let owner = layout.locate("owner")?;
assert_eq!((owner.offset, owner.size), (1, 20));

// mapping key → slot = keccak(pad32(key) ‖ slot)
let bal = layout.locate("balances[0x000000000000000000000000000000000000dEaD]")?;
assert_ne!(bal.slot, B256::ZERO);

let word = B256::from((U256::from(1u8) | (U256::from(0xdead_u64) << 8usize)).to_be_bytes::<32>());
assert_eq!(layout.decode(&layout.locate("paused")?, word), Value::Bool(true));

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 at base; Layout::erc7201_slot(id) computes the namespace slot. A manifest file does both declaratively:

    { "base": "out/Vault.sol/Vault.json",
      "namespaces": [
        { "prefix": "erc20", "layout": "out/ERC20Storage.sol/ERC20Storage.json", "erc7201": "openzeppelin.storage.ERC20" },
        { "prefix": "app",   "layout": "out/AppStorage.sol/AppStorage.json",     "slot": "0x…" } ] }

    Layout::from_artifact recognises it. The namespace layout is any solc storageLayout whose 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) names balances[0x…] when the key is among the candidates — keccak is one-way, but one keccak per guess is cheap.

bal-layout: solc storageLayout → slot arithmetic and typed decoding.

Knows nothing about where words come from. Give it a path like balances[0xabc…], totals.index, nested[0xabc…][7], items[2], items.length, and it answers with a Location (slot, byte offset, size, type). Give it a 32-byte word and a location, and it decodes a Value. The reverse direction — “which field is slot X?” — works for everything except mapping entries (keccak is one-way; see Layout::describe_slot).

Structs§

Layout
A parsed storage layout.
Location
Where a value lives.
StorageEntry
One variable (or struct member) as solc reports it.
TypeInfo
One entry of the layout’s types table.

Enums§

Encoding
solc storage encodings.
LayoutError
Layout parsing and path resolution failures.
PathKind
What a storage path names.
Value
A decoded value. Raw means the layout knew the location but not how to read it (dynamic bytes/strings, unknown encodings): the word is shown as is rather than guessed.
ValueKind
How a decoded value should be read by a caller that only sees text.

Type Aliases§

Result
Result of layout operations.