Instructions
Each instruction record in a program corresponds to one EVM opcode in the bytecode. Instructions carry the context information that debuggers need.
Structure
An instruction has two required fields:
{
"offset": 42,
"context": { ... }
}
offset: byte position in the bytecode (the program counter value when this instruction executes)context: high-level information valid after this instruction
Context types
The context field can take several forms:
Code context
Maps the instruction to source code:
{
"offset": 42,
"context": {
"code": {
"source": {
"id": 0,
"range": {
"start": { "line": 10, "column": 4 },
"end": { "line": 10, "column": 20 }
}
}
}
}
}
The source field references a source file by ID and specifies the exact
character range.
Variables context
Declares variables that are in scope:
{
"offset": 100,
"context": {
"variables": [
{
"name": "amount",
"type": { "kind": "uint", "bits": 256 },
"pointer": {
"location": "stack",
"slot": 0
}
}
]
}
}
Each variable includes:
name: the identifier from source codetype: an ethdebug/format type referencepointer: where to find the variable's value
Frame context
Indicates call stack changes:
{
"offset": 200,
"context": {
"frame": "step-in"
}
}
Frame values include:
"step-in": entering a function"step-out": returning from a function
Composing contexts
Gather
Combine multiple contexts (like nested scopes):
{
"offset": 150,
"context": {
"gather": [
{
"code": {
"source": { "id": 0, "range": { ... } }
}
},
{
"variables": [
{ "name": "x", "type": { ... }, "pointer": { ... } }
]
}
]
}
}
Pick
Choose between contexts based on runtime conditions:
{
"offset": 175,
"context": {
"pick": [
{
"guard": { "$read": "condition-flag" },
"context": {
"variables": [
{ "name": "result", "type": { ... }, "pointer": { ... } }
]
}
},
{
"context": {
"variables": []
}
}
]
}
}
The debugger evaluates guards at runtime and uses the first matching context. A context without a guard acts as the default.
Remark
Add metadata without affecting scope:
{
"offset": 180,
"context": {
"remark": "loop iteration boundary"
}
}
Instruction ordering
Instructions must be listed in bytecode order, matching the sequence of opcodes. The list is indexed by offset, so debuggers can quickly find the instruction for any program counter value.
Not every byte offset needs an instruction—only positions where opcodes begin. Push data, for instance, doesn't get its own instruction entry.
Learn more
For complete schemas, see: