Collections
While regions describe single contiguous byte ranges, collections aggregate multiple pointers together. Collections handle cases where data structures span multiple locations or have dynamic configurations.
Why collections?
Consider a Solidity struct with multiple fields, or a dynamic array whose length isn't known at compile time. These require more than pointing to a single memory location—they need to describe relationships between multiple pointers or generate pointers based on runtime values.
Collections provide six patterns for composing pointers:
| Collection | Purpose |
|---|---|
group | Combine multiple pointers into one |
list | Generate pointers for indexed sequences |
conditional | Choose between pointers based on a condition |
scope | Define variables for use in nested pointers |
reference | Invoke reusable pointer templates |
templates | Define inline templates for local reuse |
Click "▶ Try it" on any example to load it into the Pointer Playground drawer at the bottom of the screen.
Group
A group combines multiple pointers into a single composite pointer. Each pointer in the group can have a name for identification.
{
"group": [
{
"name": "len",
"location": "storage",
"slot": 0
},
{
"name": "data",
"location": "storage",
"slot": 1
}
]
}
Groups are useful for structs and other compound data types where multiple fields need to be accessed together.
List
A list generates a sequence of pointers based on a count expression. This handles dynamic arrays and other indexed collections.
{
"list": {
"count": 3,
"each": "i",
"is": {
"name": "element",
"location": "storage",
"slot": {
"$sum": [
10,
"i"
]
}
}
}
}
The list evaluates count to determine how many pointers to generate, then
for each index (bound to the variable named by each), it evaluates the is
pointer template.
List with dynamic count
When the count comes from storage:
{
"group": [
{
"name": "arrayLength",
"location": "storage",
"slot": 0
},
{
"list": {
"count": {
"$read": "arrayLength"
},
"each": "i",
"is": {
"name": "element",
"location": "storage",
"slot": {
"$sum": [
1,
"i"
]
}
}
}
}
]
}
List properties
| Property | Description |
|---|---|
count | Expression evaluating to the number of items |
each | Variable name for the current index (starting at 0) |
is | Pointer template evaluated for each index |
Conditional
A conditional selects between pointers based on whether an expression evaluates to a non-zero value.
{
"group": [
{
"name": "flag",
"location": "storage",
"slot": 0
},
{
"if": {
"$read": "flag"
},
"then": {
"name": "whenTrue",
"location": "storage",
"slot": 1
},
"else": {
"name": "whenFalse",
"location": "storage",
"slot": 2
}
}
]
}
Conditional properties
| Property | Description |
|---|---|
if | Expression to evaluate (non-zero = true) |
then | Pointer to use when condition is true |
else | Optional pointer when condition is false |
Scope
A scope defines variables that can be used in a nested pointer. Variables are evaluated in order, so later variables can reference earlier ones.
{
"define": {
"baseSlot": 3,
"offset": 2
},
"in": {
"name": "result",
"location": "storage",
"slot": {
"$sum": [
"baseSlot",
"offset"
]
}
}
}
Scopes help break complex pointer definitions into readable steps. For examples combining scopes with keccak256 for storage slot computation, see the expressions documentation.
Scope properties
| Property | Description |
|---|---|
define | Object mapping variable names to expressions |
in | Pointer where defined variables are available |
Reference and Templates
A reference invokes a named pointer template, while templates defines them inline. These work together for reusable pointer patterns.
{
"templates": {
"read-slot": {
"expect": [
"n"
],
"for": {
"name": "slot",
"location": "storage",
"slot": "n"
}
}
},
"in": {
"group": [
{
"define": {
"n": 0
},
"in": {
"template": "read-slot"
}
},
{
"define": {
"n": 1
},
"in": {
"template": "read-slot"
}
}
]
}
}
Template definition
Each template in the templates object has:
| Property | Description |
|---|---|
expect | Array of required variable names |
for | The pointer template body |
Reference properties
| Property | Description |
|---|---|
template | Name of the template to invoke |
yields | Optional object mapping template region names to new names |
Nesting collections
Collections can be nested to build complex pointer structures. A group might contain lists, conditionals might wrap groups, and scopes can define variables used throughout nested collections.
{
"group": [
{
"name": "lengthSlot",
"location": "storage",
"slot": 0
},
{
"define": {
"len": {
"$read": "lengthSlot"
}
},
"in": {
"if": "len",
"then": {
"list": {
"count": "len",
"each": "i",
"is": {
"name": "item",
"location": "storage",
"slot": {
"$sum": [
1,
"i"
]
}
}
}
}
}
}
]
}
Named regions in collections
Pointers within collections can include a name property. Named regions are
tracked during resolution and can be referenced using the .slot, .offset,
and .length syntax in expressions.
{
"group": [
{
"name": "header",
"location": "storage",
"slot": 0
},
{
"name": "body",
"location": "storage",
"slot": {
"$sum": [
{
".slot": "header"
},
1
]
}
}
]
}
Learn more
- Regions for simple pointer definitions
- Expressions for dynamic value computation
- Pointer specification for formal definitions