Skip to main content

Expressions

Static offsets work for simple variables, but most interesting data has locations that depend on runtime values. Expressions let pointers compute addresses dynamically.

To evaluate expressions against live EVM state interactively, see the Pointer playground.

Why expressions are needed​

Consider reading element i from a memory array. The element's location depends on:

  • Where the array starts (might come from the free memory pointer)
  • Which element we want (the index i)
  • How big each element is (32 bytes for uint256)

A static pointer can't capture this, but an expression can — for example, array-start + index × 32 computes the element's offset from the array's base and the index.

Arithmetic expressions​

Basic math operations for computing addresses:

$sum — Addition​

Adds all values in an array.

$difference — Subtraction​

Subtracts the second value from the first (saturates at zero).

$product — Multiplication​

Multiplies all values in an array.

$quotient — Division​

Integer division of the first value by the second.

$remainder — Modulo​

Remainder after division.

Reading values​

$read — Read from a named region​

Reads the bytes from a previously defined region. For example, a group can name an array-length-slot region and then use { $read: "array-length-slot" } to retrieve the array's length at runtime and use it in a later computation.

Region property lookups​

Reference properties of named regions with .property syntax:

.offset — Region's offset​

{ ".offset": "previous-element" }

Returns the offset of the named region.

.length — Region's length​

{ ".length": "previous-element" }

Returns the length of the named region.

.slot — Region's slot​

Returns the slot number for storage/stack/transient regions.

Chaining lookups​

Property lookups can compute the next element's position from the previous one. For example, element-1's offset can be computed as the sum of element-0's .offset and its .length.

Computing storage slots with $keccak256​

Solidity uses keccak256 hashing to compute storage locations for dynamic data.

Array element slots​

For a dynamic array at slot n, elements start at keccak256(n), so element i lives at keccak256(n) + i.

Mapping value slots​

For a mapping at slot n, the value for key k is at keccak256(k, n).

Nested mappings​

For mapping(address => mapping(uint => uint)) at slot 2, the value is at keccak256(inner_key, keccak256(outer_key, 2)).

Data manipulation​

$concat — Concatenate bytes​

Joins byte sequences without padding. Useful for building hash inputs from multiple values.

$sized<N> — resize to N bytes​

Truncates or pads to exactly N bytes. Pads with zeros on the left; truncates from the left if too long.

$wordsized — Resize to word size​

Equivalent to $sized32 on the EVM (pads or truncates to 32 bytes).

Variables in expressions​

Expressions can reference variables by name. These come from list pointer contexts: within a list, the variable named by each takes values from 0 to count-1, computing each element's slot.

Complete example: dynamic array element​

To read element i from uint256[] storage arr at slot 5, a pointer:

  1. Defines the array's base slot
  2. Computes the element's slot: keccak256(5) + element_index
  3. Returns that storage location

Learn more​