Skip to main content

Regions

A region represents a contiguous block of bytes in a specific EVM data location. Regions are the leaves of the pointer tree—the actual byte ranges that hold data.

Addressing schemes

The EVM uses two different models for organizing bytes, and regions reflect this:

Slice-based locations

Memory, calldata, returndata, and code are byte-addressable. Regions in these locations use offset and length:

Memory region (slice-based)
Reads 32 bytes starting at offset 0x40 (the free memory pointer)
{
"location": "memory",
"offset": "0x40",
"length": 32
}
  • offset: byte position from the start (required)
  • length: number of bytes (optional; may be computed or implied by type)

Slot-based locations

Storage, transient storage, and stack are organized in 32-byte slots. Regions use slot:

Storage slot (slot-based)
Reads the full 32-byte word at storage slot 5
{
"location": "storage",
"slot": 5
}

For storage and transient storage, values that don't fill a full slot can specify sub-slot positioning:

Packed storage (sub-slot)
Reads 20 bytes at offset 12 within slot 0 (an address in packed storage)
{
"location": "storage",
"slot": 0,
"offset": 12,
"length": 20
}

This addresses 20 bytes starting at byte 12 within slot 0—useful for packed storage.

Location-specific details

Memory

Memory is a simple byte array that grows as needed:

Memory region
Reads 64 bytes starting at offset 0x80
{
"location": "memory",
"offset": "0x80",
"length": 64
}

Memory addresses often come from the free memory pointer (stored at 0x40).

Storage

Storage persists between transactions. Slots are 32-byte words addressed by 256-bit keys:

Storage slot
Reads storage slot 0 (full 256-bit key)
{
"location": "storage",
"slot": "0x0000000000000000000000000000000000000000000000000000000000000000"
}

Slot addresses can be literal numbers, hex strings, or computed expressions.

Stack

The EVM stack holds up to 1024 words. Slot 0 is the top:

Stack position
Reads the top of the stack (slot 0)
{
"location": "stack",
"slot": 0
}

Stack regions are typically read-only from a debugging perspective—you observe values but don't address sub-ranges.

Calldata

Function arguments arrive in calldata, read-only and byte-addressable:

Calldata region
Reads first argument (32 bytes after 4-byte selector)
{
"location": "calldata",
"offset": 4,
"length": 32
}

The first 4 bytes are typically the function selector; arguments follow.

Returndata

After a call, the returned data is accessible:

Returndata region
Reads first 32 bytes of return data
{
"location": "returndata",
"offset": 0,
"length": 32
}

Code

Contract bytecode can be read as data:

Code region
Reads 32 bytes from bytecode at offset 100
{
"location": "code",
"offset": 100,
"length": 32
}

This is used for immutable variables and other data embedded in bytecode.

Transient storage

Transient storage (EIP-1153) persists only within a transaction:

Transient storage
Reads transient storage slot 0
{
"location": "transient",
"slot": 0
}

Uses the same slot-based addressing as regular storage.

Naming regions

Any region can have a name that other parts of the pointer reference:

Named region
Named region 'token-balance' at storage slot 3
{
"name": "token-balance",
"location": "storage",
"slot": 3
}

Names enable:

  • Reading the region's value with { "$read": "token-balance" }
  • Referencing properties with { ".slot": "token-balance" }
  • Building self-documenting pointer structures

Dynamic addresses

Region fields like offset, slot, and length can use expressions to compute values at runtime. This enables pointers for dynamic data like arrays and mappings.

For the full expression language including arithmetic, $keccak256, and value reading, see expressions.

Learn more

For complete schemas for each location type, see the pointer region specification.