Invocation contexts
An invoke context marks an instruction that enters a function. The schema distinguishes three kinds of invocation — internal calls (via JUMP), external message calls (CALL/DELEGATECALL/ STATICCALL), and contract creations (CREATE/CREATE2) — each with fields appropriate to the call mechanism.
All variants extend the function identity schema and may include pointers to the call target, arguments, gas, value, and input data as applicable. See Tracing execution for worked examples showing how debuggers use invoke and return contexts to reconstruct call stacks.
Pointer evaluation and instruction placement
An instruction's context holds following that instruction's execution: the fact that a function was invoked holds from that point forward, and pointers within the context resolve against the machine state after the instruction has executed (see instruction). The operand pointers of an external call or contract creation are the one exception.
For internal calls, this context is typically placed on the callee's entry JUMPDEST. The caller's JUMP has consumed its destination operand by then, and JUMPDEST leaves the stack unchanged, so after it executes the remaining stack (return address followed by arguments) is stable and directly addressable.
For external calls and contract creations, this context
marks the CALL/DELEGATECALL/STATICCALL/CREATE/CREATE2
instruction itself: the invocation occurs when that instruction
executes. The pointer fields of a message or create
invocation (target, gas, value, input, salt) describe
the operands of the marked instruction, which the instruction
consumes. These pointers therefore resolve against the machine
state immediately before the marked instruction executes. A
debugger that is paused about to execute the CALL can resolve
them against the state it observes.
- Explore
- View source
- Playground
- YAML
- JSON
$schema: "https://json-schema.org/draft/2020-12/schema"
$id: "schema:ethdebug/format/program/context/function/invoke"
title: ethdebug/format/program/context/function/invoke
description: |
This context indicates that the marked instruction is
associated with a function invocation. The invocation is one
of three kinds: an internal call via JUMP, an external message
call (CALL / DELEGATECALL / STATICCALL), or a contract
creation (CREATE / CREATE2).
Extends the function identity schema with kind-specific fields
such as call targets, gas, value, and input data.
Per the **ethdebug/format/program/instruction** schema, an
instruction's context holds following that instruction's
execution: the context's semantic facts (e.g., "a function was
invoked") hold from that point forward, and pointers within
the context resolve against the machine state after the
instruction has executed. The operand pointers of an external
call or contract creation are the one exception, described
below.
For internal calls, this context is typically placed on the
callee's entry JUMPDEST. The caller's JUMP has consumed its
destination operand by then, and JUMPDEST leaves the stack
unchanged, so after it executes the remaining stack (return
address, arguments) is stable and directly addressable.
For external calls and contract creations, this context marks
the CALL/DELEGATECALL/STATICCALL/CREATE/CREATE2 instruction
itself: the invocation occurs when that instruction executes.
The pointer fields of a `message` or `create` invocation
(`target`, `gas`, `value`, `input`, `salt`) describe the
operands of the marked instruction, which the instruction
consumes. These pointers therefore resolve against the machine
state immediately **before** the marked instruction executes.
type: object
properties:
invoke:
type: object
title: Function invocation
description: |
Describes the function invocation associated with this
context. Must indicate exactly one invocation kind: `jump`
for an internal call, `message` for an external call, or
`create` for a contract creation.
$ref: "schema:ethdebug/format/program/context/function"
properties:
activation:
type: string
title: Activation identifier
description: |
Correlation identifier pairing this invocation with its
matching return or revert. The invoke that opens an
activation and the return or revert that closes it carry
the same value; distinct activations carry distinct
values, unique within the program. Lets a debugger pair a
call with its return independent of trace order. Optional.
allOf:
- oneOf:
- required: [jump]
- required: [message]
- required: [create]
- if:
required: [jump]
then:
$ref: "#/$defs/InternalCall"
- if:
required: [message]
then:
$ref: "#/$defs/ExternalCall"
- if:
required: [create]
then:
$ref: "#/$defs/ContractCreation"
unevaluatedProperties: false
required:
- invoke
$defs:
InternalCall:
title: Internal call
description: |
An internal function call within the same contract. This
context is typically placed on the callee's entry JUMPDEST;
the caller's JUMP has already consumed the destination from
the stack, so pointer slot values reflect the post-JUMP
layout.
type: object
properties:
jump:
description: |
Indicates this is an internal function call (JUMP/JUMPI).
const: true
target:
type: object
title: Invocation target
description: |
Pointer to the target of the invocation. For internal
calls, this typically points to a code location.
Optional: may be omitted when there is no meaningful
target pointer to record, e.g., at the first
instruction of an inlined function body where the
inlining pass has elided the JUMP that would normally
carry this pointer. The callee identity
(`identifier`, `declaration`, `type`) is still
meaningful in this case.
properties:
pointer:
$ref: "schema:ethdebug/format/pointer"
required:
- pointer
additionalProperties: false
arguments:
type: object
title: Function arguments
description: |
Pointer to the arguments for an internal function call.
properties:
pointer:
$ref: "schema:ethdebug/format/pointer"
required:
- pointer
additionalProperties: false
required: [jump]
ExternalCall:
title: External call
description: |
An external message call to another contract via CALL,
DELEGATECALL, or STATICCALL. Set `delegate` or `static` to
`true` to indicate the call variant; if neither is present
the call is a regular CALL.
This context marks the call instruction itself. The `target`,
`gas`, `value`, and `input` pointers describe that
instruction's operands, so they resolve against the machine
state immediately **before** it executes.
type: object
properties:
message:
description: |
Indicates this is an external message call (CALL,
DELEGATECALL, or STATICCALL).
const: true
target:
type: object
title: Invocation target
description: |
Pointer to the target of the invocation. For external
calls, this points to the address and/or selector
being called.
properties:
pointer:
$ref: "schema:ethdebug/format/pointer"
required:
- pointer
additionalProperties: false
gas:
type: object
title: Gas allocation
description: |
Pointer to the gas allocated for the external call.
properties:
pointer:
$ref: "schema:ethdebug/format/pointer"
required:
- pointer
additionalProperties: false
value:
type: object
title: ETH value
description: |
Pointer to the amount of ETH being sent with the call.
properties:
pointer:
$ref: "schema:ethdebug/format/pointer"
required:
- pointer
additionalProperties: false
input:
type: object
title: Call input data
description: |
Pointer to the input data for the external call.
properties:
pointer:
$ref: "schema:ethdebug/format/pointer"
required:
- pointer
additionalProperties: false
delegate:
description: |
Indicates this external call is a DELEGATECALL.
const: true
static:
description: |
Indicates this external call is a STATICCALL.
const: true
not:
description: Only one of `delegate` and `static` can be set at a time.
required: [delegate, static]
required: [message, target]
ContractCreation:
title: Contract creation
description: |
A contract creation via CREATE or CREATE2. The presence
of `salt` distinguishes CREATE2 from CREATE.
This context marks the CREATE or CREATE2 instruction itself.
The `value`, `salt`, and `input` pointers describe that
instruction's operands, so they resolve against the machine
state immediately **before** it executes.
type: object
properties:
create:
description: |
Indicates this is a contract creation operation
(CREATE or CREATE2).
const: true
value:
type: object
title: ETH value
description: |
Pointer to the amount of ETH being sent with the
creation.
properties:
pointer:
$ref: "schema:ethdebug/format/pointer"
required:
- pointer
additionalProperties: false
salt:
type: object
title: CREATE2 salt
description: |
Pointer to the salt value for CREATE2. Its presence
implies this is a CREATE2 operation.
properties:
pointer:
$ref: "schema:ethdebug/format/pointer"
required:
- pointer
additionalProperties: false
input:
type: object
title: Creation bytecode
description: |
Pointer to the creation bytecode for the new contract.
properties:
pointer:
$ref: "schema:ethdebug/format/pointer"
required:
- pointer
additionalProperties: false
required: [create]
examples:
# -----------------------------------------------------------
# Internal call: transfer(address, uint256)
# -----------------------------------------------------------
# This context would mark the JUMPDEST at the entry of the
# `transfer` function. The caller's JUMP has consumed the
# destination from the stack, leaving (top first):
#
# slot 0: return label
# slot 1: first argument (`to`)
# slot 2: second argument (`amount`)
#
# The `target` pointer identifies the function's entry point
# in the bytecode; `arguments` uses a group to name each
# argument's stack position.
- invoke:
identifier: "transfer"
declaration:
source:
id: 0
range:
offset: 128
length: 95
type:
id: 7
jump: true
target:
pointer:
location: code
offset: "0x100"
length: 1
arguments:
pointer:
group:
- name: "to"
location: stack
slot: 1
- name: "amount"
location: stack
slot: 2
# The matching return context carries the same `activation`
# value, pairing this call with its return.
activation: "transfer#0"
# -----------------------------------------------------------
# Inlined internal call: no target pointer
# -----------------------------------------------------------
# When the compiler inlines a function, the JUMP that would
# normally carry the invoke context has been elided — there
# is no physical call instruction and no code target to
# point at. The invoke context still records the callee's
# identity so the debugger can maintain a source-level call
# stack, and a `transform: ["inline"]` context annotates the
# inlining — composed flat alongside the invoke on the same
# context object (the two carry disjoint keys).
- invoke:
identifier: "transfer"
declaration:
source:
id: 0
range:
offset: 128
length: 95
jump: true
# Correlation id: the matching inlined `return` carries the
# same value, so the two pair even if `transfer` is inlined
# at several sites.
activation: "transfer#0"
# -----------------------------------------------------------
# External CALL: token.balanceOf(account)
# -----------------------------------------------------------
# This context marks the CALL instruction. Its pointers
# describe the operands of the CALL, so they resolve against
# the state before the CALL executes (CALL consumes all of
# its stack operands):
#
# slot 0: gas to forward
# slot 1: target contract address
# slot 2: value (0 — balanceOf is non-payable)
#
# The ABI-encoded calldata has already been written to
# memory at 0x80:
#
# 0x80..0x83: function selector (4 bytes)
# 0x84..0xa3: abi-encoded `account` (32 bytes)
- invoke:
identifier: "balanceOf"
message: true
target:
pointer:
location: stack
slot: 1
gas:
pointer:
location: stack
slot: 0
value:
pointer:
location: stack
slot: 2
input:
pointer:
group:
- name: "selector"
location: memory
offset: "0x80"
length: 4
- name: "arguments"
location: memory
offset: "0x84"
length: "0x20"
# -----------------------------------------------------------
# DELEGATECALL: proxy forwarding calldata
# -----------------------------------------------------------
# This context marks a DELEGATECALL instruction in a proxy
# contract. The call executes the implementation's code
# within the proxy's storage context. The pointers describe
# the operands of the DELEGATECALL, so they resolve against
# the state before it executes (DELEGATECALL consumes all of
# its stack operands):
#
# slot 0: gas
# slot 1: implementation address
#
# The original calldata has been copied into memory:
#
# 0x80..0xe3: forwarded calldata (100 bytes)
- invoke:
message: true
delegate: true
target:
pointer:
location: stack
slot: 1
gas:
pointer:
location: stack
slot: 0
input:
pointer:
location: memory
offset: "0x80"
length: "0x64"
# -----------------------------------------------------------
# CREATE2: deploying a child contract
# -----------------------------------------------------------
# This context marks the CREATE2 instruction. The pointers
# describe the operands of the CREATE2, so they resolve
# against the state before it executes (CREATE2 consumes all
# of its stack operands). The EVM stack layout for
# CREATE2 (top first):
#
# slot 0: value (ETH to send to the new contract)
# slot 1: offset (memory offset of init code)
# slot 2: length (byte length of init code)
# slot 3: salt (for deterministic address derivation)
#
# The init code has been placed in memory:
#
# 0x80..0x027f: creation bytecode (512 bytes)
- invoke:
create: true
value:
pointer:
location: stack
slot: 0
salt:
pointer:
location: stack
slot: 3
input:
pointer:
location: memory
offset: "0x80"
length: "0x200"
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "schema:ethdebug/format/program/context/function/invoke",
"title": "ethdebug/format/program/context/function/invoke",
"description": "This context indicates that the marked instruction is\nassociated with a function invocation. The invocation is one\nof three kinds: an internal call via JUMP, an external message\ncall (CALL / DELEGATECALL / STATICCALL), or a contract\ncreation (CREATE / CREATE2).\n\nExtends the function identity schema with kind-specific fields\nsuch as call targets, gas, value, and input data.\n\nPer the **ethdebug/format/program/instruction** schema, an\ninstruction's context holds following that instruction's\nexecution: the context's semantic facts (e.g., \"a function was\ninvoked\") hold from that point forward, and pointers within\nthe context resolve against the machine state after the\ninstruction has executed. The operand pointers of an external\ncall or contract creation are the one exception, described\nbelow.\n\nFor internal calls, this context is typically placed on the\ncallee's entry JUMPDEST. The caller's JUMP has consumed its\ndestination operand by then, and JUMPDEST leaves the stack\nunchanged, so after it executes the remaining stack (return\naddress, arguments) is stable and directly addressable.\n\nFor external calls and contract creations, this context marks\nthe CALL/DELEGATECALL/STATICCALL/CREATE/CREATE2 instruction\nitself: the invocation occurs when that instruction executes.\nThe pointer fields of a `message` or `create` invocation\n(`target`, `gas`, `value`, `input`, `salt`) describe the\noperands of the marked instruction, which the instruction\nconsumes. These pointers therefore resolve against the machine\nstate immediately **before** the marked instruction executes.\n",
"type": "object",
"properties": {
"invoke": {
"type": "object",
"title": "Function invocation",
"description": "Describes the function invocation associated with this\ncontext. Must indicate exactly one invocation kind: `jump`\nfor an internal call, `message` for an external call, or\n`create` for a contract creation.\n",
"$ref": "schema:ethdebug/format/program/context/function",
"properties": {
"activation": {
"type": "string",
"title": "Activation identifier",
"description": "Correlation identifier pairing this invocation with its\nmatching return or revert. The invoke that opens an\nactivation and the return or revert that closes it carry\nthe same value; distinct activations carry distinct\nvalues, unique within the program. Lets a debugger pair a\ncall with its return independent of trace order. Optional.\n"
}
},
"allOf": [
{
"oneOf": [
{
"required": [
"jump"
]
},
{
"required": [
"message"
]
},
{
"required": [
"create"
]
}
]
},
{
"if": {
"required": [
"jump"
]
},
"then": {
"$ref": "#/$defs/InternalCall"
}
},
{
"if": {
"required": [
"message"
]
},
"then": {
"$ref": "#/$defs/ExternalCall"
}
},
{
"if": {
"required": [
"create"
]
},
"then": {
"$ref": "#/$defs/ContractCreation"
}
}
],
"unevaluatedProperties": false
}
},
"required": [
"invoke"
],
"$defs": {
"InternalCall": {
"title": "Internal call",
"description": "An internal function call within the same contract. This\ncontext is typically placed on the callee's entry JUMPDEST;\nthe caller's JUMP has already consumed the destination from\nthe stack, so pointer slot values reflect the post-JUMP\nlayout.\n",
"type": "object",
"properties": {
"jump": {
"description": "Indicates this is an internal function call (JUMP/JUMPI).\n",
"const": true
},
"target": {
"type": "object",
"title": "Invocation target",
"description": "Pointer to the target of the invocation. For internal\ncalls, this typically points to a code location.\nOptional: may be omitted when there is no meaningful\ntarget pointer to record, e.g., at the first\ninstruction of an inlined function body where the\ninlining pass has elided the JUMP that would normally\ncarry this pointer. The callee identity\n(`identifier`, `declaration`, `type`) is still\nmeaningful in this case.\n",
"properties": {
"pointer": {
"$ref": "schema:ethdebug/format/pointer"
}
},
"required": [
"pointer"
],
"additionalProperties": false
},
"arguments": {
"type": "object",
"title": "Function arguments",
"description": "Pointer to the arguments for an internal function call.\n",
"properties": {
"pointer": {
"$ref": "schema:ethdebug/format/pointer"
}
},
"required": [
"pointer"
],
"additionalProperties": false
}
},
"required": [
"jump"
]
},
"ExternalCall": {
"title": "External call",
"description": "An external message call to another contract via CALL,\nDELEGATECALL, or STATICCALL. Set `delegate` or `static` to\n`true` to indicate the call variant; if neither is present\nthe call is a regular CALL.\n\nThis context marks the call instruction itself. The `target`,\n`gas`, `value`, and `input` pointers describe that\ninstruction's operands, so they resolve against the machine\nstate immediately **before** it executes.\n",
"type": "object",
"properties": {
"message": {
"description": "Indicates this is an external message call (CALL,\nDELEGATECALL, or STATICCALL).\n",
"const": true
},
"target": {
"type": "object",
"title": "Invocation target",
"description": "Pointer to the target of the invocation. For external\ncalls, this points to the address and/or selector\nbeing called.\n",
"properties": {
"pointer": {
"$ref": "schema:ethdebug/format/pointer"
}
},
"required": [
"pointer"
],
"additionalProperties": false
},
"gas": {
"type": "object",
"title": "Gas allocation",
"description": "Pointer to the gas allocated for the external call.\n",
"properties": {
"pointer": {
"$ref": "schema:ethdebug/format/pointer"
}
},
"required": [
"pointer"
],
"additionalProperties": false
},
"value": {
"type": "object",
"title": "ETH value",
"description": "Pointer to the amount of ETH being sent with the call.\n",
"properties": {
"pointer": {
"$ref": "schema:ethdebug/format/pointer"
}
},
"required": [
"pointer"
],
"additionalProperties": false
},
"input": {
"type": "object",
"title": "Call input data",
"description": "Pointer to the input data for the external call.\n",
"properties": {
"pointer": {
"$ref": "schema:ethdebug/format/pointer"
}
},
"required": [
"pointer"
],
"additionalProperties": false
},
"delegate": {
"description": "Indicates this external call is a DELEGATECALL.\n",
"const": true
},
"static": {
"description": "Indicates this external call is a STATICCALL.\n",
"const": true
}
},
"not": {
"description": "Only one of `delegate` and `static` can be set at a time.",
"required": [
"delegate",
"static"
]
},
"required": [
"message",
"target"
]
},
"ContractCreation": {
"title": "Contract creation",
"description": "A contract creation via CREATE or CREATE2. The presence\nof `salt` distinguishes CREATE2 from CREATE.\n\nThis context marks the CREATE or CREATE2 instruction itself.\nThe `value`, `salt`, and `input` pointers describe that\ninstruction's operands, so they resolve against the machine\nstate immediately **before** it executes.\n",
"type": "object",
"properties": {
"create": {
"description": "Indicates this is a contract creation operation\n(CREATE or CREATE2).\n",
"const": true
},
"value": {
"type": "object",
"title": "ETH value",
"description": "Pointer to the amount of ETH being sent with the\ncreation.\n",
"properties": {
"pointer": {
"$ref": "schema:ethdebug/format/pointer"
}
},
"required": [
"pointer"
],
"additionalProperties": false
},
"salt": {
"type": "object",
"title": "CREATE2 salt",
"description": "Pointer to the salt value for CREATE2. Its presence\nimplies this is a CREATE2 operation.\n",
"properties": {
"pointer": {
"$ref": "schema:ethdebug/format/pointer"
}
},
"required": [
"pointer"
],
"additionalProperties": false
},
"input": {
"type": "object",
"title": "Creation bytecode",
"description": "Pointer to the creation bytecode for the new contract.\n",
"properties": {
"pointer": {
"$ref": "schema:ethdebug/format/pointer"
}
},
"required": [
"pointer"
],
"additionalProperties": false
}
},
"required": [
"create"
]
}
},
"examples": [
{
"invoke": {
"identifier": "transfer",
"declaration": {
"source": {
"id": 0
},
"range": {
"offset": 128,
"length": 95
}
},
"type": {
"id": 7
},
"jump": true,
"target": {
"pointer": {
"location": "code",
"offset": "0x100",
"length": 1
}
},
"arguments": {
"pointer": {
"group": [
{
"name": "to",
"location": "stack",
"slot": 1
},
{
"name": "amount",
"location": "stack",
"slot": 2
}
]
}
},
"activation": "transfer#0"
}
},
{
"invoke": {
"identifier": "transfer",
"declaration": {
"source": {
"id": 0
},
"range": {
"offset": 128,
"length": 95
}
},
"jump": true,
"activation": "transfer#0"
}
},
{
"invoke": {
"identifier": "balanceOf",
"message": true,
"target": {
"pointer": {
"location": "stack",
"slot": 1
}
},
"gas": {
"pointer": {
"location": "stack",
"slot": 0
}
},
"value": {
"pointer": {
"location": "stack",
"slot": 2
}
},
"input": {
"pointer": {
"group": [
{
"name": "selector",
"location": "memory",
"offset": "0x80",
"length": 4
},
{
"name": "arguments",
"location": "memory",
"offset": "0x84",
"length": "0x20"
}
]
}
}
}
},
{
"invoke": {
"message": true,
"delegate": true,
"target": {
"pointer": {
"location": "stack",
"slot": 1
}
},
"gas": {
"pointer": {
"location": "stack",
"slot": 0
}
},
"input": {
"pointer": {
"location": "memory",
"offset": "0x80",
"length": "0x64"
}
}
}
},
{
"invoke": {
"create": true,
"value": {
"pointer": {
"location": "stack",
"slot": 0
}
},
"salt": {
"pointer": {
"location": "stack",
"slot": 3
}
},
"input": {
"pointer": {
"location": "memory",
"offset": "0x80",
"length": "0x200"
}
}
}
}
]
}
Internal call
An internal call represents a function call within the same contract. This context is typically placed on the callee's entry JUMPDEST; the caller's JUMP has already consumed the destination from the stack, so pointer slot values reflect the post-JUMP layout. The target points to a code location and arguments are passed on the stack.
The target field is optional. It may be omitted when there is no
meaningful code pointer to record — most notably at the first
instruction of an inlined function body, where the inlining pass
has elided the JUMP that would normally carry the target. The
callee identity (identifier, declaration, type) remains
meaningful in this case; a sibling transform: ["inline"] key
on the same context indicates that the call was inlined rather
than physically invoked.
- Explore
- View source
- Playground
- YAML
- JSON
title: Internal call
description: |
An internal function call within the same contract. This
context is typically placed on the callee's entry JUMPDEST;
the caller's JUMP has already consumed the destination from
the stack, so pointer slot values reflect the post-JUMP
layout.
type: object
properties:
jump:
description: |
Indicates this is an internal function call (JUMP/JUMPI).
const: true
target:
type: object
title: Invocation target
description: |
Pointer to the target of the invocation. For internal
calls, this typically points to a code location.
Optional: may be omitted when there is no meaningful
target pointer to record, e.g., at the first
instruction of an inlined function body where the
inlining pass has elided the JUMP that would normally
carry this pointer. The callee identity
(`identifier`, `declaration`, `type`) is still
meaningful in this case.
properties:
pointer:
$ref: "schema:ethdebug/format/pointer"
required:
- pointer
additionalProperties: false
arguments:
type: object
title: Function arguments
description: |
Pointer to the arguments for an internal function call.
properties:
pointer:
$ref: "schema:ethdebug/format/pointer"
required:
- pointer
additionalProperties: false
required: [ jump ]
{
"title": "Internal call",
"description": "An internal function call within the same contract. This\ncontext is typically placed on the callee's entry JUMPDEST;\nthe caller's JUMP has already consumed the destination from\nthe stack, so pointer slot values reflect the post-JUMP\nlayout.\n",
"type": "object",
"properties": {
"jump": {
"description": "Indicates this is an internal function call (JUMP/JUMPI).\n",
"const": true
},
"target": {
"type": "object",
"title": "Invocation target",
"description": "Pointer to the target of the invocation. For internal\ncalls, this typically points to a code location.\nOptional: may be omitted when there is no meaningful\ntarget pointer to record, e.g., at the first\ninstruction of an inlined function body where the\ninlining pass has elided the JUMP that would normally\ncarry this pointer. The callee identity\n(`identifier`, `declaration`, `type`) is still\nmeaningful in this case.\n",
"properties": {
"pointer": {
"$ref": "schema:ethdebug/format/pointer"
}
},
"required": [
"pointer"
],
"additionalProperties": false
},
"arguments": {
"type": "object",
"title": "Function arguments",
"description": "Pointer to the arguments for an internal function call.\n",
"properties": {
"pointer": {
"$ref": "schema:ethdebug/format/pointer"
}
},
"required": [
"pointer"
],
"additionalProperties": false
}
},
"required": [
"jump"
]
}
Inlined internal calls
When the compiler inlines a callee, there is no JUMP and no runtime activation record: the callee's instructions are spliced directly into the caller. The call still happened at the source level, so it is still marked with an invoke context — one that describes the kind of call without a physical target:
jump: truemarks the invocation as an internal call kind (as opposed to a message call or contract creation). It does not assert that a JUMP instruction executes here — an inlined call has none.targetis omitted: there is no code location to point at, because the JUMP that would carry it was elided.- a sibling
transform: ["inline"]key marks the instruction as belonging to an inlined body.
The callee identity (identifier, declaration, type, all
optional) is preserved, so the inlined function still appears on
the debugger's call stack: the debugger reconstructs a virtual
activation for it (see
Reconstructing activations).
Compilers typically inline small or leaf non-recursive callees at every call site, so the same callee can produce several independent virtual activations across a trace — one per inlined site. (The precise eligibility rule is a compiler choice; the format is the same however inlining decisions are made.)
External call
An external call represents a call to another contract via CALL,
DELEGATECALL, or STATICCALL. The type of call may be indicated by
setting delegate or static to true. If neither flag is present,
the invocation represents a regular CALL.
- Explore
- View source
- Playground
- YAML
- JSON
title: External call
description: |
An external message call to another contract via CALL,
DELEGATECALL, or STATICCALL. Set `delegate` or `static` to
`true` to indicate the call variant; if neither is present
the call is a regular CALL.
This context marks the call instruction itself. The `target`,
`gas`, `value`, and `input` pointers describe that
instruction's operands, so they resolve against the machine
state immediately **before** it executes.
type: object
properties:
message:
description: |
Indicates this is an external message call (CALL,
DELEGATECALL, or STATICCALL).
const: true
target:
type: object
title: Invocation target
description: |
Pointer to the target of the invocation. For external
calls, this points to the address and/or selector
being called.
properties:
pointer:
$ref: "schema:ethdebug/format/pointer"
required:
- pointer
additionalProperties: false
gas:
type: object
title: Gas allocation
description: |
Pointer to the gas allocated for the external call.
properties:
pointer:
$ref: "schema:ethdebug/format/pointer"
required:
- pointer
additionalProperties: false
value:
type: object
title: ETH value
description: |
Pointer to the amount of ETH being sent with the call.
properties:
pointer:
$ref: "schema:ethdebug/format/pointer"
required:
- pointer
additionalProperties: false
input:
type: object
title: Call input data
description: |
Pointer to the input data for the external call.
properties:
pointer:
$ref: "schema:ethdebug/format/pointer"
required:
- pointer
additionalProperties: false
delegate:
description: |
Indicates this external call is a DELEGATECALL.
const: true
static:
description: |
Indicates this external call is a STATICCALL.
const: true
not:
description: Only one of `delegate` and `static` can be set at a time.
required: [ delegate, static ]
required: [ message, target ]
{
"title": "External call",
"description": "An external message call to another contract via CALL,\nDELEGATECALL, or STATICCALL. Set `delegate` or `static` to\n`true` to indicate the call variant; if neither is present\nthe call is a regular CALL.\n\nThis context marks the call instruction itself. The `target`,\n`gas`, `value`, and `input` pointers describe that\ninstruction's operands, so they resolve against the machine\nstate immediately **before** it executes.\n",
"type": "object",
"properties": {
"message": {
"description": "Indicates this is an external message call (CALL,\nDELEGATECALL, or STATICCALL).\n",
"const": true
},
"target": {
"type": "object",
"title": "Invocation target",
"description": "Pointer to the target of the invocation. For external\ncalls, this points to the address and/or selector\nbeing called.\n",
"properties": {
"pointer": {
"$ref": "schema:ethdebug/format/pointer"
}
},
"required": [
"pointer"
],
"additionalProperties": false
},
"gas": {
"type": "object",
"title": "Gas allocation",
"description": "Pointer to the gas allocated for the external call.\n",
"properties": {
"pointer": {
"$ref": "schema:ethdebug/format/pointer"
}
},
"required": [
"pointer"
],
"additionalProperties": false
},
"value": {
"type": "object",
"title": "ETH value",
"description": "Pointer to the amount of ETH being sent with the call.\n",
"properties": {
"pointer": {
"$ref": "schema:ethdebug/format/pointer"
}
},
"required": [
"pointer"
],
"additionalProperties": false
},
"input": {
"type": "object",
"title": "Call input data",
"description": "Pointer to the input data for the external call.\n",
"properties": {
"pointer": {
"$ref": "schema:ethdebug/format/pointer"
}
},
"required": [
"pointer"
],
"additionalProperties": false
},
"delegate": {
"description": "Indicates this external call is a DELEGATECALL.\n",
"const": true
},
"static": {
"description": "Indicates this external call is a STATICCALL.\n",
"const": true
}
},
"not": {
"description": "Only one of `delegate` and `static` can be set at a time.",
"required": [
"delegate",
"static"
]
},
"required": [
"message",
"target"
]
}
Contract creation
A contract creation represents a CREATE or CREATE2 operation. The
presence of salt implies CREATE2.
- Explore
- View source
- Playground
- YAML
- JSON
title: Contract creation
description: |
A contract creation via CREATE or CREATE2. The presence
of `salt` distinguishes CREATE2 from CREATE.
This context marks the CREATE or CREATE2 instruction itself.
The `value`, `salt`, and `input` pointers describe that
instruction's operands, so they resolve against the machine
state immediately **before** it executes.
type: object
properties:
create:
description: |
Indicates this is a contract creation operation
(CREATE or CREATE2).
const: true
value:
type: object
title: ETH value
description: |
Pointer to the amount of ETH being sent with the
creation.
properties:
pointer:
$ref: "schema:ethdebug/format/pointer"
required:
- pointer
additionalProperties: false
salt:
type: object
title: CREATE2 salt
description: |
Pointer to the salt value for CREATE2. Its presence
implies this is a CREATE2 operation.
properties:
pointer:
$ref: "schema:ethdebug/format/pointer"
required:
- pointer
additionalProperties: false
input:
type: object
title: Creation bytecode
description: |
Pointer to the creation bytecode for the new contract.
properties:
pointer:
$ref: "schema:ethdebug/format/pointer"
required:
- pointer
additionalProperties: false
required: [ create ]
{
"title": "Contract creation",
"description": "A contract creation via CREATE or CREATE2. The presence\nof `salt` distinguishes CREATE2 from CREATE.\n\nThis context marks the CREATE or CREATE2 instruction itself.\nThe `value`, `salt`, and `input` pointers describe that\ninstruction's operands, so they resolve against the machine\nstate immediately **before** it executes.\n",
"type": "object",
"properties": {
"create": {
"description": "Indicates this is a contract creation operation\n(CREATE or CREATE2).\n",
"const": true
},
"value": {
"type": "object",
"title": "ETH value",
"description": "Pointer to the amount of ETH being sent with the\ncreation.\n",
"properties": {
"pointer": {
"$ref": "schema:ethdebug/format/pointer"
}
},
"required": [
"pointer"
],
"additionalProperties": false
},
"salt": {
"type": "object",
"title": "CREATE2 salt",
"description": "Pointer to the salt value for CREATE2. Its presence\nimplies this is a CREATE2 operation.\n",
"properties": {
"pointer": {
"$ref": "schema:ethdebug/format/pointer"
}
},
"required": [
"pointer"
],
"additionalProperties": false
},
"input": {
"type": "object",
"title": "Creation bytecode",
"description": "Pointer to the creation bytecode for the new contract.\n",
"properties": {
"pointer": {
"$ref": "schema:ethdebug/format/pointer"
}
},
"required": [
"pointer"
],
"additionalProperties": false
}
},
"required": [
"create"
]
}
Reconstructing activations
A debugger reconstructs the logical call stack from invoke and
return contexts. Each entry on that stack is an activation
(the DWARF term for a call-stack entry). Activation handling is
uniform whether or not the call was inlined:
- Push an activation when an
invokecontext is encountered and pop it when the matchingreturncontext is encountered, in trace order. Theinvokeopens the activation inclusive of its instruction; thereturncloses it after its instruction, so the instruction bearingreturnis still inside the activation.
Because push/pop is driven by where the invoke and return
contexts sit, a compiler must emit them as a bracket: the
invoke on the first instruction of the body and the return on
its last. A compiler must not duplicate invoke or return
across a body's interior instructions — repeating them would push
or pop spurious activations. The sole exception is a body that
compiles to a single instruction, whose entry and exit
coincide: that one instruction legitimately carries both invoke
and return, and a debugger processes them in order (push, then
pop). This bracket rule is what keeps the guarantee below —
that a debugger ignoring transform still sees a coherent
invoke/return pair — true for inlined calls.
An inlined callee therefore appears on the call stack exactly as a
non-inlined one does. Two kinds of activation differ only in how
they are backed, distinguished by the presence of an inline
transform marker — not by whether target is present:
- A real activation comes from an
invokewithout aninlinetransform marker. It corresponds to an actual call at runtime, corroborated by machine state — a return address on the EVM stack — and occupies a real stack region. - A virtual activation comes from an
invokewhose context carriestransform: ["inline"](aninlineidentifier in its transform list). It has no runtime corroboration and occupies no EVM stack region; it exists only in the debug annotations. Itstargetis typically omitted (the JUMP was elided), buttarget-absence is not itself the signal — a real internal call may also omittarget(see Internal call). The reliable discriminator is theinlinemarker.
Activation membership
Push/pop and membership answer two different questions. Push/pop (above) determines when a virtual activation is open — its lifetime on the call stack. Membership determines which open activation a given instruction belongs to. The two are independent, and a debugger uses both.
An instruction belongs to the innermost open virtual activation if
and only if its context carries an inline identifier in its
transform list — so composed markers such as ["inline", "fold"]
still confer membership. The nesting depth is the number of
"inline" occurrences in the list (doubly-inlined code carries
["inline", "inline"]). Membership is determined
per-instruction from this marker, not from instruction ranges:
optimization passes may relocate or interleave an inlined body, so
a positional "everything between the invoke and the return" rule
would be unsound.
This is why membership is separate from lifetime. An activation
opened by an invoke stays open until its return, even across
instructions that are not its members — for example, caller
code an optimizer interleaved into the body's trace span. Such a
non-member instruction (no inline marker for that depth) is
attributed to the enclosing activation, not the inlined one, even
while the virtual activation remains on the stack.
Correlating with activation
The push/pop rule above reconstructs the call stack without any
correlation identifier: it relies on invoke/return appearing
in a well-nested order. That is sufficient for typical compiler
output, but it has a blind spot — push/pop alone can't tell one
activation from another of the same function, so two inlined
copies of a callee placed back-to-back, or an optimizer that moves
a return ahead of its invoke, can defeat strict pairing.
An activation identifier closes it. The invoke object that
opens an activation and the return (or revert) that closes it
carry the same activation value; distinct activations carry
distinct values. Pairing is then explicit and order-independent —
matching values pair an invoke with its return regardless of trace
order, and adjacent same-function activations stay distinct because
their identifiers differ.
Unlike function identity (which function
this is), activation identifies which activation instance this
is, so it lives inside the invoke/return/revert object
rather than as a sibling context. Two activation facts on one instruction
— a tail call's back-edge, which both returns from one activation
and invokes the next — therefore stay flat, each fact carrying its
own activation value, with no gather required.
When activation is present it is authoritative for pairing —
which invoke matches which return. Push/pop remains the fallback
an identifier-less debugger uses; in well-nested output the two
agree. Membership — which instructions belong to a virtual
activation — is unaffected: it stays the per-instruction
inline marker. Attributing a body that
an optimizer relocated to a specific activation would need a
membership-level identifier; that is out of scope here.
Identity and values
Every function-identity field (identifier, declaration,
type) is optional, so a virtual activation degrades gracefully —
from full identity down to an anonymous inlined frame — with no
fabricated data. A debugger renders whatever is present.
An inlining compiler typically preserves the callee's declaration
and per-instruction source ranges for a virtual activation, and can
resolve inlined locals that it homed in addressable memory, via
variables contexts. Identity
fields remain optional and degrade gracefully as described above.
Such a compiler does not emit invoke.arguments or
return.data pointers; individual parameter
values may still be inspectable as locals inside the body where
they are memory-homed. A virtual activation with no resolvable
values is still a valid, displayable frame.
A debugger that ignores transform contexts still sees a coherent
invoke/return pair and a sound source-level call stack. One
that understands them can present virtual activations distinctly —
for example, collapsible and tied to the callee's source location.