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 describes what is known following that instruction's execution: the fact that a function was invoked holds from that point forward. Pointers within the context reference the machine state at the instruction's trace step — the state a debugger observes when it encounters the instruction.
For internal calls, this context is typically placed on the callee's entry JUMPDEST rather than the caller's JUMP. JUMP consumes its destination operand from the stack; at the entry JUMPDEST, 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, where the call parameters are visible on the stack.
- 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 describes what is known following that
instruction's execution: the context's semantic facts (e.g.,
"a function was invoked") hold from that point forward.
Pointers within the context reference the machine state at
the instruction's trace step, which is the state a debugger
observes when it encounters the instruction.
For internal calls, this context is typically placed on the
callee's entry JUMPDEST rather than the caller's JUMP, because
JUMP consumes its destination operand from the stack. At the
entry JUMPDEST 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, where the call parameters are visible on the stack.
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.
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.
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.
- invoke:
identifier: "transfer"
declaration:
source:
id: 0
range:
offset: 128
length: 95
jump: true
# -----------------------------------------------------------
# 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 (typically
# via `gather`) annotates the inlining.
- 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. Stack-based
# pointers reference the pre-execution state visible in
# the trace step (CALL consumes all 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. Stack-based pointers
# reference the pre-execution state (DELEGATECALL consumes
# all 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. Stack-based
# pointers reference the pre-execution state (CREATE2
# consumes all 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 describes what is known following that\ninstruction's execution: the context's semantic facts (e.g.,\n\"a function was invoked\") hold from that point forward.\nPointers within the context reference the machine state at\nthe instruction's trace step, which is the state a debugger\nobserves when it encounters the instruction.\n\nFor internal calls, this context is typically placed on the\ncallee's entry JUMPDEST rather than the caller's JUMP, because\nJUMP consumes its destination operand from the stack. At the\nentry JUMPDEST the remaining stack (return address, arguments)\nis stable and directly addressable.\n\nFor external calls and contract creations, this context marks\nthe CALL/DELEGATECALL/STATICCALL/CREATE/CREATE2 instruction\nitself, where the call parameters are visible on the stack.\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",
"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",
"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
}
},
{
"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.
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",
"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.
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",
"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.