The interpreter¶
The user-space EVM: it fetches and decodes bytecode (Yellow Paper §9), drives the step loop, and enters sub-frames for the call and create opcodes. This module specifies that machinery in three layers:
- Fetch/decode — read the opcode at the carried program counter,
decode PUSH immediates inline, and map every other byte to its AST
node (an undefined byte decodes to
INVALID). Reading past the end of code yieldsSTOP(YP: implicit halt). - Run loop — interpret steps fetch-then-execute until the active frame stops, threading the carried machine state (program counter, gas, operand-stack cursor, memory cursor) through every step: each step's arguments are supplied from the frame registers and the returned state is assigned back, so no handler ever reads or writes those registers. Popping a pending FrameContinuation resumes a completed child; Empty marks completion of the top-level frame.
- Message calls — run_call handles
CALL/CALLCODE/DELEGATECALL/STATICCALL(multiplexed onmode) and run_create handlesCREATE/CREATE2. A sub-call publishes the parent's carried state to the frame registers, saves them through frame_stack_push, installs the child as the active frame, and returns the child's carried state to the single run loop. When the child halts, that loop restores and resumes the parent. There is no recursive interpreter invocation. All world effects go through kernel syscalls: k_journal_checkpoint on entry, k_transfer for value, and k_journal_revert on failure — the kernel rolls the world back atomically on a reverting child. The applicable rules are EIP-150 (63/64ths gas cap + stipend), EIP-214 (static-context write protection), EIP-2929 (cold/warm access), and EIP-7702 (delegated-code execution).
The decoder¶
function read_push¶
Assembles an n-byte big-endian PUSH immediate from a local code cursor;
bytes past the end of code read as zero.
function read_push(code : CodeSlice, offset : code_pointer, n : push_width) -> word =
slice_load_n(code, offset, n)A source-backed executable byte span. Its length carries the separate representation invariant needed by program-counter arithmetic; this is not a protocol deployment-size limit.
type CodeSlice = {
'off 'len,
code_region_valid_range('off, 'len) & code_valid_length('len).
CodeRegionSliceFields('off, 'len)
}An absolute byte position in the code arena.
type code_pointer = range(0, code_region_bound)The immediate-byte width of a PUSH instruction.
type push_width = range(0, 32)The EVM 256-bit machine word (YP §9.1). A transparent range keeps the mathematical subtype relation visible: narrower non-negative ranges can be passed as words without a model-level conversion.
type word = range(0, 2 ^ 256 - 1)function opcode_available¶
Reports whether an opcode byte is defined by the active fork. Undefined
opcode bytes remain available here and decode to INVALID; this predicate
contains only fork-dependent availability so every interpreter can share
the same deployment rules without duplicating them in its dispatch.
function opcode_available(opcode : opcode, fork : Fork) -> bool =
match opcode {
30 => fork >= Osaka,
72 => fork >= London,
73 => fork >= Cancun,
74 => fork >= Cancun,
75 => fork >= Amsterdam,
92 => fork >= Cancun,
93 => fork >= Cancun,
94 => fork >= Cancun,
95 => fork >= Shanghai,
230 => fork >= Amsterdam,
231 => fork >= Amsterdam,
232 => fork >= Amsterdam,
_ => true,
}EIP-7954 code/initcode size bump (65536/131072).
let Amsterdam : int(amsterdam_fork_value) = sizeof(amsterdam_fork_value)EIP-1153/4844; precompiles 0x01-0x0a.
let Cancun : int(first_blob_fork_value) = sizeof(first_blob_fork_value)EIP-1559 fee market and EIP-3529 refund reduction.
let London : int(london_fork_value) = sizeof(london_fork_value)EIP-7883 modexp gas, EIP-7825 cap; precompile 0x100.
let Osaka : int(osaka_fork_value) = sizeof(osaka_fork_value)EIP-3651 warm coinbase, EIP-3855 PUSH0, EIP-3860 initcode.
let Shanghai : int(shanghai_fork_value) = sizeof(shanghai_fork_value)Every supported protocol and schema fork, in activation order. This is the
sole fork identity in the model: the decoded schema byte selects a
ProtocolProfile, which stores one of these values. The bounded semantic
type prevents values outside the supported fork sequence, while each named
constant retains its precise singleton type for dependent profile typing.
type Fork = range(0, 16)An EVM instruction byte.
type opcode = range(0, 255)function decode_push_immediate¶
Decodes one PUSH immediate and returns the semantic program counter after all encoded immediate bytes. Missing code bytes contribute zero to the value but still belong to the instruction encoding.
function decode_push_immediate(
frame_code : Code,
immediate_offset : code_scan_position,
width : push_width,
) -> (
(code_pointer, word)
) = {
let bytes = code_bytes(frame_code);
let value = read_push(bytes, immediate_offset, width);
(immediate_offset + width, value)
}function code_bytes(code) = struct { bytes = code.bytes, len = code.len }Assembles an n-byte big-endian PUSH immediate from a local code cursor;
bytes past the end of code read as zero.
function read_push(code : CodeSlice, offset : code_pointer, n : push_width) -> word =
slice_load_n(code, offset, n)Existential executable-code value whose concrete byte address and length
remain correlated inside CodeFields.
type Code = {
'off 'len,
code_region_valid_range('off, 'len) & code_valid_length('len).
CodeFields('off, 'len)
}An absolute byte position in the code arena.
type code_pointer = range(0, code_region_bound)An opcode-aligned scan cursor: a code position that still carries the PUSH32 immediate headroom, so reading past the current opcode stays in the code region.
type code_scan_position = range(0, code_region_bound - 32)The immediate-byte width of a PUSH instruction.
type push_width = range(0, 32)The EVM 256-bit machine word (YP §9.1). A transparent range keeps the mathematical subtype relation visible: narrower non-negative ranges can be passed as words without a model-level conversion.
type word = range(0, 2 ^ 256 - 1)function decode_deep_immediate¶
Decodes the immediate of an Amsterdam deep-stack instruction. A valid
immediate advances the counter; an invalid immediate remains unconsumed so
the handler can report InvalidOpcode with the canonical instruction
boundary. Reads beyond code are zero-padded.
function decode_deep_immediate(
frame_code : Code,
immediate_offset : code_scan_position,
operation : DeepStackOperation,
) -> (
(code_pointer, byte)
) = {
let bytes = code_bytes(frame_code);
let immediate = slice_byte(bytes, immediate_offset);
let immediate_valid = deep_stack_operation_immediate_valid(operation, immediate);
let next_pc : code_pointer =
if immediate_valid then immediate_offset + 1 else immediate_offset;
(next_pc, immediate)
}function code_bytes(code) = struct { bytes = code.bytes, len = code.len }Applies the immediate-validity rule selected by a decoded deep-stack
operation. DUPN and SWAPN share the single-index encoding, while
EXCHANGE uses the pair encoding.
function deep_stack_operation_immediate_valid(operation : DeepStackOperation, immediate : byte) -> bool =
match operation {
DeepStackDuplicate => deep_stack_immediate_valid(immediate),
DeepStackSwap => deep_stack_immediate_valid(immediate),
DeepStackExchange => exchange_immediate_valid(immediate),
NotDeepStackOperation => false,
}Existential executable-code value whose concrete byte address and length
remain correlated inside CodeFields.
type Code = {
'off 'len,
code_region_valid_range('off, 'len) & code_valid_length('len).
CodeFields('off, 'len)
}The closed family of Amsterdam opcodes whose instruction encoding carries one validity-sensitive immediate byte. Keeping this classification in the specification lets instruction fetch and PUSH-aware code analysis share one dispatch without introducing a function-valued decoder.
enum DeepStackOperation = { DeepStackDuplicate, DeepStackSwap, DeepStackExchange, NotDeepStackOperation }An 8-bit byte.
type byte = bits(8)An absolute byte position in the code arena.
type code_pointer = range(0, code_region_bound)An opcode-aligned scan cursor: a code position that still carries the PUSH32 immediate headroom, so reading past the current opcode stays in the code region.
type code_scan_position = range(0, code_region_bound - 32)function execute_push_encoded¶
Executes an encoded PUSH instruction from its immediate cursor. This is the shared semantic boundary used by raw-byte interpreters: decoding, PC progression, stack validation, gas charging, and the stack effect remain generated from Sail.
function execute_push_encoded(
frame_code : Code,
opcode : opcode,
immediate_offset : code_scan_position,
execution_gas : gas,
sp : StackPointer,
) -> (
(code_pointer, gas, StackPointer, OpcodeOutcome)
) = {
if 95 <= opcode & opcode <= 127 then {
let width : push_width = opcode - 95;
let (next_pc, value) = decode_push_immediate(frame_code, immediate_offset, width);
let (gas_after, sp_after, status_after) = execute_push(execution_gas, sp, width, value);
(next_pc, gas_after, sp_after, status_after)
} else {
let (gas_after, status_after) = execute_invalid(execution_gas);
(immediate_offset, gas_after, sp, status_after)
}
}Decodes one PUSH immediate and returns the semantic program counter after all encoded immediate bytes. Missing code bytes contribute zero to the value but still belong to the instruction encoding.
function decode_push_immediate(
frame_code : Code,
immediate_offset : code_scan_position,
width : push_width,
) -> (
(code_pointer, word)
) = {
let bytes = code_bytes(frame_code);
let value = read_push(bytes, immediate_offset, width);
(immediate_offset + width, value)
}Reports invalid-opcode termination to the interpreter's exceptional-halt boundary.
function execute_invalid(carried_gas : gas) -> (gas, OpcodeOutcome) = {
(carried_gas, Failed(InvalidOpcode))
}Implements the PUSH0 through PUSH32 family.
function execute_push(
carried_gas : gas,
carried_sp : StackPointer,
n : push_width,
v : word,
) -> (
(gas, StackPointer, OpcodeOutcome)
) = {
let stack_status = guard_stack(carried_sp, 0, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
let cost =
if n == 0 then G_base else G_verylow;
if carried_gas < cost then {
return (GAS_ZERO, carried_sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - cost;
let sp = stack_top_advance(carried_sp, 1);
write_stack_word(sp, v);
(gas, sp, Continue())
}Existential executable-code value whose concrete byte address and length
remain correlated inside CodeFields.
type Code = {
'off 'len,
code_region_valid_range('off, 'len) & code_valid_length('len).
CodeFields('off, 'len)
}Lightweight result of one opcode handler.
union OpcodeOutcome = {
/*! Continue executing the active frame. */
Continue : unit,
/*! Stop the active opcode path with the given exceptional halt. */
Failed : ExceptionKind,
}The operand-stack cursor for the active frame, threaded by value through
the interpreter in the state-passing convention and held in the
stack_top frame register at frame boundaries. storage is an opaque
host coordinate while height is the semantic stack height. Keeping the
height in the cursor makes stack validation independent of the host stack
representation. Optimized C refines storage to a native u256 *.
struct StackPointer = {
storage : bits(64),
height : operand_stack_height,
}An absolute byte position in the code arena.
type code_pointer = range(0, code_region_bound)An opcode-aligned scan cursor: a code position that still carries the PUSH32 immediate headroom, so reading past the current opcode stays in the code region.
type code_scan_position = range(0, code_region_bound - 32)Available gas in a running EVM frame. Every admitted transaction gas
limit originates in the execution payload's SSZ uint64 gas-limit
domain, and child frames can only receive gas from their parent.
type gas = range(0, 2 ^ 64 - 1)An EVM instruction byte.
type opcode = range(0, 255)The immediate-byte width of a PUSH instruction.
type push_width = range(0, 32)function execute_dup_encoded¶
Executes one opcode from the DUP1 through DUP16 family. The raw-byte
interpreter routes the family here without reproducing its index
relationship.
function execute_dup_encoded(
opcode : opcode,
execution_gas : gas,
sp : StackPointer,
) -> (
(gas, StackPointer, OpcodeOutcome)
) = {
if 128 <= opcode & opcode <= 143 then {
execute_dup(execution_gas, sp, opcode - 127)
} else {
let (gas_after, status_after) = execute_invalid(execution_gas);
(gas_after, sp, status_after)
}
}Implements the DUP1 through DUP16 family.
function execute_dup(
carried_gas : gas,
carried_sp : StackPointer,
n : stack_operation_index,
) -> (
(gas, StackPointer, OpcodeOutcome)
) = {
let stack_status = guard_stack(carried_sp, n, n + 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
if carried_gas < G_verylow then {
return (GAS_ZERO, carried_sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_verylow;
let value = stack_slot_read(carried_sp, n - 1);
let sp = stack_top_advance(carried_sp, 1);
write_stack_word(sp, value);
(gas, sp, Continue())
}Reports invalid-opcode termination to the interpreter's exceptional-halt boundary.
function execute_invalid(carried_gas : gas) -> (gas, OpcodeOutcome) = {
(carried_gas, Failed(InvalidOpcode))
}Lightweight result of one opcode handler.
union OpcodeOutcome = {
/*! Continue executing the active frame. */
Continue : unit,
/*! Stop the active opcode path with the given exceptional halt. */
Failed : ExceptionKind,
}The operand-stack cursor for the active frame, threaded by value through
the interpreter in the state-passing convention and held in the
stack_top frame register at frame boundaries. storage is an opaque
host coordinate while height is the semantic stack height. Keeping the
height in the cursor makes stack validation independent of the host stack
representation. Optimized C refines storage to a native u256 *.
struct StackPointer = {
storage : bits(64),
height : operand_stack_height,
}Available gas in a running EVM frame. Every admitted transaction gas
limit originates in the execution payload's SSZ uint64 gas-limit
domain, and child frames can only receive gas from their parent.
type gas = range(0, 2 ^ 64 - 1)An EVM instruction byte.
type opcode = range(0, 255)function execute_swap_encoded¶
Executes one opcode from the SWAP1 through SWAP16 family.
function execute_swap_encoded(
opcode : opcode,
execution_gas : gas,
sp : StackPointer,
) -> (
(gas, StackPointer, OpcodeOutcome)
) = {
if 144 <= opcode & opcode <= 159 then {
execute_swap(execution_gas, sp, opcode - 143)
} else {
let (gas_after, status_after) = execute_invalid(execution_gas);
(gas_after, sp, status_after)
}
}Reports invalid-opcode termination to the interpreter's exceptional-halt boundary.
function execute_invalid(carried_gas : gas) -> (gas, OpcodeOutcome) = {
(carried_gas, Failed(InvalidOpcode))
}Implements the SWAP1 through SWAP16 family.
function execute_swap(
carried_gas : gas,
carried_sp : StackPointer,
n : stack_operation_index,
) -> (
(gas, StackPointer, OpcodeOutcome)
) = {
let stack_status = guard_stack(carried_sp, n + 1, n + 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
if carried_gas < G_verylow then {
return (GAS_ZERO, carried_sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_verylow;
let top_value = read_stack_word(carried_sp);
let other = stack_slot_read(carried_sp, n);
stack_set(carried_sp, 0, other);
stack_set(carried_sp, n, top_value);
(gas, carried_sp, Continue())
}Lightweight result of one opcode handler.
union OpcodeOutcome = {
/*! Continue executing the active frame. */
Continue : unit,
/*! Stop the active opcode path with the given exceptional halt. */
Failed : ExceptionKind,
}The operand-stack cursor for the active frame, threaded by value through
the interpreter in the state-passing convention and held in the
stack_top frame register at frame boundaries. storage is an opaque
host coordinate while height is the semantic stack height. Keeping the
height in the cursor makes stack validation independent of the host stack
representation. Optimized C refines storage to a native u256 *.
struct StackPointer = {
storage : bits(64),
height : operand_stack_height,
}Available gas in a running EVM frame. Every admitted transaction gas
limit originates in the execution payload's SSZ uint64 gas-limit
domain, and child frames can only receive gas from their parent.
type gas = range(0, 2 ^ 64 - 1)An EVM instruction byte.
type opcode = range(0, 255)function execute_log_encoded¶
Executes one opcode from the LOG0 through LOG4 family.
function execute_log_encoded(
carried_address : address,
carried_is_static : bool,
memory_base : memory_base,
opcode : opcode,
execution_gas : gas,
sp : StackPointer,
memory : memory_height,
) -> (
(gas, StackPointer, memory_height, OpcodeOutcome)
) = {
if 160 <= opcode & opcode <= 164 then {
execute_log(carried_address, carried_is_static, memory_base, opcode - 160, execution_gas, sp, memory)
} else {
let (gas_after, status_after) = execute_invalid(execution_gas);
(gas_after, sp, memory, status_after)
}
}Reports invalid-opcode termination to the interpreter's exceptional-halt boundary.
function execute_invalid(carried_gas : gas) -> (gas, OpcodeOutcome) = {
(carried_gas, Failed(InvalidOpcode))
}Implements the LOG0 through LOG4 family.
function execute_log(
carried_address : address,
carried_is_static : bool,
memory_base : memory_base,
n : log_topic_count,
carried_gas : gas,
carried_sp : StackPointer,
carried_memory_height : memory_height,
) -> (
(gas, StackPointer, memory_height, OpcodeOutcome)
) = {
let stack_status = guard_stack(carried_sp, n + 2, 0);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, carried_memory_height, stack_status)
};
var gas : gas = carried_gas;
var status : OpcodeOutcome = Continue();
var sp : StackPointer = carried_sp;
var memory : memory_height = carried_memory_height;
var topics : LogTopics = LogTopics0();
(gas, status) = guard_static(gas, carried_is_static);
if match status {
Failed(_) => true,
_ => false,
} then {
return (gas, sp, memory, status)
};
let offset_word = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let length_word = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
(topics, sp) = pop_log_topics(n, sp);
let log_cost = log_gas_cost(n, length_word, gas);
if not_bool(log_cost.affordable) then {
return (GAS_ZERO, sp, memory, Failed(OutOfGas))
};
gas = gas_sub(gas, log_cost.cost);
let requested_height = memory_requested_height(offset_word, length_word);
let expansion_cost = memory_expansion_gas_cost(memory, requested_height, gas);
if not_bool(expansion_cost.affordable) then {
return (GAS_ZERO, sp, memory, Failed(OutOfGas))
};
gas = gas_sub(gas, expansion_cost.cost);
let access = memory_access(offset_word, length_word);
memory = expand_memory(memory_base, memory, access.requested_height);
let range = access.range;
let data = active_memory_slice(memory_base, memory, range.off, range.len);
let memory_slice = evm_memory_slice(data.bytes, data.len);
let log_data = LogDataMemory(memory_slice);
k_log(carried_address, topics, log_data);
(gas, sp, memory, Continue())
}Lightweight result of one opcode handler.
union OpcodeOutcome = {
/*! Continue executing the active frame. */
Continue : unit,
/*! Stop the active opcode path with the given exceptional halt. */
Failed : ExceptionKind,
}The operand-stack cursor for the active frame, threaded by value through
the interpreter in the state-passing convention and held in the
stack_top frame register at frame boundaries. storage is an opaque
host coordinate while height is the semantic stack height. Keeping the
height in the cursor makes stack validation independent of the host stack
representation. Optimized C refines storage to a native u256 *.
struct StackPointer = {
storage : bits(64),
height : operand_stack_height,
}A 20-byte account address (YP §4.1), in canonical protocol byte order.
type address = vector(20, inc, byte)Available gas in a running EVM frame. Every admitted transaction gas
limit originates in the execution payload's SSZ uint64 gas-limit
domain, and child frames can only receive gas from their parent.
type gas = range(0, 2 ^ 64 - 1)An absolute byte position in the shared EVM-memory arena.
type memory_base = range(0, memory_region_bound)The active EVM frame's exact relative byte high-water mark. It is carried
beside the frame's absolute memory_base; the host retains no hidden
frame coordinate or lifecycle state.
type memory_height = memory_lengthAn EVM instruction byte.
type opcode = range(0, 255)function execute_deep_stack_encoded¶
Executes an encoded Amsterdam deep-stack instruction from its immediate cursor. Opcode classification and immediate validity are specification semantics; the raw-byte interpreter supplies only the opcode byte.
function execute_deep_stack_encoded(
frame_code : Code,
opcode : opcode,
immediate_offset : code_scan_position,
execution_gas : gas,
sp : StackPointer,
) -> (
(code_pointer, gas, StackPointer, OpcodeOutcome)
) = {
let operation = deep_stack_operation(opcode);
let (next_pc, immediate) = decode_deep_immediate(frame_code, immediate_offset, operation);
let result : (gas, StackPointer, OpcodeOutcome) = match operation {
DeepStackDuplicate => execute_dupn(execution_gas, sp, immediate),
DeepStackSwap => execute_swapn(execution_gas, sp, immediate),
DeepStackExchange => execute_exchange(execution_gas, sp, immediate),
NotDeepStackOperation => {
let (gas_after, status_after) = execute_invalid(execution_gas);
(gas_after, sp, status_after)
},
};
let (gas_after, sp_after, status_after) = result;
(next_pc, gas_after, sp_after, status_after)
}Decodes the immediate of an Amsterdam deep-stack instruction. A valid
immediate advances the counter; an invalid immediate remains unconsumed so
the handler can report InvalidOpcode with the canonical instruction
boundary. Reads beyond code are zero-padded.
function decode_deep_immediate(
frame_code : Code,
immediate_offset : code_scan_position,
operation : DeepStackOperation,
) -> (
(code_pointer, byte)
) = {
let bytes = code_bytes(frame_code);
let immediate = slice_byte(bytes, immediate_offset);
let immediate_valid = deep_stack_operation_immediate_valid(operation, immediate);
let next_pc : code_pointer =
if immediate_valid then immediate_offset + 1 else immediate_offset;
(next_pc, immediate)
}Classifies an opcode against Amsterdam's immediate deep-stack operations; every other opcode maps to the non-member.
function deep_stack_operation(opcode : opcode) -> DeepStackOperation =
match opcode {
230 => DeepStackDuplicate,
231 => DeepStackSwap,
232 => DeepStackExchange,
_ => NotDeepStackOperation,
}Implements immediate deep-stack duplication DUPN.
function execute_dupn(
carried_gas : gas,
carried_sp : StackPointer,
immediate : byte,
) -> (
(gas, StackPointer, OpcodeOutcome)
) = {
let valid_immediate = deep_stack_immediate_valid(immediate);
if not_bool(valid_immediate) then {
return (carried_gas, carried_sp, Failed(InvalidOpcode))
};
let n = decode_single_stack_index(immediate);
let stack_status = guard_stack(carried_sp, n, n + 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
if carried_gas < G_verylow then {
return (GAS_ZERO, carried_sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_verylow;
let value = stack_slot_read(carried_sp, n - 1);
let sp = stack_top_advance(carried_sp, 1);
write_stack_word(sp, value);
(gas, sp, Continue())
}Implements immediate pairwise deep-stack EXCHANGE.
function execute_exchange(
carried_gas : gas,
carried_sp : StackPointer,
immediate : byte,
) -> (
(gas, StackPointer, OpcodeOutcome)
) = {
let valid_immediate = exchange_immediate_valid(immediate);
if not_bool(valid_immediate) then {
return (carried_gas, carried_sp, Failed(InvalidOpcode))
};
let (n, m) = decode_exchange_stack_indices(immediate);
let stack_status = guard_stack(carried_sp, m + 1, m + 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
if carried_gas < G_verylow then {
return (GAS_ZERO, carried_sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_verylow;
let first = stack_slot_read(carried_sp, n);
let second = stack_slot_read(carried_sp, m);
stack_set(carried_sp, n, second);
stack_set(carried_sp, m, first);
(gas, carried_sp, Continue())
}Reports invalid-opcode termination to the interpreter's exceptional-halt boundary.
function execute_invalid(carried_gas : gas) -> (gas, OpcodeOutcome) = {
(carried_gas, Failed(InvalidOpcode))
}Implements immediate deep-stack exchange SWAPN.
function execute_swapn(
carried_gas : gas,
carried_sp : StackPointer,
immediate : byte,
) -> (
(gas, StackPointer, OpcodeOutcome)
) = {
let valid_immediate = deep_stack_immediate_valid(immediate);
if not_bool(valid_immediate) then {
return (carried_gas, carried_sp, Failed(InvalidOpcode))
};
let n = decode_single_stack_index(immediate);
let stack_status = guard_stack(carried_sp, n + 1, n + 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
if carried_gas < G_verylow then {
return (GAS_ZERO, carried_sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_verylow;
let top_value = read_stack_word(carried_sp);
let other = stack_slot_read(carried_sp, n);
stack_set(carried_sp, 0, other);
stack_set(carried_sp, n, top_value);
(gas, carried_sp, Continue())
}Existential executable-code value whose concrete byte address and length
remain correlated inside CodeFields.
type Code = {
'off 'len,
code_region_valid_range('off, 'len) & code_valid_length('len).
CodeFields('off, 'len)
}The closed family of Amsterdam opcodes whose instruction encoding carries one validity-sensitive immediate byte. Keeping this classification in the specification lets instruction fetch and PUSH-aware code analysis share one dispatch without introducing a function-valued decoder.
enum DeepStackOperation = { DeepStackDuplicate, DeepStackSwap, DeepStackExchange, NotDeepStackOperation }Lightweight result of one opcode handler.
union OpcodeOutcome = {
/*! Continue executing the active frame. */
Continue : unit,
/*! Stop the active opcode path with the given exceptional halt. */
Failed : ExceptionKind,
}The operand-stack cursor for the active frame, threaded by value through
the interpreter in the state-passing convention and held in the
stack_top frame register at frame boundaries. storage is an opaque
host coordinate while height is the semantic stack height. Keeping the
height in the cursor makes stack validation independent of the host stack
representation. Optimized C refines storage to a native u256 *.
struct StackPointer = {
storage : bits(64),
height : operand_stack_height,
}An absolute byte position in the code arena.
type code_pointer = range(0, code_region_bound)An opcode-aligned scan cursor: a code position that still carries the PUSH32 immediate headroom, so reading past the current opcode stays in the code region.
type code_scan_position = range(0, code_region_bound - 32)Available gas in a running EVM frame. Every admitted transaction gas
limit originates in the execution payload's SSZ uint64 gas-limit
domain, and child frames can only receive gas from their parent.
type gas = range(0, 2 ^ 64 - 1)An EVM instruction byte.
type opcode = range(0, 255)function decode_simple¶
Decodes one non-PUSH opcode byte to its AST node. The three
contiguous families fold to an arity argument — DUP1–DUP16
(0x80–0x8f), SWAP1–SWAP16 (0x90–0x9f), LOG0–LOG4 (0xa0–0xa4)
— and the remainder is a flat table. Any byte with no defined opcode
decodes to INVALID.
function decode_simple(opcode : opcode, fork : Fork) -> ast = {
let available = opcode_available(opcode, fork);
if not_bool(available) then {
INVALID()
} else if 128 <= opcode & opcode <= 143 then {
DUP(opcode - 127)
} else if 144 <= opcode & opcode <= 159 then {
SWAP(opcode - 143)
} else if 160 <= opcode & opcode <= 164 then {
LOG(opcode - 160)
} else {
match opcode {
0 => STOP(),
1 => ADD(),
2 => MUL(),
3 => SUB(),
4 => DIV(),
5 => SDIV(),
6 => MOD(),
7 => SMOD(),
8 => ADDMOD(),
9 => MULMOD(),
10 => EXP(),
11 => SIGNEXTEND(),
16 => LT(),
17 => GT(),
18 => SLT(),
19 => SGT(),
20 => EQ(),
21 => ISZERO(),
22 => AND(),
23 => OR(),
24 => XOR(),
25 => NOT(),
26 => BYTE(),
27 => SHL(),
28 => SHR(),
29 => SAR(),
30 => CLZ(), /* EIP-7939: Osaka+ */
32 => KECCAK256(),
48 => ADDRESS(),
49 => BALANCE(),
50 => ORIGIN(),
51 => CALLER(),
52 => CALLVALUE(),
53 => CALLDATALOAD(),
54 => CALLDATASIZE(),
55 => CALLDATACOPY(),
56 => CODESIZE(),
57 => CODECOPY(),
58 => GASPRICE(),
59 => EXTCODESIZE(),
60 => EXTCODECOPY(),
61 => RETURNDATASIZE(),
62 => RETURNDATACOPY(),
63 => EXTCODEHASH(),
64 => BLOCKHASH(),
65 => COINBASE(),
66 => TIMESTAMP(),
67 => NUMBER(),
68 => PREVRANDAO(),
69 => GASLIMIT(),
70 => CHAINID(),
71 => SELFBALANCE(),
72 => BASEFEE(),
73 => BLOBHASH(),
74 => BLOBBASEFEE(),
75 => SLOTNUM(), /* EIP-7843: Amsterdam+ (0x4b is undefined earlier) */
80 => POP(),
81 => MLOAD(),
82 => MSTORE(),
83 => MSTORE8(),
84 => SLOAD(),
85 => SSTORE(),
86 => JUMP(),
87 => JUMPI(),
88 => PC(),
89 => MSIZE(),
90 => GAS(),
91 => JUMPDEST(),
92 => TLOAD(),
93 => TSTORE(),
94 => MCOPY(),
240 => opcode_CREATE(),
241 => CALL(),
242 => CALLCODE(),
243 => RETURN(),
244 => DELEGATECALL(),
245 => CREATE2(),
250 => STATICCALL(),
253 => REVERT(),
255 => SELFDESTRUCT(),
_ => INVALID(),
}
}
}val not_bool = pure {coq: "negb", lean: "_lean_not", _: "not"}: forall ('p : Bool). bool('p) -> bool(not('p))Reports whether an opcode byte is defined by the active fork. Undefined
opcode bytes remain available here and decode to INVALID; this predicate
contains only fork-dependent availability so every interpreter can share
the same deployment rules without duplicating them in its dispatch.
function opcode_available(opcode : opcode, fork : Fork) -> bool =
match opcode {
30 => fork >= Osaka,
72 => fork >= London,
73 => fork >= Cancun,
74 => fork >= Cancun,
75 => fork >= Amsterdam,
92 => fork >= Cancun,
93 => fork >= Cancun,
94 => fork >= Cancun,
95 => fork >= Shanghai,
230 => fork >= Amsterdam,
231 => fork >= Amsterdam,
232 => fork >= Amsterdam,
_ => true,
}Every supported protocol and schema fork, in activation order. This is the
sole fork identity in the model: the decoded schema byte selects a
ProtocolProfile, which stores one of these values. The bounded semantic
type prevents values outside the supported fork sequence, while each named
constant retains its precise singleton type for dependent profile typing.
type Fork = range(0, 16)One constructor per opcode. Immediates are carried inline: PUSH
holds its byte width (0–32) and value, DUP/SWAP hold the index
n, LOG holds its topic count. The constructor groups are labelled
with the opcode byte range they cover. Decoding code bytes into this
AST is fetch's job; undefined bytes decode to INVALID and halt
exceptionally when executed.
union ast = {
/* 0x00, 0x0b: arithmetic */
STOP : unit, ADD : unit, MUL : unit, SUB : unit, DIV : unit,
SDIV : unit, MOD : unit, SMOD : unit, ADDMOD : unit,
MULMOD : unit, EXP : unit, SIGNEXTEND : unit,
/* 0x10, 0x1e: comparison / bitwise */
LT : unit, GT : unit, SLT : unit, SGT : unit, EQ : unit,
ISZERO : unit, AND : unit, OR : unit, XOR : unit, NOT : unit,
BYTE : unit, SHL : unit, SHR : unit, SAR : unit, CLZ : unit,
/* 0x20: keccak */
KECCAK256 : unit,
/* 0x30, 0x3f: environment / account */
ADDRESS : unit, BALANCE : unit, ORIGIN : unit, CALLER : unit,
CALLVALUE : unit, CALLDATALOAD : unit, CALLDATASIZE : unit,
CALLDATACOPY : unit, CODESIZE : unit, CODECOPY : unit,
GASPRICE : unit, EXTCODESIZE : unit, EXTCODECOPY : unit,
RETURNDATASIZE : unit, RETURNDATACOPY : unit, EXTCODEHASH : unit,
/* 0x40, 0x4a: block */
BLOCKHASH : unit, COINBASE : unit, TIMESTAMP : unit, NUMBER : unit,
PREVRANDAO : unit, GASLIMIT : unit, CHAINID : unit,
SELFBALANCE : unit, BASEFEE : unit, BLOBHASH : unit, BLOBBASEFEE : unit,
/* EIP-7843 (0x4b) */
SLOTNUM : unit,
/* 0x50, 0x5e: stack / memory / storage / flow */
POP : unit, MLOAD : unit, MSTORE : unit, MSTORE8 : unit,
SLOAD : unit, SSTORE : unit, JUMP : unit, JUMPI : unit,
PC : unit, MSIZE : unit, GAS : unit, JUMPDEST : unit,
TLOAD : unit, TSTORE : unit, MCOPY : unit,
/* 0x5f, 0x7f: push (width 0..32, value) */
PUSH : (push_width, word),
/* 0x80, 0x9f: dup / swap (n) */
DUP : stack_operation_index, SWAP : stack_operation_index,
/* 0xa0, 0xa4: log (num topics) */
LOG : log_topic_count,
/* 0xe6, 0xe8: EIP-8024 deep-stack access (immediate byte) */
DUPN : byte, SWAPN : byte, EXCHANGE : byte,
/* 0xf0, 0xff: system */
opcode_CREATE : unit, CALL : unit, CALLCODE : unit, RETURN : unit,
DELEGATECALL : unit, CREATE2 : unit, STATICCALL : unit,
REVERT : unit, INVALID : unit, SELFDESTRUCT : unit
}An EVM instruction byte.
type opcode = range(0, 255)function fetch¶
Fetches and decodes the opcode at the carried program counter,
returning the counter advanced past the opcode and any immediate.
Past the end of code the frame implicitly executes STOP (YP).
PUSH0–PUSH32 (0x5f–0x7f) carry an n-byte immediate; Amsterdam's
DUPN/SWAPN/EXCHANGE carry one byte, zero-padded at end of code.
Every other byte decodes via decode_simple.
function fetch(frame_code : Code, current : code_pointer, fork : Fork) -> (code_pointer, ast) = {
let analyzed = frame_code;
let code = code_bytes(analyzed);
let code_length = code.len;
let past_end = not_bool(current < code_length);
if past_end then {
(current, STOP())
} else {
let opcode_byte = slice_byte(code, current);
let opcode : opcode = unsigned(opcode_byte);
let immediate_offset = current + 1;
let available = opcode_available(opcode, fork);
let decoded : (code_pointer, ast) =
if not_bool(available)
then (immediate_offset, INVALID())
else if 95 <= opcode & opcode <= 127 then {
let size : push_width = opcode - 95;
let (after_immediate, value) = decode_push_immediate(frame_code, immediate_offset, size);
(after_immediate, PUSH(size, value))
} else {
let deep_operation = deep_stack_operation(opcode);
match deep_operation {
NotDeepStackOperation => (immediate_offset, decode_simple(opcode, fork)),
operation => {
let (after_instruction, immediate) = decode_deep_immediate(
frame_code,
immediate_offset,
operation,
);
let instruction : ast = match operation {
DeepStackDuplicate => DUPN(immediate),
DeepStackSwap => SWAPN(immediate),
DeepStackExchange => EXCHANGE(immediate),
NotDeepStackOperation => decode_simple(opcode, fork),
};
(after_instruction, instruction)
},
}
};
decoded
}
}function code_bytes(code) = struct { bytes = code.bytes, len = code.len }Decodes the immediate of an Amsterdam deep-stack instruction. A valid
immediate advances the counter; an invalid immediate remains unconsumed so
the handler can report InvalidOpcode with the canonical instruction
boundary. Reads beyond code are zero-padded.
function decode_deep_immediate(
frame_code : Code,
immediate_offset : code_scan_position,
operation : DeepStackOperation,
) -> (
(code_pointer, byte)
) = {
let bytes = code_bytes(frame_code);
let immediate = slice_byte(bytes, immediate_offset);
let immediate_valid = deep_stack_operation_immediate_valid(operation, immediate);
let next_pc : code_pointer =
if immediate_valid then immediate_offset + 1 else immediate_offset;
(next_pc, immediate)
}Decodes one PUSH immediate and returns the semantic program counter after all encoded immediate bytes. Missing code bytes contribute zero to the value but still belong to the instruction encoding.
function decode_push_immediate(
frame_code : Code,
immediate_offset : code_scan_position,
width : push_width,
) -> (
(code_pointer, word)
) = {
let bytes = code_bytes(frame_code);
let value = read_push(bytes, immediate_offset, width);
(immediate_offset + width, value)
}Decodes one non-PUSH opcode byte to its AST node. The three
contiguous families fold to an arity argument — DUP1–DUP16
(0x80–0x8f), SWAP1–SWAP16 (0x90–0x9f), LOG0–LOG4 (0xa0–0xa4)
— and the remainder is a flat table. Any byte with no defined opcode
decodes to INVALID.
function decode_simple(opcode : opcode, fork : Fork) -> ast = {
let available = opcode_available(opcode, fork);
if not_bool(available) then {
INVALID()
} else if 128 <= opcode & opcode <= 143 then {
DUP(opcode - 127)
} else if 144 <= opcode & opcode <= 159 then {
SWAP(opcode - 143)
} else if 160 <= opcode & opcode <= 164 then {
LOG(opcode - 160)
} else {
match opcode {
0 => STOP(),
1 => ADD(),
2 => MUL(),
3 => SUB(),
4 => DIV(),
5 => SDIV(),
6 => MOD(),
7 => SMOD(),
8 => ADDMOD(),
9 => MULMOD(),
10 => EXP(),
11 => SIGNEXTEND(),
16 => LT(),
17 => GT(),
18 => SLT(),
19 => SGT(),
20 => EQ(),
21 => ISZERO(),
22 => AND(),
23 => OR(),
24 => XOR(),
25 => NOT(),
26 => BYTE(),
27 => SHL(),
28 => SHR(),
29 => SAR(),
30 => CLZ(), /* EIP-7939: Osaka+ */
32 => KECCAK256(),
48 => ADDRESS(),
49 => BALANCE(),
50 => ORIGIN(),
51 => CALLER(),
52 => CALLVALUE(),
53 => CALLDATALOAD(),
54 => CALLDATASIZE(),
55 => CALLDATACOPY(),
56 => CODESIZE(),
57 => CODECOPY(),
58 => GASPRICE(),
59 => EXTCODESIZE(),
60 => EXTCODECOPY(),
61 => RETURNDATASIZE(),
62 => RETURNDATACOPY(),
63 => EXTCODEHASH(),
64 => BLOCKHASH(),
65 => COINBASE(),
66 => TIMESTAMP(),
67 => NUMBER(),
68 => PREVRANDAO(),
69 => GASLIMIT(),
70 => CHAINID(),
71 => SELFBALANCE(),
72 => BASEFEE(),
73 => BLOBHASH(),
74 => BLOBBASEFEE(),
75 => SLOTNUM(), /* EIP-7843: Amsterdam+ (0x4b is undefined earlier) */
80 => POP(),
81 => MLOAD(),
82 => MSTORE(),
83 => MSTORE8(),
84 => SLOAD(),
85 => SSTORE(),
86 => JUMP(),
87 => JUMPI(),
88 => PC(),
89 => MSIZE(),
90 => GAS(),
91 => JUMPDEST(),
92 => TLOAD(),
93 => TSTORE(),
94 => MCOPY(),
240 => opcode_CREATE(),
241 => CALL(),
242 => CALLCODE(),
243 => RETURN(),
244 => DELEGATECALL(),
245 => CREATE2(),
250 => STATICCALL(),
253 => REVERT(),
255 => SELFDESTRUCT(),
_ => INVALID(),
}
}
}Classifies an opcode against Amsterdam's immediate deep-stack operations; every other opcode maps to the non-member.
function deep_stack_operation(opcode : opcode) -> DeepStackOperation =
match opcode {
230 => DeepStackDuplicate,
231 => DeepStackSwap,
232 => DeepStackExchange,
_ => NotDeepStackOperation,
}val not_bool = pure {coq: "negb", lean: "_lean_not", _: "not"}: forall ('p : Bool). bool('p) -> bool(not('p))Reports whether an opcode byte is defined by the active fork. Undefined
opcode bytes remain available here and decode to INVALID; this predicate
contains only fork-dependent availability so every interpreter can share
the same deployment rules without duplicating them in its dispatch.
function opcode_available(opcode : opcode, fork : Fork) -> bool =
match opcode {
30 => fork >= Osaka,
72 => fork >= London,
73 => fork >= Cancun,
74 => fork >= Cancun,
75 => fork >= Amsterdam,
92 => fork >= Cancun,
93 => fork >= Cancun,
94 => fork >= Cancun,
95 => fork >= Shanghai,
230 => fork >= Amsterdam,
231 => fork >= Amsterdam,
232 => fork >= Amsterdam,
_ => true,
}converts a bit vector of length $n$ to an integer in the range $0$ to $2^n - 1$.
val unsigned = pure {ocaml: "uint", lem: "uint", interpreter: "uint", coq: "uint", lean: "BitVec.toNatInt", _: "sail_unsigned"}: forall ('n : Int).
bits('n) -> range(0, 2 ^ 'n - 1)Existential executable-code value whose concrete byte address and length
remain correlated inside CodeFields.
type Code = {
'off 'len,
code_region_valid_range('off, 'len) & code_valid_length('len).
CodeFields('off, 'len)
}The closed family of Amsterdam opcodes whose instruction encoding carries one validity-sensitive immediate byte. Keeping this classification in the specification lets instruction fetch and PUSH-aware code analysis share one dispatch without introducing a function-valued decoder.
enum DeepStackOperation = { DeepStackDuplicate, DeepStackSwap, DeepStackExchange, NotDeepStackOperation }Every supported protocol and schema fork, in activation order. This is the
sole fork identity in the model: the decoded schema byte selects a
ProtocolProfile, which stores one of these values. The bounded semantic
type prevents values outside the supported fork sequence, while each named
constant retains its precise singleton type for dependent profile typing.
type Fork = range(0, 16)One constructor per opcode. Immediates are carried inline: PUSH
holds its byte width (0–32) and value, DUP/SWAP hold the index
n, LOG holds its topic count. The constructor groups are labelled
with the opcode byte range they cover. Decoding code bytes into this
AST is fetch's job; undefined bytes decode to INVALID and halt
exceptionally when executed.
union ast = {
/* 0x00, 0x0b: arithmetic */
STOP : unit, ADD : unit, MUL : unit, SUB : unit, DIV : unit,
SDIV : unit, MOD : unit, SMOD : unit, ADDMOD : unit,
MULMOD : unit, EXP : unit, SIGNEXTEND : unit,
/* 0x10, 0x1e: comparison / bitwise */
LT : unit, GT : unit, SLT : unit, SGT : unit, EQ : unit,
ISZERO : unit, AND : unit, OR : unit, XOR : unit, NOT : unit,
BYTE : unit, SHL : unit, SHR : unit, SAR : unit, CLZ : unit,
/* 0x20: keccak */
KECCAK256 : unit,
/* 0x30, 0x3f: environment / account */
ADDRESS : unit, BALANCE : unit, ORIGIN : unit, CALLER : unit,
CALLVALUE : unit, CALLDATALOAD : unit, CALLDATASIZE : unit,
CALLDATACOPY : unit, CODESIZE : unit, CODECOPY : unit,
GASPRICE : unit, EXTCODESIZE : unit, EXTCODECOPY : unit,
RETURNDATASIZE : unit, RETURNDATACOPY : unit, EXTCODEHASH : unit,
/* 0x40, 0x4a: block */
BLOCKHASH : unit, COINBASE : unit, TIMESTAMP : unit, NUMBER : unit,
PREVRANDAO : unit, GASLIMIT : unit, CHAINID : unit,
SELFBALANCE : unit, BASEFEE : unit, BLOBHASH : unit, BLOBBASEFEE : unit,
/* EIP-7843 (0x4b) */
SLOTNUM : unit,
/* 0x50, 0x5e: stack / memory / storage / flow */
POP : unit, MLOAD : unit, MSTORE : unit, MSTORE8 : unit,
SLOAD : unit, SSTORE : unit, JUMP : unit, JUMPI : unit,
PC : unit, MSIZE : unit, GAS : unit, JUMPDEST : unit,
TLOAD : unit, TSTORE : unit, MCOPY : unit,
/* 0x5f, 0x7f: push (width 0..32, value) */
PUSH : (push_width, word),
/* 0x80, 0x9f: dup / swap (n) */
DUP : stack_operation_index, SWAP : stack_operation_index,
/* 0xa0, 0xa4: log (num topics) */
LOG : log_topic_count,
/* 0xe6, 0xe8: EIP-8024 deep-stack access (immediate byte) */
DUPN : byte, SWAPN : byte, EXCHANGE : byte,
/* 0xf0, 0xff: system */
opcode_CREATE : unit, CALL : unit, CALLCODE : unit, RETURN : unit,
DELEGATECALL : unit, CREATE2 : unit, STATICCALL : unit,
REVERT : unit, INVALID : unit, SELFDESTRUCT : unit
}A contract-code length.
type code_length = range(0, code_region_bound)An absolute byte position in the code arena.
type code_pointer = range(0, code_region_bound)An EVM instruction byte.
type opcode = range(0, 255)The immediate-byte width of a PUSH instruction.
type push_width = range(0, 32)The run loop¶
function frame_output¶
Returns the active frame's halt output.
function frame_output(frame_status : FrameStatus) -> OutputSlice =
match frame_status {
Halted(HaltReturn(output)) => output,
Halted(HaltRevert(output)) => output,
_ => EMPTY_OUTPUT_SLICE,
}let EMPTY_OUTPUT_SLICE : OutputSliceFields(0, 0) = output_slice(0, 0)Per-frame execution status: running, halted normally, or exceptionally halted.
union FrameStatus = {
/* mid-execution */
Running : unit,
/* halted normally (YP §9.4.4) */
Halted : HaltKind,
/* halted exceptionally: all frame gas consumed, effects void */
Exceptional : ExceptionKind
}Ordinary frame stops. Only RETURN and REVERT produce output bytes.
union HaltKind = {
/* STOP: success, empty return data */
HaltStop : unit,
/* RETURN: success with output data */
HaltReturn : OutputSlice,
/* REVERT: state unwound, output kept (EIP-140) */
HaltRevert : OutputSlice,
/* SELFDESTRUCT: success, empty return data */
HaltSelfDestruct : unit
}A frame-output range with its coordinate and length packed existentially.
type OutputSlice = {
'off 'len,
output_region_valid_range('off, 'len).
OutputSliceFields('off, 'len)
}type call_tree_steps¶
A decreasing bound for the non-recursive interpreter's complete call tree.
type call_tree_steps = range(0, 3 * (2 * (2 ^ 64 - 1)) + 2)function interpret¶
The non-recursive step loop for one complete call tree. It executes
the active frame, resumes suspended parents through
frame_stack_pop as children halt, and returns the top-level
frame's output. Each step's carried state is supplied from the frame
registers and its returned state is assigned back; the handlers
themselves never touch the registers. STOP, SELFDESTRUCT, and
exceptional halts return the empty slice; RETURN and REVERT carry
their frozen memory slice in the halt value.
function interpret(
initial_gas : gas,
initial_state_gas : state_gas,
initial_state_spill : state_gas_spill,
initial_refund : gas_refund,
initial_sp : StackPointer,
initial_memory_base : memory_base,
initial_memory_height : memory_height,
initial_caller : address,
initial_address : address,
initial_code_address : address,
initial_value : word,
initial_state_gas_reservoir : state_gas,
initial_is_static : bool,
initial_depth : frame_depth,
initial_code : Code,
initial_calldata : CalldataSlice,
) -> (
(gas, state_gas, state_gas_spill, gas_refund, FrameStatus, OutputSlice)
) = {
let execution_profile = k_execution_profile;
let profile = execution_profile.protocol;
let fork = profile.fork;
let blob_fee = blob_base_fee(fork, profile.blob_schedule, profile.excess_blob_gas_limit, k_header.excess_blob_gas);
frame_stack_reset();
var interpreting : bool = true;
var result : OutputSlice = EMPTY_OUTPUT_SLICE;
var carried_pc : code_pointer = 0;
var carried_sp : StackPointer = initial_sp;
var carried_memory_base : memory_base = initial_memory_base;
var carried_memory_height : memory_height = initial_memory_height;
var carried_gas : gas = initial_gas;
var carried_state_gas : state_gas = initial_state_gas;
var carried_state_spill : state_gas_spill = initial_state_spill;
var carried_refund : gas_refund = initial_refund;
var carried_status : FrameStatus = Running();
var carried_caller : address = initial_caller;
var carried_address : address = initial_address;
var carried_account_context : AccountExecutionContext = account_execution_context(initial_address);
var carried_code_address : address = initial_code_address;
var carried_value : word = initial_value;
var carried_state_gas_reservoir : state_gas = initial_state_gas_reservoir;
var carried_is_static : bool = initial_is_static;
var carried_depth : frame_depth = initial_depth;
var carried_code : Code = initial_code;
var carried_calldata : CalldataSlice = initial_calldata;
var carried_returndata : OutputSlice = EMPTY_OUTPUT_SLICE;
let initial_call_tree_gas = initial_gas + initial_state_gas;
var call_tree_steps_remaining : call_tree_steps = 3 * initial_call_tree_gas + 2;
while interpreting termination_measure(call_tree_steps_remaining) do {
let running = is_running(carried_status);
if running then {
let (fetched_pc, instruction) = fetch(carried_code, carried_pc, fork);
carried_pc = fetched_pc;
match instruction {
opcode_CREATE() => {
let previous_address = carried_address;
let transition = run_create(
carried_pc,
carried_gas,
carried_state_gas,
carried_state_spill,
carried_refund,
carried_sp,
carried_memory_base,
carried_memory_height,
carried_caller,
carried_address,
carried_code_address,
carried_value,
carried_state_gas_reservoir,
carried_is_static,
carried_depth,
carried_code,
carried_calldata,
carried_returndata,
CreateByNonce,
);
carried_pc = transition.pc;
carried_gas = transition.gas_remaining;
carried_state_gas = transition.state_gas_remaining;
carried_state_spill = transition.state_gas_spilled;
carried_refund = transition.refund;
carried_status = transition.status;
carried_sp = transition.stack_top;
carried_memory_base = transition.memory_base;
carried_memory_height = transition.memory_height;
carried_caller = transition.message.caller;
carried_address = transition.message.address;
carried_code_address = transition.message.code_address;
carried_value = transition.message.value;
carried_state_gas_reservoir = transition.message.state_gas_reservoir;
carried_is_static = transition.message.is_static;
carried_depth = transition.message.depth;
carried_code = transition.code;
carried_calldata = transition.calldata;
carried_returndata = transition.returndata;
carried_account_context = refresh_account_execution_context(
carried_account_context,
previous_address,
carried_address,
)
},
CREATE2() => {
let previous_address = carried_address;
let transition = run_create(
carried_pc,
carried_gas,
carried_state_gas,
carried_state_spill,
carried_refund,
carried_sp,
carried_memory_base,
carried_memory_height,
carried_caller,
carried_address,
carried_code_address,
carried_value,
carried_state_gas_reservoir,
carried_is_static,
carried_depth,
carried_code,
carried_calldata,
carried_returndata,
CreateBySalt,
);
carried_pc = transition.pc;
carried_gas = transition.gas_remaining;
carried_state_gas = transition.state_gas_remaining;
carried_state_spill = transition.state_gas_spilled;
carried_refund = transition.refund;
carried_status = transition.status;
carried_sp = transition.stack_top;
carried_memory_base = transition.memory_base;
carried_memory_height = transition.memory_height;
carried_caller = transition.message.caller;
carried_address = transition.message.address;
carried_code_address = transition.message.code_address;
carried_value = transition.message.value;
carried_state_gas_reservoir = transition.message.state_gas_reservoir;
carried_is_static = transition.message.is_static;
carried_depth = transition.message.depth;
carried_code = transition.code;
carried_calldata = transition.calldata;
carried_returndata = transition.returndata;
carried_account_context = refresh_account_execution_context(
carried_account_context,
previous_address,
carried_address,
)
},
CALL() => {
let previous_address = carried_address;
let transition = run_call(
carried_pc,
carried_gas,
carried_state_gas,
carried_state_spill,
carried_refund,
carried_sp,
carried_memory_base,
carried_memory_height,
carried_caller,
carried_address,
carried_code_address,
carried_value,
carried_state_gas_reservoir,
carried_is_static,
carried_depth,
carried_code,
carried_calldata,
carried_returndata,
Call,
);
carried_pc = transition.pc;
carried_gas = transition.gas_remaining;
carried_state_gas = transition.state_gas_remaining;
carried_state_spill = transition.state_gas_spilled;
carried_refund = transition.refund;
carried_status = transition.status;
carried_sp = transition.stack_top;
carried_memory_base = transition.memory_base;
carried_memory_height = transition.memory_height;
carried_caller = transition.message.caller;
carried_address = transition.message.address;
carried_code_address = transition.message.code_address;
carried_value = transition.message.value;
carried_state_gas_reservoir = transition.message.state_gas_reservoir;
carried_is_static = transition.message.is_static;
carried_depth = transition.message.depth;
carried_code = transition.code;
carried_calldata = transition.calldata;
carried_returndata = transition.returndata;
carried_account_context = refresh_account_execution_context(
carried_account_context,
previous_address,
carried_address,
)
},
CALLCODE() => {
let previous_address = carried_address;
let transition = run_call(
carried_pc,
carried_gas,
carried_state_gas,
carried_state_spill,
carried_refund,
carried_sp,
carried_memory_base,
carried_memory_height,
carried_caller,
carried_address,
carried_code_address,
carried_value,
carried_state_gas_reservoir,
carried_is_static,
carried_depth,
carried_code,
carried_calldata,
carried_returndata,
CallCode,
);
carried_pc = transition.pc;
carried_gas = transition.gas_remaining;
carried_state_gas = transition.state_gas_remaining;
carried_state_spill = transition.state_gas_spilled;
carried_refund = transition.refund;
carried_status = transition.status;
carried_sp = transition.stack_top;
carried_memory_base = transition.memory_base;
carried_memory_height = transition.memory_height;
carried_caller = transition.message.caller;
carried_address = transition.message.address;
carried_code_address = transition.message.code_address;
carried_value = transition.message.value;
carried_state_gas_reservoir = transition.message.state_gas_reservoir;
carried_is_static = transition.message.is_static;
carried_depth = transition.message.depth;
carried_code = transition.code;
carried_calldata = transition.calldata;
carried_returndata = transition.returndata;
carried_account_context = refresh_account_execution_context(
carried_account_context,
previous_address,
carried_address,
)
},
DELEGATECALL() => {
let previous_address = carried_address;
let transition = run_call(
carried_pc,
carried_gas,
carried_state_gas,
carried_state_spill,
carried_refund,
carried_sp,
carried_memory_base,
carried_memory_height,
carried_caller,
carried_address,
carried_code_address,
carried_value,
carried_state_gas_reservoir,
carried_is_static,
carried_depth,
carried_code,
carried_calldata,
carried_returndata,
DelegateCall,
);
carried_pc = transition.pc;
carried_gas = transition.gas_remaining;
carried_state_gas = transition.state_gas_remaining;
carried_state_spill = transition.state_gas_spilled;
carried_refund = transition.refund;
carried_status = transition.status;
carried_sp = transition.stack_top;
carried_memory_base = transition.memory_base;
carried_memory_height = transition.memory_height;
carried_caller = transition.message.caller;
carried_address = transition.message.address;
carried_code_address = transition.message.code_address;
carried_value = transition.message.value;
carried_state_gas_reservoir = transition.message.state_gas_reservoir;
carried_is_static = transition.message.is_static;
carried_depth = transition.message.depth;
carried_code = transition.code;
carried_calldata = transition.calldata;
carried_returndata = transition.returndata;
carried_account_context = refresh_account_execution_context(
carried_account_context,
previous_address,
carried_address,
)
},
STATICCALL() => {
let previous_address = carried_address;
let transition = run_call(
carried_pc,
carried_gas,
carried_state_gas,
carried_state_spill,
carried_refund,
carried_sp,
carried_memory_base,
carried_memory_height,
carried_caller,
carried_address,
carried_code_address,
carried_value,
carried_state_gas_reservoir,
carried_is_static,
carried_depth,
carried_code,
carried_calldata,
carried_returndata,
StaticCall,
);
carried_pc = transition.pc;
carried_gas = transition.gas_remaining;
carried_state_gas = transition.state_gas_remaining;
carried_state_spill = transition.state_gas_spilled;
carried_refund = transition.refund;
carried_status = transition.status;
carried_sp = transition.stack_top;
carried_memory_base = transition.memory_base;
carried_memory_height = transition.memory_height;
carried_caller = transition.message.caller;
carried_address = transition.message.address;
carried_code_address = transition.message.code_address;
carried_value = transition.message.value;
carried_state_gas_reservoir = transition.message.state_gas_reservoir;
carried_is_static = transition.message.is_static;
carried_depth = transition.message.depth;
carried_code = transition.code;
carried_calldata = transition.calldata;
carried_returndata = transition.returndata;
carried_account_context = refresh_account_execution_context(
carried_account_context,
previous_address,
carried_address,
)
},
_ => {
let result :
(
code_pointer,
gas,
state_gas,
state_gas_spill,
gas_refund,
StackPointer,
memory_height,
FrameStatus,
) = match instruction {
STOP() => {
let status_after = execute_stop();
(
carried_pc,
carried_gas,
carried_state_gas,
carried_state_spill,
carried_refund,
carried_sp,
carried_memory_height,
status_after,
)
},
ADD() => {
let (gas_after, sp_after, status_after) = execute_add(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
MUL() => {
let (gas_after, sp_after, status_after) = execute_mul(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
SUB() => {
let (gas_after, sp_after, status_after) = execute_sub(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
DIV() => {
let (gas_after, sp_after, status_after) = execute_div(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
SDIV() => {
let (gas_after, sp_after, status_after) = execute_sdiv(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
MOD() => {
let (gas_after, sp_after, status_after) = execute_mod(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
SMOD() => {
let (gas_after, sp_after, status_after) = execute_smod(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
ADDMOD() => {
let (gas_after, sp_after, status_after) = execute_addmod(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
MULMOD() => {
let (gas_after, sp_after, status_after) = execute_mulmod(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
EXP() => {
let (gas_after, sp_after, status_after) = execute_exp(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
SIGNEXTEND() => {
let (gas_after, sp_after, status_after) = execute_signextend(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
LT() => {
let (gas_after, sp_after, status_after) = execute_lt(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
GT() => {
let (gas_after, sp_after, status_after) = execute_gt(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
SLT() => {
let (gas_after, sp_after, status_after) = execute_slt(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
SGT() => {
let (gas_after, sp_after, status_after) = execute_sgt(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
EQ() => {
let (gas_after, sp_after, status_after) = execute_eq(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
ISZERO() => {
let (gas_after, sp_after, status_after) = execute_iszero(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
AND() => {
let (gas_after, sp_after, status_after) = execute_and(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
OR() => {
let (gas_after, sp_after, status_after) = execute_or(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
XOR() => {
let (gas_after, sp_after, status_after) = execute_xor(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
NOT() => {
let (gas_after, sp_after, status_after) = execute_not(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
BYTE() => {
let (gas_after, sp_after, status_after) = execute_byte(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
SHL() => {
let (gas_after, sp_after, status_after) = execute_shl(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
SHR() => {
let (gas_after, sp_after, status_after) = execute_shr(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
SAR() => {
let (gas_after, sp_after, status_after) = execute_sar(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
CLZ() => {
let (gas_after, sp_after, status_after) = execute_clz(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
KECCAK256() => {
let (gas_after, sp_after, memory_after, status_after) = execute_keccak256(
carried_memory_base,
carried_gas,
carried_sp,
carried_memory_height,
);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
memory_after,
opcode_frame_status(status_after),
)
},
ADDRESS() => {
let (gas_after, sp_after, status_after) = execute_address(
carried_address,
carried_gas,
carried_sp,
);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
BALANCE() => {
let (gas_after, sp_after, status_after) = execute_balance(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
ORIGIN() => {
let (gas_after, sp_after, status_after) = execute_origin(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
CALLER() => {
let (gas_after, sp_after, status_after) = execute_caller(
carried_caller,
carried_gas,
carried_sp,
);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
CALLVALUE() => {
let (gas_after, sp_after, status_after) = execute_callvalue(
carried_value,
carried_gas,
carried_sp,
);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
CALLDATALOAD() => {
let (gas_after, sp_after, status_after) = execute_calldataload(
carried_calldata,
carried_gas,
carried_sp,
);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
CALLDATASIZE() => {
let (gas_after, sp_after, status_after) = execute_calldatasize(
carried_calldata,
carried_gas,
carried_sp,
);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
CALLDATACOPY() => {
let (gas_after, sp_after, memory_after, status_after) = execute_calldatacopy(
carried_calldata,
carried_memory_base,
carried_gas,
carried_sp,
carried_memory_height,
);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
memory_after,
opcode_frame_status(status_after),
)
},
CODESIZE() => {
let (gas_after, sp_after, status_after) = execute_codesize(
carried_code,
carried_gas,
carried_sp,
);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
CODECOPY() => {
let (gas_after, sp_after, memory_after, status_after) = execute_codecopy(
carried_code,
carried_memory_base,
carried_gas,
carried_sp,
carried_memory_height,
);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
memory_after,
opcode_frame_status(status_after),
)
},
GASPRICE() => {
let (gas_after, sp_after, status_after) = execute_gasprice(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
EXTCODESIZE() => {
let (gas_after, sp_after, status_after) = execute_extcodesize(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
EXTCODECOPY() => {
let (gas_after, sp_after, memory_after, status_after) = execute_extcodecopy(
carried_memory_base,
carried_gas,
carried_sp,
carried_memory_height,
);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
memory_after,
opcode_frame_status(status_after),
)
},
RETURNDATASIZE() => {
let (gas_after, sp_after, status_after) = execute_returndatasize(
carried_returndata,
carried_gas,
carried_sp,
);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
RETURNDATACOPY() => {
let (gas_after, sp_after, memory_after, status_after) = execute_returndatacopy(
carried_returndata,
carried_memory_base,
carried_gas,
carried_sp,
carried_memory_height,
);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
memory_after,
opcode_frame_status(status_after),
)
},
EXTCODEHASH() => {
let (gas_after, sp_after, status_after) = execute_extcodehash(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
BLOCKHASH() => {
let (gas_after, sp_after, status_after) = execute_blockhash(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
COINBASE() => {
let (gas_after, sp_after, status_after) = execute_coinbase(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
TIMESTAMP() => {
let (gas_after, sp_after, status_after) = execute_timestamp(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
NUMBER() => {
let (gas_after, sp_after, status_after) = execute_number(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
SLOTNUM() => {
let (gas_after, sp_after, status_after) = execute_slotnum(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
PREVRANDAO() => {
let (gas_after, sp_after, status_after) = execute_prevrandao(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
GASLIMIT() => {
let (gas_after, sp_after, status_after) = execute_gaslimit(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
CHAINID() => {
let (gas_after, sp_after, status_after) = execute_chainid(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
SELFBALANCE() => {
let (gas_after, sp_after, status_after) = execute_selfbalance(
carried_address,
carried_gas,
carried_sp,
);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
BASEFEE() => {
let (gas_after, sp_after, status_after) = execute_basefee(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
BLOBHASH() => {
let (gas_after, sp_after, status_after) = execute_blobhash(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
BLOBBASEFEE() => {
let (gas_after, sp_after, status_after) = execute_blobbasefee(
blob_fee,
carried_gas,
carried_sp,
);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
POP() => {
let (gas_after, sp_after, status_after) = execute_pop(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
MLOAD() => {
let (gas_after, sp_after, memory_after, status_after) = execute_mload(
carried_memory_base,
carried_gas,
carried_sp,
carried_memory_height,
);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
memory_after,
opcode_frame_status(status_after),
)
},
MSTORE() => {
let (gas_after, sp_after, memory_after, status_after) = execute_mstore(
carried_memory_base,
carried_gas,
carried_sp,
carried_memory_height,
);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
memory_after,
opcode_frame_status(status_after),
)
},
MSTORE8() => {
let (gas_after, sp_after, memory_after, status_after) = execute_mstore8(
carried_memory_base,
carried_gas,
carried_sp,
carried_memory_height,
);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
memory_after,
opcode_frame_status(status_after),
)
},
SLOAD() => {
let (gas_after, sp_after, status_after) = execute_sload(
carried_account_context,
carried_gas,
carried_sp,
);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
SSTORE() => {
let (gas_after, state_gas_after, state_spill_after, refund_after, sp_after, status_after) = execute_sstore(
carried_account_context,
fork,
carried_is_static,
carried_gas,
carried_state_gas,
carried_state_spill,
carried_refund,
carried_sp,
);
(
carried_pc,
gas_after,
state_gas_after,
state_spill_after,
refund_after,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
JUMP() => {
let (pc_after, gas_after, sp_after, status_after) = execute_jump(
carried_code,
carried_pc,
carried_gas,
carried_sp,
);
(
pc_after,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
JUMPI() => {
let (pc_after, gas_after, sp_after, status_after) = execute_jumpi(
carried_code,
carried_pc,
carried_gas,
carried_sp,
);
(
pc_after,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
PC() => {
let (pc_after, gas_after, sp_after, status_after) = execute_pc(
carried_pc,
carried_gas,
carried_sp,
);
(
pc_after,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
MSIZE() => {
let (gas_after, sp_after, memory_after, status_after) = execute_msize(
carried_gas,
carried_sp,
carried_memory_height,
);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
memory_after,
opcode_frame_status(status_after),
)
},
GAS() => {
let (gas_after, sp_after, status_after) = execute_gas(carried_gas, carried_sp);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
JUMPDEST() => {
let (gas_after, status_after) = execute_jumpdest(carried_gas);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
carried_sp,
carried_memory_height,
opcode_frame_status(status_after),
)
},
TLOAD() => {
let (gas_after, sp_after, status_after) = execute_tload(
carried_address,
carried_gas,
carried_sp,
);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
TSTORE() => {
let (gas_after, sp_after, status_after) = execute_tstore(
carried_address,
carried_is_static,
carried_gas,
carried_sp,
);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
MCOPY() => {
let (gas_after, sp_after, memory_after, status_after) = execute_mcopy(
carried_memory_base,
carried_gas,
carried_sp,
carried_memory_height,
);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
memory_after,
opcode_frame_status(status_after),
)
},
PUSH(n, value) => {
let (gas_after, sp_after, status_after) = execute_push(carried_gas, carried_sp, n, value);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
DUP(n) => {
let (gas_after, sp_after, status_after) = execute_dup(carried_gas, carried_sp, n);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
SWAP(n) => {
let (gas_after, sp_after, status_after) = execute_swap(carried_gas, carried_sp, n);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
DUPN(immediate) => {
let (gas_after, sp_after, status_after) = execute_dupn(carried_gas, carried_sp, immediate);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
SWAPN(immediate) => {
let (gas_after, sp_after, status_after) = execute_swapn(carried_gas, carried_sp, immediate);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
EXCHANGE(immediate) => {
let (gas_after, sp_after, status_after) = execute_exchange(
carried_gas,
carried_sp,
immediate,
);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
carried_memory_height,
opcode_frame_status(status_after),
)
},
LOG(n) => {
let (gas_after, sp_after, memory_after, status_after) = execute_log(
carried_address,
carried_is_static,
carried_memory_base,
n,
carried_gas,
carried_sp,
carried_memory_height,
);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
memory_after,
opcode_frame_status(status_after),
)
},
opcode_CREATE() => fatal_error(ExecutionInvalid),
CREATE2() => fatal_error(ExecutionInvalid),
CALL() => fatal_error(ExecutionInvalid),
CALLCODE() => fatal_error(ExecutionInvalid),
DELEGATECALL() => fatal_error(ExecutionInvalid),
STATICCALL() => fatal_error(ExecutionInvalid),
RETURN() => {
let (gas_after, sp_after, memory_after, status_after) = execute_return(
carried_memory_base,
carried_gas,
carried_sp,
carried_memory_height,
);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
sp_after,
memory_after,
status_after,
)
},
REVERT() => {
let (gas_after, state_gas_after, state_spill_after, sp_after, memory_after, status_after) = execute_revert(
carried_state_gas_reservoir,
carried_memory_base,
carried_gas,
carried_state_gas,
carried_state_spill,
carried_sp,
carried_memory_height,
);
(
carried_pc,
gas_after,
state_gas_after,
state_spill_after,
carried_refund,
sp_after,
memory_after,
status_after,
)
},
INVALID() => {
let (gas_after, status_after) = execute_invalid(carried_gas);
(
carried_pc,
gas_after,
carried_state_gas,
carried_state_spill,
carried_refund,
carried_sp,
carried_memory_height,
opcode_frame_status(status_after),
)
},
SELFDESTRUCT() => {
let (gas_after, state_gas_after, state_spill_after, refund_after, sp_after, status_after) = execute_selfdestruct(
carried_address,
fork,
carried_is_static,
carried_gas,
carried_state_gas,
carried_state_spill,
carried_refund,
carried_sp,
);
(
carried_pc,
gas_after,
state_gas_after,
state_spill_after,
refund_after,
sp_after,
carried_memory_height,
status_after,
)
},
};
(
carried_pc,
carried_gas,
carried_state_gas,
carried_state_spill,
carried_refund,
carried_sp,
carried_memory_height,
carried_status,
) = match result {
(
pc_after,
_,
state_gas_after,
state_spill_after,
refund_after,
sp_after,
memory_after,
Exceptional(kind),
) => {
let exceptional = exceptional_state(
state_gas_after,
state_spill_after,
carried_state_gas_reservoir,
kind,
);
let state_gas_after = exceptional.state_gas_remaining;
let state_spill_after = exceptional.state_gas_spilled;
let status_after = exceptional.status;
(
pc_after,
GAS_ZERO,
state_gas_after,
state_spill_after,
refund_after,
sp_after,
memory_after,
status_after,
)
},
_ => result,
}
},
}
} else {
let output = frame_output(carried_status);
let continuation = frame_stack_pop();
match continuation {
Empty() => {
result = output;
interpreting = false
},
continuation => {
let previous_address = carried_address;
let transition = resume_frame(
continuation,
output,
carried_memory_base,
carried_gas,
carried_state_gas,
carried_state_spill,
carried_refund,
carried_status,
carried_state_gas_reservoir,
);
carried_pc = transition.pc;
carried_gas = transition.gas_remaining;
carried_state_gas = transition.state_gas_remaining;
carried_state_spill = transition.state_gas_spilled;
carried_refund = transition.refund;
carried_status = transition.status;
carried_sp = transition.stack_top;
carried_memory_base = transition.memory_base;
carried_memory_height = transition.memory_height;
carried_caller = transition.message.caller;
carried_address = transition.message.address;
carried_code_address = transition.message.code_address;
carried_value = transition.message.value;
carried_state_gas_reservoir = transition.message.state_gas_reservoir;
carried_is_static = transition.message.is_static;
carried_depth = transition.message.depth;
carried_code = transition.code;
carried_calldata = transition.calldata;
carried_returndata = transition.returndata;
carried_account_context = refresh_account_execution_context(
carried_account_context,
previous_address,
carried_address,
)
},
}
};
let remaining_steps = call_tree_steps_remaining;
call_tree_steps_remaining =
if remaining_steps == 0 then {
0
} else {
remaining_steps - 1
}
};
(carried_gas, carried_state_gas, carried_state_spill, carried_refund, carried_status, result)
}function account_execution_context(address : address) -> AccountExecutionContext =
struct { address = address }function blob_base_fee(fork, schedule, limit, excess_blob_gas) = {
if (fork >= Cancun) & (excess_blob_gas <= limit) then {
fake_exponential_word(schedule, excess_blob_gas)
} else {
fatal_error(InvalidConfig)
}
}function blob_schedule(target, maximum, denominator) =
struct { target = target, max = maximum, base_fee_update_fraction = denominator }function exceptional_state(state_gas_remaining, state_gas_spilled, state_gas_reservoir, k) = {
let execution_profile = k_execution_profile;
let profile = execution_profile.protocol;
if profile.fork >= Amsterdam then {
struct {
state_gas_remaining = state_gas_reservoir,
state_gas_spilled = STATE_GAS_SPILL_ZERO,
status = Exceptional(k),
}
} else {
struct {
state_gas_remaining = state_gas_remaining,
state_gas_spilled = state_gas_spilled,
status = Exceptional(k),
}
}
}Implements ADD.
function execute_add(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 2, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < G_verylow then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_verylow;
let a = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let b = read_stack_word(sp);
let result = alu_add(a, b);
write_stack_word(sp, result);
(gas, sp, Continue())
}Implements ADDMOD.
function execute_addmod(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 3, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < G_mid then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_mid;
let a = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let b = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let n = read_stack_word(sp);
let result = alu_addmod(a, b, n);
write_stack_word(sp, result);
(gas, sp, Continue())
}Implements ADDRESS.
function execute_address(
carried_address : address,
carried_gas : gas,
carried_sp : StackPointer,
) -> (
(gas, StackPointer, OpcodeOutcome)
) = {
let stack_status = guard_stack(carried_sp, 0, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < G_base then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_base;
let address_word = address_to_word(carried_address);
sp = stack_top_advance(sp, 1);
write_stack_word(sp, address_word);
(gas, sp, Continue())
}Implements bitwise AND.
function execute_and(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 2, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < G_verylow then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_verylow;
let a = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let b = read_stack_word(sp);
let result = alu_and(a, b);
write_stack_word(sp, result);
(gas, sp, Continue())
}Implements BALANCE, including warm/cold account access.
function execute_balance(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 1, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
let address_word = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let a = word_to_address(address_word);
let warm = k_account_is_warm(a);
let gas_cost = account_cost(warm);
if carried_gas < gas_cost then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - gas_cost;
k_account_mark_warm(a);
let balance = k_get_balance(a);
sp = stack_top_advance(sp, 1);
write_stack_word(sp, balance);
(gas, sp, Continue())
}Implements BASEFEE.
function execute_basefee(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 0, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < G_base then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_base;
let base_fee = k_env(F_BaseFee);
sp = stack_top_advance(sp, 1);
write_stack_word(sp, base_fee);
(gas, sp, Continue())
}Implements BLOBBASEFEE.
function execute_blobbasefee(
blob_fee : word,
carried_gas : gas,
carried_sp : StackPointer,
) -> (
(gas, StackPointer, OpcodeOutcome)
) = {
let stack_status = guard_stack(carried_sp, 0, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < G_base then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_base;
sp = stack_top_advance(sp, 1);
write_stack_word(sp, blob_fee);
(gas, sp, Continue())
}Implements BLOBHASH.
function execute_blobhash(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 1, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < G_verylow then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_verylow;
let index = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let blob_hash = k_blobhash(index);
sp = stack_top_advance(sp, 1);
write_stack_word(sp, blob_hash);
(gas, sp, Continue())
}Implements BLOCKHASH.
function execute_blockhash(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 1, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < 20 then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - 20;
let block_number = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let block_hash = k_blockhash(block_number);
let hash_word = hash_to_word(block_hash);
sp = stack_top_advance(sp, 1);
write_stack_word(sp, hash_word);
(gas, sp, Continue())
}Implements BYTE.
function execute_byte(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 2, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < G_verylow then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_verylow;
let i = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let x = read_stack_word(sp);
let result = alu_byte(i, x);
write_stack_word(sp, result);
(gas, sp, Continue())
}Implements CALLDATACOPY.
function execute_calldatacopy(
carried_calldata : CalldataSlice,
memory_base : memory_base,
carried_gas : gas,
carried_sp : StackPointer,
carried_memory_height : memory_height,
) -> (
(gas, StackPointer, memory_height, OpcodeOutcome)
) = {
let stack_status = guard_stack(carried_sp, 3, 0);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, carried_memory_height, stack_status)
};
var gas : gas = carried_gas;
var sp : StackPointer = carried_sp;
var memory : memory_height = carried_memory_height;
if gas < G_verylow then {
return (GAS_ZERO, sp, memory, Failed(OutOfGas))
};
gas = gas_sub(gas, G_verylow);
let destination_word = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let source_word = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let length_word = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let copy_cost = copy_gas_cost(length_word, gas);
if not_bool(copy_cost.affordable) then {
return (GAS_ZERO, sp, memory, Failed(OutOfGas))
};
gas = gas_sub(gas, copy_cost.cost);
let requested_height = memory_requested_height(destination_word, length_word);
let expansion_cost = memory_expansion_gas_cost(memory, requested_height, gas);
if not_bool(expansion_cost.affordable) then {
return (GAS_ZERO, sp, memory, Failed(OutOfGas))
};
gas = gas_sub(gas, expansion_cost.cost);
let access = memory_access(destination_word, length_word);
memory = expand_memory(memory_base, memory, access.requested_height);
let range = access.range;
let destination = memory_absolute(memory_base, range.off);
slice_copy_word_offset(carried_calldata, destination, source_word, range.len);
(gas, sp, memory, Continue())
}Implements CALLDATALOAD.
function execute_calldataload(
carried_calldata : CalldataSlice,
carried_gas : gas,
carried_sp : StackPointer,
) -> (
(gas, StackPointer, OpcodeOutcome)
) = {
let stack_status = guard_stack(carried_sp, 1, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < G_verylow then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_verylow;
let offset_word = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let value = slice_load_word_offset(carried_calldata, offset_word);
sp = stack_top_advance(sp, 1);
write_stack_word(sp, value);
(gas, sp, Continue())
}Implements CALLDATASIZE.
function execute_calldatasize(
carried_calldata : CalldataSlice,
carried_gas : gas,
carried_sp : StackPointer,
) -> (
(gas, StackPointer, OpcodeOutcome)
) = {
let stack_status = guard_stack(carried_sp, 0, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < G_base then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_base;
let input = carried_calldata;
let input_length = region_slice_length(input);
let length_word = word_of_byte_count(input_length);
sp = stack_top_advance(sp, 1);
write_stack_word(sp, length_word);
(gas, sp, Continue())
}Implements CALLER.
function execute_caller(
carried_caller : address,
carried_gas : gas,
carried_sp : StackPointer,
) -> (
(gas, StackPointer, OpcodeOutcome)
) = {
let stack_status = guard_stack(carried_sp, 0, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < G_base then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_base;
let caller = address_to_word(carried_caller);
sp = stack_top_advance(sp, 1);
write_stack_word(sp, caller);
(gas, sp, Continue())
}Implements CALLVALUE.
function execute_callvalue(
carried_value : word,
carried_gas : gas,
carried_sp : StackPointer,
) -> (
(gas, StackPointer, OpcodeOutcome)
) = {
let stack_status = guard_stack(carried_sp, 0, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < G_base then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_base;
sp = stack_top_advance(sp, 1);
write_stack_word(sp, carried_value);
(gas, sp, Continue())
}Implements CHAINID.
function execute_chainid(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 0, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < G_base then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_base;
let chain_id = k_env(F_ChainId);
sp = stack_top_advance(sp, 1);
write_stack_word(sp, chain_id);
(gas, sp, Continue())
}Implements count-leading-zeroes CLZ (EIP-7939).
function execute_clz(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 1, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
/* EIP-7939 */
if carried_gas < G_low then {
return (GAS_ZERO, carried_sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_low;
let x = read_stack_word(carried_sp);
let result = alu_clz(x);
write_stack_word(carried_sp, result);
(gas, carried_sp, Continue())
}Implements CODECOPY.
function execute_codecopy(
carried_code : Code,
memory_base : memory_base,
carried_gas : gas,
carried_sp : StackPointer,
carried_memory_height : memory_height,
) -> (
(gas, StackPointer, memory_height, OpcodeOutcome)
) = {
let stack_status = guard_stack(carried_sp, 3, 0);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, carried_memory_height, stack_status)
};
var gas : gas = carried_gas;
var sp : StackPointer = carried_sp;
var memory : memory_height = carried_memory_height;
if gas < G_verylow then {
return (GAS_ZERO, sp, memory, Failed(OutOfGas))
};
gas = gas_sub(gas, G_verylow);
let destination_word = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let source_word = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let length_word = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let copy_cost = copy_gas_cost(length_word, gas);
if not_bool(copy_cost.affordable) then {
return (GAS_ZERO, sp, memory, Failed(OutOfGas))
};
gas = gas_sub(gas, copy_cost.cost);
let requested_height = memory_requested_height(destination_word, length_word);
let expansion_cost = memory_expansion_gas_cost(memory, requested_height, gas);
if not_bool(expansion_cost.affordable) then {
return (GAS_ZERO, sp, memory, Failed(OutOfGas))
};
gas = gas_sub(gas, expansion_cost.cost);
let access = memory_access(destination_word, length_word);
memory = expand_memory(memory_base, memory, access.requested_height);
let range = access.range;
let code = carried_code;
let bytes = code_bytes(code);
let destination = memory_absolute(memory_base, range.off);
slice_copy_word_offset(bytes, destination, source_word, range.len);
(gas, sp, memory, Continue())
}Implements CODESIZE.
function execute_codesize(
carried_code : Code,
carried_gas : gas,
carried_sp : StackPointer,
) -> (
(gas, StackPointer, OpcodeOutcome)
) = {
let stack_status = guard_stack(carried_sp, 0, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < G_base then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_base;
let code_length = frame_code_len(carried_code);
let length_word = word_of_byte_count(code_length);
sp = stack_top_advance(sp, 1);
write_stack_word(sp, length_word);
(gas, sp, Continue())
}Implements COINBASE.
function execute_coinbase(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 0, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < G_base then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_base;
let coinbase = k_env(F_Coinbase);
sp = stack_top_advance(sp, 1);
write_stack_word(sp, coinbase);
(gas, sp, Continue())
}Implements unsigned DIV.
function execute_div(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 2, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < G_low then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_low;
let a = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let b = read_stack_word(sp);
let result = alu_div(a, b);
write_stack_word(sp, result);
(gas, sp, Continue())
}Implements the DUP1 through DUP16 family.
function execute_dup(
carried_gas : gas,
carried_sp : StackPointer,
n : stack_operation_index,
) -> (
(gas, StackPointer, OpcodeOutcome)
) = {
let stack_status = guard_stack(carried_sp, n, n + 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
if carried_gas < G_verylow then {
return (GAS_ZERO, carried_sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_verylow;
let value = stack_slot_read(carried_sp, n - 1);
let sp = stack_top_advance(carried_sp, 1);
write_stack_word(sp, value);
(gas, sp, Continue())
}Implements immediate deep-stack duplication DUPN.
function execute_dupn(
carried_gas : gas,
carried_sp : StackPointer,
immediate : byte,
) -> (
(gas, StackPointer, OpcodeOutcome)
) = {
let valid_immediate = deep_stack_immediate_valid(immediate);
if not_bool(valid_immediate) then {
return (carried_gas, carried_sp, Failed(InvalidOpcode))
};
let n = decode_single_stack_index(immediate);
let stack_status = guard_stack(carried_sp, n, n + 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
if carried_gas < G_verylow then {
return (GAS_ZERO, carried_sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_verylow;
let value = stack_slot_read(carried_sp, n - 1);
let sp = stack_top_advance(carried_sp, 1);
write_stack_word(sp, value);
(gas, sp, Continue())
}Implements EQ.
function execute_eq(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 2, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < G_verylow then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_verylow;
let a = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let b = read_stack_word(sp);
let result = alu_eq(a, b);
write_stack_word(sp, result);
(gas, sp, Continue())
}Implements immediate pairwise deep-stack EXCHANGE.
function execute_exchange(
carried_gas : gas,
carried_sp : StackPointer,
immediate : byte,
) -> (
(gas, StackPointer, OpcodeOutcome)
) = {
let valid_immediate = exchange_immediate_valid(immediate);
if not_bool(valid_immediate) then {
return (carried_gas, carried_sp, Failed(InvalidOpcode))
};
let (n, m) = decode_exchange_stack_indices(immediate);
let stack_status = guard_stack(carried_sp, m + 1, m + 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
if carried_gas < G_verylow then {
return (GAS_ZERO, carried_sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_verylow;
let first = stack_slot_read(carried_sp, n);
let second = stack_slot_read(carried_sp, m);
stack_set(carried_sp, n, second);
stack_set(carried_sp, m, first);
(gas, carried_sp, Continue())
}Implements EXP, including exponent-dependent gas.
function execute_exp(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 2, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
let a = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let e = read_stack_word(sp);
let gas_cost = exp_gas(e);
if carried_gas < gas_cost then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - gas_cost;
let result = alu_exp(a, e);
write_stack_word(sp, result);
(gas, sp, Continue())
}Implements EXTCODECOPY, including access and copy gas.
function execute_extcodecopy(
memory_base : memory_base,
carried_gas : gas,
carried_sp : StackPointer,
carried_memory_height : memory_height,
) -> (
(gas, StackPointer, memory_height, OpcodeOutcome)
) = {
let stack_status = guard_stack(carried_sp, 4, 0);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, carried_memory_height, stack_status)
};
var gas : gas = carried_gas;
var sp : StackPointer = carried_sp;
var memory : memory_height = carried_memory_height;
let address_word = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let a = word_to_address(address_word);
let destination_word = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let source_word = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let length_word = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let warm = k_account_is_warm(a);
let access_cost = account_cost(warm);
let read_cost = external_code_read_cost();
if gas < access_cost + read_cost then {
return (GAS_ZERO, sp, memory, Failed(OutOfGas))
};
gas = gas_sub(gas, access_cost + read_cost);
let copy_cost = copy_gas_cost(length_word, gas);
if not_bool(copy_cost.affordable) then {
return (GAS_ZERO, sp, memory, Failed(OutOfGas))
};
gas = gas_sub(gas, copy_cost.cost);
let requested_height = memory_requested_height(destination_word, length_word);
let expansion_cost = memory_expansion_gas_cost(memory, requested_height, gas);
if not_bool(expansion_cost.affordable) then {
return (GAS_ZERO, sp, memory, Failed(OutOfGas))
};
gas = gas_sub(gas, expansion_cost.cost);
let access = memory_access(destination_word, length_word);
memory = expand_memory(memory_base, memory, access.requested_height);
let range = access.range;
k_account_mark_warm(a);
let destination = memory_absolute(memory_base, range.off);
k_code_copy(a, destination, source_word, range.len);
(gas, sp, memory, Continue())
}Implements EXTCODEHASH, including warm/cold account access.
function execute_extcodehash(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 1, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
let address_word = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let a = word_to_address(address_word);
let warm = k_account_is_warm(a);
let gas_cost = account_cost(warm);
if carried_gas < gas_cost then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - gas_cost;
k_account_mark_warm(a);
let code_hash = k_get_codehash(a);
let hash_word = hash_to_word(code_hash);
sp = stack_top_advance(sp, 1);
write_stack_word(sp, hash_word);
(gas, sp, Continue())
}Implements EXTCODESIZE, including warm/cold account access.
function execute_extcodesize(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 1, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
let address_word = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let a = word_to_address(address_word);
let warm = k_account_is_warm(a);
let access_cost = account_cost(warm);
let read_cost = external_code_read_cost();
if carried_gas < access_cost + read_cost then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = gas_sub(carried_gas, access_cost + read_cost);
k_account_mark_warm(a);
let code_size = k_get_code_size(a);
let size_word = word_of_byte_count(code_size);
sp = stack_top_advance(sp, 1);
write_stack_word(sp, size_word);
(gas, sp, Continue())
}Implements GAS, returning the carried gas remaining after its own
charge.
function execute_gas(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 0, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
if carried_gas < G_base then {
return (GAS_ZERO, carried_sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_base;
let gas_word = word_of_nat_byte_count(gas);
let sp = stack_top_advance(carried_sp, 1);
write_stack_word(sp, gas_word);
(gas, sp, Continue())
}Implements GASLIMIT.
function execute_gaslimit(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 0, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < G_base then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_base;
let gas_limit = k_env(F_GasLimit);
sp = stack_top_advance(sp, 1);
write_stack_word(sp, gas_limit);
(gas, sp, Continue())
}Implements GASPRICE.
function execute_gasprice(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 0, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < G_base then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_base;
let gas_price = k_env(F_GasPrice);
sp = stack_top_advance(sp, 1);
write_stack_word(sp, gas_price);
(gas, sp, Continue())
}Implements unsigned GT.
function execute_gt(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 2, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < G_verylow then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_verylow;
let a = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let b = read_stack_word(sp);
let result = alu_gt(a, b);
write_stack_word(sp, result);
(gas, sp, Continue())
}Reports invalid-opcode termination to the interpreter's exceptional-halt boundary.
function execute_invalid(carried_gas : gas) -> (gas, OpcodeOutcome) = {
(carried_gas, Failed(InvalidOpcode))
}Implements ISZERO.
function execute_iszero(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 1, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
if carried_gas < G_verylow then {
return (GAS_ZERO, carried_sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_verylow;
let a = read_stack_word(carried_sp);
let result = alu_iszero(a);
write_stack_word(carried_sp, result);
(gas, carried_sp, Continue())
}Implements unconditional JUMP.
function execute_jump(
carried_code : Code,
carried_pc : code_pointer,
carried_gas : gas,
carried_sp : StackPointer,
) -> (
(code_pointer, gas, StackPointer, OpcodeOutcome)
) = {
let stack_status = guard_stack(carried_sp, 1, 0);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (carried_pc, GAS_ZERO, carried_sp, stack_status)
};
var gas : gas = carried_gas;
var pc : code_pointer = carried_pc;
var status : OpcodeOutcome = Continue();
if gas < G_mid then {
return (pc, GAS_ZERO, carried_sp, Failed(OutOfGas))
};
gas = gas_sub(gas, G_mid);
let dest = read_stack_word(carried_sp);
let sp = stack_top_retreat(carried_sp, 1);
(pc, gas, status) = do_jump(pc, gas, carried_code, dest);
(pc, gas, sp, status)
}Implements JUMPDEST.
function execute_jumpdest(carried_gas : gas) -> (gas, OpcodeOutcome) = {
if carried_gas < G_jumpdest then {
return (GAS_ZERO, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_jumpdest;
(gas, Continue())
}Implements conditional JUMPI.
function execute_jumpi(
carried_code : Code,
carried_pc : code_pointer,
carried_gas : gas,
carried_sp : StackPointer,
) -> (
(code_pointer, gas, StackPointer, OpcodeOutcome)
) = {
let stack_status = guard_stack(carried_sp, 2, 0);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (carried_pc, GAS_ZERO, carried_sp, stack_status)
};
var gas : gas = carried_gas;
var pc : code_pointer = carried_pc;
var status : OpcodeOutcome = Continue();
var sp : StackPointer = carried_sp;
if gas < G_high then {
return (pc, GAS_ZERO, sp, Failed(OutOfGas))
};
gas = gas_sub(gas, G_high);
let dest = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let cond = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let condition_is_zero = word_is_zero(cond);
if condition_is_zero then {
return (pc, gas, sp, status)
};
(pc, gas, status) = do_jump(pc, gas, carried_code, dest);
(pc, gas, sp, status)
}Implements KECCAK256 over an expanded memory range.
function execute_keccak256(
memory_base : memory_base,
carried_gas : gas,
carried_sp : StackPointer,
carried_memory_height : memory_height,
) -> (
(gas, StackPointer, memory_height, OpcodeOutcome)
) = {
let stack_status = guard_stack(carried_sp, 2, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, carried_memory_height, stack_status)
};
var gas : gas = carried_gas;
var sp : StackPointer = carried_sp;
var memory : memory_height = carried_memory_height;
let offset_word = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let length_word = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let keccak_cost = keccak_gas_cost(length_word, gas);
if not_bool(keccak_cost.affordable) then {
return (GAS_ZERO, sp, memory, Failed(OutOfGas))
};
gas = gas_sub(gas, keccak_cost.cost);
let requested_height = memory_requested_height(offset_word, length_word);
let expansion_cost = memory_expansion_gas_cost(memory, requested_height, gas);
if not_bool(expansion_cost.affordable) then {
return (GAS_ZERO, sp, memory, Failed(OutOfGas))
};
gas = gas_sub(gas, expansion_cost.cost);
let access = memory_access(offset_word, length_word);
memory = expand_memory(memory_base, memory, access.requested_height);
let digest = mem_keccak(memory_base, memory, access.range);
sp = stack_top_advance(sp, 1);
write_stack_word(sp, digest);
(gas, sp, memory, Continue())
}Implements the LOG0 through LOG4 family.
function execute_log(
carried_address : address,
carried_is_static : bool,
memory_base : memory_base,
n : log_topic_count,
carried_gas : gas,
carried_sp : StackPointer,
carried_memory_height : memory_height,
) -> (
(gas, StackPointer, memory_height, OpcodeOutcome)
) = {
let stack_status = guard_stack(carried_sp, n + 2, 0);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, carried_memory_height, stack_status)
};
var gas : gas = carried_gas;
var status : OpcodeOutcome = Continue();
var sp : StackPointer = carried_sp;
var memory : memory_height = carried_memory_height;
var topics : LogTopics = LogTopics0();
(gas, status) = guard_static(gas, carried_is_static);
if match status {
Failed(_) => true,
_ => false,
} then {
return (gas, sp, memory, status)
};
let offset_word = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let length_word = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
(topics, sp) = pop_log_topics(n, sp);
let log_cost = log_gas_cost(n, length_word, gas);
if not_bool(log_cost.affordable) then {
return (GAS_ZERO, sp, memory, Failed(OutOfGas))
};
gas = gas_sub(gas, log_cost.cost);
let requested_height = memory_requested_height(offset_word, length_word);
let expansion_cost = memory_expansion_gas_cost(memory, requested_height, gas);
if not_bool(expansion_cost.affordable) then {
return (GAS_ZERO, sp, memory, Failed(OutOfGas))
};
gas = gas_sub(gas, expansion_cost.cost);
let access = memory_access(offset_word, length_word);
memory = expand_memory(memory_base, memory, access.requested_height);
let range = access.range;
let data = active_memory_slice(memory_base, memory, range.off, range.len);
let memory_slice = evm_memory_slice(data.bytes, data.len);
let log_data = LogDataMemory(memory_slice);
k_log(carried_address, topics, log_data);
(gas, sp, memory, Continue())
}Implements unsigned LT.
function execute_lt(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 2, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < G_verylow then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_verylow;
let a = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let b = read_stack_word(sp);
let result = alu_lt(a, b);
write_stack_word(sp, result);
(gas, sp, Continue())
}Implements overlapping memory copy MCOPY (EIP-5656).
function execute_mcopy(
memory_base : memory_base,
carried_gas : gas,
carried_sp : StackPointer,
carried_memory_height : memory_height,
) -> (
(gas, StackPointer, memory_height, OpcodeOutcome)
) = {
let stack_status = guard_stack(carried_sp, 3, 0);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, carried_memory_height, stack_status)
};
var gas : gas = carried_gas;
var sp : StackPointer = carried_sp;
var memory : memory_height = carried_memory_height;
/* EIP-5656 */
if gas < G_verylow then {
return (GAS_ZERO, sp, memory, Failed(OutOfGas))
};
gas = gas_sub(gas, G_verylow);
let destination_word = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let source_word = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let length_word = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let copy_cost = copy_gas_cost(length_word, gas);
if not_bool(copy_cost.affordable) then {
return (GAS_ZERO, sp, memory, Failed(OutOfGas))
};
gas = gas_sub(gas, copy_cost.cost);
let destination_requested_height = memory_requested_height(destination_word, length_word);
let source_requested_height = memory_requested_height(source_word, length_word);
let requested_height =
if destination_requested_height < source_requested_height
then source_requested_height
else destination_requested_height;
let expansion_cost = memory_expansion_gas_cost(memory, requested_height, gas);
if not_bool(expansion_cost.affordable) then {
return (GAS_ZERO, sp, memory, Failed(OutOfGas))
};
gas = gas_sub(gas, expansion_cost.cost);
let destination = memory_access(destination_word, length_word);
let source = memory_access(source_word, length_word);
let materialized_required_size =
if destination.requested_height < source.requested_height
then source.requested_height
else destination.requested_height;
memory = expand_memory(memory_base, memory, materialized_required_size);
mem_mcopy(memory_base, destination.range.off, source.range.off, destination.range.len);
(gas, sp, memory, Continue())
}Implements MLOAD.
function execute_mload(
memory_base : memory_base,
carried_gas : gas,
carried_sp : StackPointer,
carried_memory_height : memory_height,
) -> (
(gas, StackPointer, memory_height, OpcodeOutcome)
) = {
let stack_status = guard_stack(carried_sp, 1, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, carried_memory_height, stack_status)
};
var gas : gas = carried_gas;
var sp : StackPointer = carried_sp;
var memory : memory_height = carried_memory_height;
if gas < G_verylow then {
return (GAS_ZERO, sp, memory, Failed(OutOfGas))
};
gas = gas_sub(gas, G_verylow);
let offset_word = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let word_size = u256(32);
let requested_height = memory_requested_height(offset_word, word_size);
let expansion_cost = memory_expansion_gas_cost(memory, requested_height, gas);
if not_bool(expansion_cost.affordable) then {
return (GAS_ZERO, sp, memory, Failed(OutOfGas))
};
gas = gas_sub(gas, expansion_cost.cost);
let access = memory_access(offset_word, word_size);
memory = expand_memory(memory_base, memory, access.requested_height);
let value = mem_load(memory_base, access.range.off);
sp = stack_top_advance(sp, 1);
write_stack_word(sp, value);
(gas, sp, memory, Continue())
}Implements unsigned MOD.
function execute_mod(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 2, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < G_low then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_low;
let a = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let b = read_stack_word(sp);
let result = alu_mod(a, b);
write_stack_word(sp, result);
(gas, sp, Continue())
}Implements MSIZE.
function execute_msize(
carried_gas : gas,
carried_sp : StackPointer,
carried_memory_height : memory_height,
) -> (
(gas, StackPointer, memory_height, OpcodeOutcome)
) = {
let stack_status = guard_stack(carried_sp, 0, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, carried_memory_height, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < G_base then {
return (GAS_ZERO, sp, carried_memory_height, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_base;
let high_water = memory_high_water(carried_memory_height);
let words = memory_word_count(high_water);
let size = word_of_nat_byte_count(words * 32);
sp = stack_top_advance(sp, 1);
write_stack_word(sp, size);
(gas, sp, carried_memory_height, Continue())
}Implements MSTORE.
function execute_mstore(
memory_base : memory_base,
carried_gas : gas,
carried_sp : StackPointer,
carried_memory_height : memory_height,
) -> (
(gas, StackPointer, memory_height, OpcodeOutcome)
) = {
let stack_status = guard_stack(carried_sp, 2, 0);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, carried_memory_height, stack_status)
};
var gas : gas = carried_gas;
var sp : StackPointer = carried_sp;
var memory : memory_height = carried_memory_height;
if gas < G_verylow then {
return (GAS_ZERO, sp, memory, Failed(OutOfGas))
};
gas = gas_sub(gas, G_verylow);
let offset_word = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let v = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let word_size = u256(32);
let requested_height = memory_requested_height(offset_word, word_size);
let expansion_cost = memory_expansion_gas_cost(memory, requested_height, gas);
if not_bool(expansion_cost.affordable) then {
return (GAS_ZERO, sp, memory, Failed(OutOfGas))
};
gas = gas_sub(gas, expansion_cost.cost);
let access = memory_access(offset_word, word_size);
memory = expand_memory(memory_base, memory, access.requested_height);
mem_store(memory_base, access.range.off, v);
(gas, sp, memory, Continue())
}Implements MSTORE8.
function execute_mstore8(
memory_base : memory_base,
carried_gas : gas,
carried_sp : StackPointer,
carried_memory_height : memory_height,
) -> (
(gas, StackPointer, memory_height, OpcodeOutcome)
) = {
let stack_status = guard_stack(carried_sp, 2, 0);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, carried_memory_height, stack_status)
};
var gas : gas = carried_gas;
var sp : StackPointer = carried_sp;
var memory : memory_height = carried_memory_height;
if gas < G_verylow then {
return (GAS_ZERO, sp, memory, Failed(OutOfGas))
};
gas = gas_sub(gas, G_verylow);
let offset_word = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let v = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let requested_height = memory_requested_height(offset_word, WORD_ONE);
let expansion_cost = memory_expansion_gas_cost(memory, requested_height, gas);
if not_bool(expansion_cost.affordable) then {
return (GAS_ZERO, sp, memory, Failed(OutOfGas))
};
gas = gas_sub(gas, expansion_cost.cost);
let access = memory_access(offset_word, WORD_ONE);
memory = expand_memory(memory_base, memory, access.requested_height);
mem_store_byte(memory_base, access.range.off, v);
(gas, sp, memory, Continue())
}Implements MUL.
function execute_mul(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 2, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < G_low then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_low;
let a = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let b = read_stack_word(sp);
let result = alu_mul(a, b);
write_stack_word(sp, result);
(gas, sp, Continue())
}Implements MULMOD.
function execute_mulmod(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 3, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < G_mid then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_mid;
let a = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let b = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let n = read_stack_word(sp);
let result = alu_mulmod(a, b, n);
write_stack_word(sp, result);
(gas, sp, Continue())
}Implements bitwise NOT.
function execute_not(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 1, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
if carried_gas < G_verylow then {
return (GAS_ZERO, carried_sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_verylow;
let a = read_stack_word(carried_sp);
let result = alu_not(a);
write_stack_word(carried_sp, result);
(gas, carried_sp, Continue())
}Implements NUMBER.
function execute_number(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 0, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < G_base then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_base;
let block_number = k_env(F_Number);
sp = stack_top_advance(sp, 1);
write_stack_word(sp, block_number);
(gas, sp, Continue())
}Implements bitwise OR.
function execute_or(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 2, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < G_verylow then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_verylow;
let a = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let b = read_stack_word(sp);
let result = alu_or(a, b);
write_stack_word(sp, result);
(gas, sp, Continue())
}Implements ORIGIN.
function execute_origin(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 0, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < G_base then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_base;
let origin = k_env(F_Origin);
sp = stack_top_advance(sp, 1);
write_stack_word(sp, origin);
(gas, sp, Continue())
}Implements PC, returning the current opcode position from the
carried, already-advanced program counter.
function execute_pc(
carried_pc : code_pointer,
carried_gas : gas,
carried_sp : StackPointer,
) -> (
(code_pointer, gas, StackPointer, OpcodeOutcome)
) = {
let stack_status = guard_stack(carried_sp, 0, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (carried_pc, GAS_ZERO, carried_sp, stack_status)
};
if carried_gas < G_base then {
return (carried_pc, GAS_ZERO, carried_sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_base;
let next_pc = word_of_byte_count(carried_pc);
let opcode_pc = alu_sub(next_pc, WORD_ONE);
let sp = stack_top_advance(carried_sp, 1);
write_stack_word(sp, opcode_pc);
(carried_pc, gas, sp, Continue())
}Implements POP.
function execute_pop(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 1, 0);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < G_base then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_base;
let _ = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
(gas, sp, Continue())
}Implements PREVRANDAO.
function execute_prevrandao(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 0, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < G_base then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_base;
let prev_randao = k_env(F_PrevRandao);
sp = stack_top_advance(sp, 1);
write_stack_word(sp, prev_randao);
(gas, sp, Continue())
}Implements the PUSH0 through PUSH32 family.
function execute_push(
carried_gas : gas,
carried_sp : StackPointer,
n : push_width,
v : word,
) -> (
(gas, StackPointer, OpcodeOutcome)
) = {
let stack_status = guard_stack(carried_sp, 0, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
let cost =
if n == 0 then G_base else G_verylow;
if carried_gas < cost then {
return (GAS_ZERO, carried_sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - cost;
let sp = stack_top_advance(carried_sp, 1);
write_stack_word(sp, v);
(gas, sp, Continue())
}Implements successful RETURN.
function execute_return(
memory_base : memory_base,
carried_gas : gas,
carried_sp : StackPointer,
carried_memory_height : memory_height,
) -> (
(gas, StackPointer, memory_height, FrameStatus)
) = {
let stack_status = guard_stack(carried_sp, 2, 0);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, carried_memory_height, opcode_frame_status(stack_status))
};
var gas : gas = carried_gas;
var sp : StackPointer = carried_sp;
var memory : memory_height = carried_memory_height;
let offset_word = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let length_word = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let requested_height = memory_requested_height(offset_word, length_word);
let expansion_cost = memory_expansion_gas_cost(memory, requested_height, gas);
if not_bool(expansion_cost.affordable) then {
return (GAS_ZERO, sp, memory, Exceptional(OutOfGas))
};
gas = gas_sub(gas, expansion_cost.cost);
let access = memory_access(offset_word, length_word);
memory = expand_memory(memory_base, memory, access.requested_height);
let range = access.range;
let data = active_memory_slice(memory_base, memory, range.off, range.len);
let output = freeze_output(data);
let reason = HaltReturn(output);
(gas, sp, memory, Halted(reason))
}Implements bounds-checked RETURNDATACOPY.
function execute_returndatacopy(
carried_returndata : OutputSlice,
memory_base : memory_base,
carried_gas : gas,
carried_sp : StackPointer,
carried_memory_height : memory_height,
) -> (
(gas, StackPointer, memory_height, OpcodeOutcome)
) = {
let stack_status = guard_stack(carried_sp, 3, 0);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, carried_memory_height, stack_status)
};
var gas : gas = carried_gas;
var sp : StackPointer = carried_sp;
var memory : memory_height = carried_memory_height;
if gas < G_verylow then {
return (GAS_ZERO, sp, memory, Failed(OutOfGas))
};
gas = gas_sub(gas, G_verylow);
let destination_word = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let source_word = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let length_word = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let available = returndata_size(carried_returndata);
if source_word <= available then {
let remaining = returndata_remaining(available, source_word);
if length_word <= remaining then {
let bounded_length : memory_length = length_word;
let copy_cost = copy_gas_cost(length_word, gas);
if not_bool(copy_cost.affordable) then {
return (GAS_ZERO, sp, memory, Failed(OutOfGas))
};
gas = gas_sub(gas, copy_cost.cost);
let requested_height = memory_requested_height(destination_word, length_word);
let expansion_cost = memory_expansion_gas_cost(memory, requested_height, gas);
if not_bool(expansion_cost.affordable) then {
return (GAS_ZERO, sp, memory, Failed(OutOfGas))
};
gas = gas_sub(gas, expansion_cost.cost);
let access = memory_access(destination_word, length_word);
memory = expand_memory(memory_base, memory, access.requested_height);
let destination = memory_absolute(memory_base, access.range.off);
let bounded_source_offset : source_pointer = source_word;
returndata_copy(carried_returndata, destination, bounded_source_offset, bounded_length);
(gas, sp, memory, Continue())
} else {
(GAS_ZERO, sp, memory, Failed(InvalidOpcode))
}
} else {
(GAS_ZERO, sp, memory, Failed(InvalidOpcode))
}
}Implements RETURNDATASIZE.
function execute_returndatasize(
carried_returndata : OutputSlice,
carried_gas : gas,
carried_sp : StackPointer,
) -> (
(gas, StackPointer, OpcodeOutcome)
) = {
let stack_status = guard_stack(carried_sp, 0, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < G_base then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_base;
let return_data_size = returndata_size(carried_returndata);
let size_word = word_of_byte_count(return_data_size);
sp = stack_top_advance(sp, 1);
write_stack_word(sp, size_word);
(gas, sp, Continue())
}function execute_revert(
carried_state_gas_reservoir,
memory_base,
carried_gas,
carried_state_gas,
carried_state_spill,
carried_sp,
carried_memory_height,
) = {
let stack_status = guard_stack(carried_sp, 2, 0);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (
GAS_ZERO,
carried_state_gas,
carried_state_spill,
carried_sp,
carried_memory_height,
opcode_frame_status(stack_status),
)
};
var gas : gas = carried_gas;
var state_gas : state_gas = carried_state_gas;
var state_spill : state_gas_spill = carried_state_spill;
var sp : StackPointer = carried_sp;
var memory : memory_height = carried_memory_height;
/* EIP-140 */
let offset_word = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let length_word = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let requested_height = memory_requested_height(offset_word, length_word);
let expansion_cost = memory_expansion_gas_cost(memory, requested_height, gas);
if not_bool(expansion_cost.affordable) then {
return (GAS_ZERO, carried_state_gas, carried_state_spill, sp, memory, Exceptional(OutOfGas))
};
gas = gas_sub(gas, expansion_cost.cost);
let access = memory_access(offset_word, length_word);
memory = expand_memory(memory_base, memory, access.requested_height);
let range = access.range;
let execution_profile = k_execution_profile;
let profile = execution_profile.protocol;
if profile.fork >= Amsterdam then {
gas = conserved_gas_add(gas, state_spill);
state_gas = carried_state_gas_reservoir;
state_spill = STATE_GAS_SPILL_ZERO
};
let data = active_memory_slice(memory_base, memory, range.off, range.len);
let output = freeze_output(data);
let reason = HaltRevert(output);
(gas, state_gas, state_spill, sp, memory, Halted(reason))
}Implements arithmetic right shift SAR (EIP-145).
function execute_sar(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 2, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
/* EIP-145 */
if carried_gas < G_verylow then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_verylow;
let s = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let v = read_stack_word(sp);
let result = alu_sar(s, v);
write_stack_word(sp, result);
(gas, sp, Continue())
}Implements signed SDIV.
function execute_sdiv(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 2, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < G_low then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_low;
let a = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let b = read_stack_word(sp);
let result = alu_sdiv(a, b);
write_stack_word(sp, result);
(gas, sp, Continue())
}Implements SELFBALANCE.
function execute_selfbalance(
carried_address : address,
carried_gas : gas,
carried_sp : StackPointer,
) -> (
(gas, StackPointer, OpcodeOutcome)
) = {
let stack_status = guard_stack(carried_sp, 0, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < G_low then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_low;
let balance = k_get_balance(carried_address);
sp = stack_top_advance(sp, 1);
write_stack_word(sp, balance);
(gas, sp, Continue())
}function execute_selfdestruct(
carried_address,
fork,
carried_is_static,
carried_gas,
carried_state_gas,
carried_state_spill,
carried_refund,
carried_sp,
) = {
let stack_status = guard_stack(carried_sp, 1, 0);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (
GAS_ZERO,
carried_state_gas,
carried_state_spill,
carried_refund,
carried_sp,
opcode_frame_status(stack_status),
)
};
var gas : gas = carried_gas;
var state_gas : state_gas = carried_state_gas;
var state_spill : state_gas_spill = carried_state_spill;
var refund : gas_refund = carried_refund;
var sp : StackPointer = carried_sp;
var halt : bool = false;
var status : OpcodeOutcome = Continue();
(gas, status) = guard_static(gas, carried_is_static);
if match status {
Failed(_) => true,
_ => false,
} then {
return (gas, state_gas, state_spill, refund, sp, opcode_frame_status(status))
};
let beneficiary_word = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let beneficiary = word_to_address(beneficiary_word);
let halt_reason = HaltSelfDestruct();
let halt_status = Halted(halt_reason);
if fork >= Amsterdam then {
let warm = k_account_is_warm(beneficiary);
let cold_access_cost =
if warm then G_zero else G_amsterdam_cold_account_access;
let access_cost = G_selfdestruct + cold_access_cost;
if gas < access_cost then {
return (GAS_ZERO, state_gas, state_spill, refund, sp, Exceptional(OutOfGas))
};
k_account_mark_warm(beneficiary);
let bal = k_get_balance(carried_address);
let nonzero_balance = word_nonzero(bal);
let beneficiary_empty = k_account_is_empty(beneficiary);
let creates_account = nonzero_balance & beneficiary_empty;
let execution_cost =
if creates_account then access_cost + G_amsterdam_account_write else access_cost;
if gas < execution_cost then {
return (GAS_ZERO, state_gas, state_spill, refund, sp, Exceptional(OutOfGas))
};
gas = gas_sub(gas, execution_cost);
if creates_account then {
(halt, gas, state_gas, state_spill) = charge_state_gas(
gas,
state_gas,
state_spill,
G_amsterdam_state_new_account,
)
};
if halt then {
return (gas, state_gas, state_spill, refund, sp, Exceptional(OutOfGas))
};
k_transfer(carried_address, beneficiary, bal);
let created = k_was_created(carried_address);
if created then {
k_selfdestruct(carried_address)
};
(gas, state_gas, state_spill, refund, sp, halt_status)
} else {
let bal = k_get_balance(carried_address);
let warm = k_account_is_warm(beneficiary);
if gas < G_selfdestruct then {
return (GAS_ZERO, state_gas, state_spill, refund, sp, Exceptional(OutOfGas))
};
gas = gas_sub(gas, G_selfdestruct);
if not_bool(warm) then {
if gas < G_cold_account then {
return (GAS_ZERO, state_gas, state_spill, refund, sp, Exceptional(OutOfGas))
};
gas = gas_sub(gas, G_cold_account)
};
k_account_mark_warm(beneficiary);
let nonzero_balance = word_nonzero(bal);
let beneficiary_empty = k_account_is_empty(beneficiary);
if nonzero_balance & beneficiary_empty then {
if gas < G_newaccount then {
return (GAS_ZERO, state_gas, state_spill, refund, sp, Exceptional(OutOfGas))
};
gas = gas_sub(gas, G_newaccount)
};
let is_selfdestructed = k_is_selfdestructed(carried_address);
let first_selfdestruct = not_bool(is_selfdestructed);
if (fork < London) & first_selfdestruct then {
refund = record_refund(refund, R_selfdestruct_pre_london)
};
k_transfer(carried_address, beneficiary, bal);
if fork < Cancun then {
k_zero_balance(carried_address);
k_selfdestruct(carried_address)
} else {
let created = k_was_created(carried_address);
if created then {
k_zero_balance(carried_address);
k_selfdestruct(carried_address)
}
};
(gas, state_gas, state_spill, refund, sp, halt_status)
}
}Implements signed SGT.
function execute_sgt(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 2, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < G_verylow then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_verylow;
let a = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let b = read_stack_word(sp);
let result = alu_sgt(a, b);
write_stack_word(sp, result);
(gas, sp, Continue())
}Implements logical left shift SHL (EIP-145).
function execute_shl(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 2, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
/* EIP-145 */
if carried_gas < G_verylow then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_verylow;
let s = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let v = read_stack_word(sp);
let result = alu_shl(s, v);
write_stack_word(sp, result);
(gas, sp, Continue())
}Implements logical right shift SHR (EIP-145).
function execute_shr(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 2, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
/* EIP-145 */
if carried_gas < G_verylow then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_verylow;
let s = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let v = read_stack_word(sp);
let result = alu_shr(s, v);
write_stack_word(sp, result);
(gas, sp, Continue())
}Implements SIGNEXTEND.
function execute_signextend(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 2, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < G_low then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_low;
let bi = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let v = read_stack_word(sp);
let result = alu_signextend(bi, v);
write_stack_word(sp, result);
(gas, sp, Continue())
}Implements SLOAD, including warm/cold access gas.
function execute_sload(
context : AccountExecutionContext,
carried_gas : gas,
carried_sp : StackPointer,
) -> (
(gas, StackPointer, OpcodeOutcome)
) = {
let stack_status = guard_stack(carried_sp, 1, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
/* EIP-2929: cold (2100) vs warm (100) by the slot's accessed-set bit */
let s = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let warm = k_slot_is_warm(context.address, s);
let gas_cost = sload_cost(warm);
if carried_gas < gas_cost then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - gas_cost;
k_slot_mark_warm(context.address, s);
let entry = k_sload(context.address, s);
sp = stack_top_advance(sp, 1);
write_stack_word(sp, entry.curr);
(gas, sp, Continue())
}Implements SLOTNUM.
function execute_slotnum(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 0, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < G_base then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_base;
let slot_number = k_env(F_SlotNumber);
sp = stack_top_advance(sp, 1);
write_stack_word(sp, slot_number);
(gas, sp, Continue())
}Implements signed SLT.
function execute_slt(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 2, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < G_verylow then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_verylow;
let a = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let b = read_stack_word(sp);
let result = alu_slt(a, b);
write_stack_word(sp, result);
(gas, sp, Continue())
}Implements signed SMOD.
function execute_smod(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 2, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < G_low then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_low;
let a = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let b = read_stack_word(sp);
let result = alu_smod(a, b);
write_stack_word(sp, result);
(gas, sp, Continue())
}function execute_sstore(
context,
fork,
carried_is_static,
carried_gas,
carried_state_gas,
carried_state_spill,
carried_refund,
carried_sp,
) = {
let stack_status = guard_stack(carried_sp, 2, 0);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_state_gas, carried_state_spill, carried_refund, carried_sp, stack_status)
};
var gas : gas = carried_gas;
var state_gas : state_gas = carried_state_gas;
var state_spill : state_gas_spill = carried_state_spill;
var refund : gas_refund = carried_refund;
var sp : StackPointer = carried_sp;
var halt : bool = false;
var status : OpcodeOutcome = Continue();
(gas, status) = guard_static(gas, carried_is_static);
if match status {
Failed(_) => true,
_ => false,
} then {
return (gas, state_gas, state_spill, refund, sp, status)
};
if (fork < Amsterdam) & (gas <= G_callstipend) then {
return (gas, state_gas, state_spill, refund, sp, Failed(OutOfGas))
};
let s = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let v = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let warm = k_slot_is_warm(context.address, s);
let cold = not_bool(warm);
if fork >= Amsterdam then {
let sentry_cost = sstore_sentry_cost(cold);
if gas < sentry_cost then {
return (GAS_ZERO, state_gas, state_spill, refund, sp, Failed(OutOfGas))
}
};
k_slot_mark_warm(context.address, s);
let entry = k_sload(context.address, s);
let costs = sstore_costs(entry.orig, entry.curr, v, cold);
if costs.state_credit != 0 then {
(gas, state_gas, state_spill) = credit_state_gas_refund(gas, state_gas, state_spill, costs.state_credit)
};
if gas < costs.execution then {
return (GAS_ZERO, state_gas, state_spill, refund, sp, Failed(OutOfGas))
};
gas = gas_sub(gas, costs.execution);
(halt, gas, state_gas, state_spill) = charge_state_gas(gas, state_gas, state_spill, costs.state_charge);
if halt then {
return (gas, state_gas, state_spill, refund, sp, Failed(OutOfGas))
};
if not_bool(costs.refund == GAS_REFUND_ZERO) then {
refund = record_refund(refund, costs.refund)
};
if entry.curr != v then {
k_sstore(context.address, s, struct { curr = v, orig = entry.orig })
};
(gas, state_gas, state_spill, refund, sp, Continue())
}Implements normal STOP.
function execute_stop() -> FrameStatus = {
let reason = HaltStop();
Halted(reason)
}Implements SUB.
function execute_sub(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 2, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < G_verylow then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_verylow;
let a = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let b = read_stack_word(sp);
let result = alu_sub(a, b);
write_stack_word(sp, result);
(gas, sp, Continue())
}Implements the SWAP1 through SWAP16 family.
function execute_swap(
carried_gas : gas,
carried_sp : StackPointer,
n : stack_operation_index,
) -> (
(gas, StackPointer, OpcodeOutcome)
) = {
let stack_status = guard_stack(carried_sp, n + 1, n + 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
if carried_gas < G_verylow then {
return (GAS_ZERO, carried_sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_verylow;
let top_value = read_stack_word(carried_sp);
let other = stack_slot_read(carried_sp, n);
stack_set(carried_sp, 0, other);
stack_set(carried_sp, n, top_value);
(gas, carried_sp, Continue())
}Implements immediate deep-stack exchange SWAPN.
function execute_swapn(
carried_gas : gas,
carried_sp : StackPointer,
immediate : byte,
) -> (
(gas, StackPointer, OpcodeOutcome)
) = {
let valid_immediate = deep_stack_immediate_valid(immediate);
if not_bool(valid_immediate) then {
return (carried_gas, carried_sp, Failed(InvalidOpcode))
};
let n = decode_single_stack_index(immediate);
let stack_status = guard_stack(carried_sp, n + 1, n + 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
if carried_gas < G_verylow then {
return (GAS_ZERO, carried_sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_verylow;
let top_value = read_stack_word(carried_sp);
let other = stack_slot_read(carried_sp, n);
stack_set(carried_sp, 0, other);
stack_set(carried_sp, n, top_value);
(gas, carried_sp, Continue())
}Implements TIMESTAMP.
function execute_timestamp(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 0, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < G_base then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_base;
let timestamp = k_env(F_Timestamp);
sp = stack_top_advance(sp, 1);
write_stack_word(sp, timestamp);
(gas, sp, Continue())
}Implements transient-storage load TLOAD (EIP-1153).
function execute_tload(
carried_address : address,
carried_gas : gas,
carried_sp : StackPointer,
) -> (
(gas, StackPointer, OpcodeOutcome)
) = {
let stack_status = guard_stack(carried_sp, 1, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
/* EIP-1153 */
if carried_gas < G_warm_access then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_warm_access;
let s = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let value = k_tload(carried_address, s);
sp = stack_top_advance(sp, 1);
write_stack_word(sp, value);
(gas, sp, Continue())
}Implements transient-storage write TSTORE (EIP-1153).
function execute_tstore(
carried_address : address,
carried_is_static : bool,
carried_gas : gas,
carried_sp : StackPointer,
) -> (
(gas, StackPointer, OpcodeOutcome)
) = {
let stack_status = guard_stack(carried_sp, 2, 0);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var gas : gas = carried_gas;
var status : OpcodeOutcome = Continue();
var sp : StackPointer = carried_sp;
/* EIP-1153 */
(gas, status) = guard_static(gas, carried_is_static);
if match status {
Failed(_) => true,
_ => false,
} then {
return (gas, sp, status)
};
if gas < G_warm_access then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
gas = gas_sub(gas, G_warm_access);
let s = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let v = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
k_tstore(carried_address, s, v);
(gas, sp, Continue())
}Implements bitwise XOR.
function execute_xor(carried_gas : gas, carried_sp : StackPointer) -> (gas, StackPointer, OpcodeOutcome) = {
let stack_status = guard_stack(carried_sp, 2, 1);
if match stack_status {
Failed(_) => true,
_ => false,
} then {
return (GAS_ZERO, carried_sp, stack_status)
};
var sp : StackPointer = carried_sp;
if carried_gas < G_verylow then {
return (GAS_ZERO, sp, Failed(OutOfGas))
};
let gas : gas = carried_gas - G_verylow;
let a = read_stack_word(sp);
sp = stack_top_retreat(sp, 1);
let b = read_stack_word(sp);
let result = alu_xor(a, b);
write_stack_word(sp, result);
(gas, sp, Continue())
}function fatal_error(_reason) = exit(())Fetches and decodes the opcode at the carried program counter,
returning the counter advanced past the opcode and any immediate.
Past the end of code the frame implicitly executes STOP (YP).
PUSH0–PUSH32 (0x5f–0x7f) carry an n-byte immediate; Amsterdam's
DUPN/SWAPN/EXCHANGE carry one byte, zero-padded at end of code.
Every other byte decodes via decode_simple.
function fetch(frame_code : Code, current : code_pointer, fork : Fork) -> (code_pointer, ast) = {
let analyzed = frame_code;
let code = code_bytes(analyzed);
let code_length = code.len;
let past_end = not_bool(current < code_length);
if past_end then {
(current, STOP())
} else {
let opcode_byte = slice_byte(code, current);
let opcode : opcode = unsigned(opcode_byte);
let immediate_offset = current + 1;
let available = opcode_available(opcode, fork);
let decoded : (code_pointer, ast) =
if not_bool(available)
then (immediate_offset, INVALID())
else if 95 <= opcode & opcode <= 127 then {
let size : push_width = opcode - 95;
let (after_immediate, value) = decode_push_immediate(frame_code, immediate_offset, size);
(after_immediate, PUSH(size, value))
} else {
let deep_operation = deep_stack_operation(opcode);
match deep_operation {
NotDeepStackOperation => (immediate_offset, decode_simple(opcode, fork)),
operation => {
let (after_instruction, immediate) = decode_deep_immediate(
frame_code,
immediate_offset,
operation,
);
let instruction : ast = match operation {
DeepStackDuplicate => DUPN(immediate),
DeepStackSwap => SWAPN(immediate),
DeepStackExchange => EXCHANGE(immediate),
NotDeepStackOperation => decode_simple(opcode, fork),
};
(after_instruction, instruction)
},
}
};
decoded
}
}Returns the active frame's halt output.
function frame_output(frame_status : FrameStatus) -> OutputSlice =
match frame_status {
Halted(HaltReturn(output)) => output,
Halted(HaltRevert(output)) => output,
_ => EMPTY_OUTPUT_SLICE,
}Pops the most recently pushed continuation, or Empty() at top level.
val frame_stack_pop = impure { c: "frame_stack_pop" } : unit -> FrameContinuationRemoves every suspended-parent continuation.
val frame_stack_reset = impure { c: "frame_stack_reset" } : unit -> unitWhether the frame is still running.
function is_running(frame_status : FrameStatus) -> bool = match frame_status {
Running() => true,
_ => false,
}Converts a non-terminal opcode result into the corresponding frame status.
function opcode_frame_status(result : OpcodeOutcome) -> FrameStatus = match result {
Continue() => Running(),
Failed(kind) => Exceptional(kind),
}Reuses the carried account context when the frame address is unchanged and rebuilds it when execution enters a different account.
function refresh_account_execution_context(
context : AccountExecutionContext,
previous_address : address,
next_address : address,
) -> (
AccountExecutionContext
) =
if previous_address == next_address then {
context
} else {
account_execution_context(next_address)
}Applies the pending operation for one completed child frame.
function resume_frame(
continuation : FrameContinuation,
output : OutputSlice,
child_memory_base : memory_base,
child_gas : gas,
child_state_gas : state_gas,
child_state_spill : state_gas_spill,
child_refund : gas_refund,
child_status : FrameStatus,
child_state_gas_reservoir : state_gas,
) -> (
FrameTransition
) =
match continuation {
Empty() => fatal_error(ExecutionInvalid),
ResumeCall(call) => resume_call(
call,
output,
child_memory_base,
child_gas,
child_state_gas,
child_state_spill,
child_refund,
child_status,
),
ResumeCreate(create) => resume_create(
create,
output,
child_memory_base,
child_gas,
child_state_gas,
child_state_spill,
child_refund,
child_status,
child_state_gas_reservoir,
),
}Executes a message-call instruction through its non-entering failure paths or installs the child frame and returns its initial carried machine state.
function run_call(
carried_pc : code_pointer,
carried_gas : gas,
carried_state_gas : state_gas,
carried_state_spill : state_gas_spill,
carried_refund : gas_refund,
carried_sp : StackPointer,
carried_memory_base : memory_base,
carried_memory_height : memory_height,
carried_caller : address,
carried_address : address,
carried_code_address : address,
carried_value : word,
carried_state_gas_reservoir : state_gas,
carried_is_static : bool,
carried_depth : frame_depth,
carried_code : Code,
carried_calldata : CalldataSlice,
carried_returndata : OutputSlice,
kind : CallKind,
) -> (
FrameTransition
) = {
let stack_inputs = call_stack_inputs(kind);
let stack_status = guard_stack(carried_sp, stack_inputs, 1);
match stack_status {
Failed(halt_kind) => {
let exceptional = exceptional_state(
carried_state_gas,
carried_state_spill,
carried_state_gas_reservoir,
halt_kind,
);
let state_gas_after = exceptional.state_gas_remaining;
let state_spill_after = exceptional.state_gas_spilled;
let status_after = exceptional.status;
struct {
pc = carried_pc,
gas_remaining = GAS_ZERO,
state_gas_remaining = state_gas_after,
state_gas_spilled = state_spill_after,
refund = carried_refund,
status = status_after,
stack_top = carried_sp,
memory_base = carried_memory_base,
memory_height = carried_memory_height,
message =
struct {
caller = carried_caller,
address = carried_address,
code_address = carried_code_address,
value = carried_value,
state_gas_reservoir = carried_state_gas_reservoir,
is_static = carried_is_static,
depth = carried_depth,
},
code = carried_code,
calldata = carried_calldata,
returndata = carried_returndata,
}
},
Continue() => {
let pc_after : code_pointer = carried_pc;
var gas_after : gas = carried_gas;
var state_gas_after : state_gas = carried_state_gas;
var state_spill_after : state_gas_spill = carried_state_spill;
var status_after : FrameStatus = Running();
var sp_after : StackPointer = carried_sp;
var memory_after : memory_height = carried_memory_height;
var returndata_after : OutputSlice = carried_returndata;
let parent_message : Message = struct {
caller = carried_caller,
address = carried_address,
code_address = carried_code_address,
value = carried_value,
state_gas_reservoir = carried_state_gas_reservoir,
is_static = carried_is_static,
depth = carried_depth,
};
let semantics = call_semantics(kind);
let execution_profile = k_execution_profile;
let profile = execution_profile.protocol;
let current_depth = carried_depth;
let caller = carried_address;
let gas_request = read_stack_word(sp_after);
sp_after = stack_top_retreat(sp_after, 1);
let target_word = read_stack_word(sp_after);
sp_after = stack_top_retreat(sp_after, 1);
let target = word_to_address(target_word);
let (value, next_sp) : (word, StackPointer) =
if semantics.takes_value then {
let value = read_stack_word(sp_after);
(value, stack_top_retreat(sp_after, 1))
} else {
(WORD_ZERO, sp_after)
};
sp_after = next_sp;
let value_nonzero = word_nonzero(value);
let args_off_word = read_stack_word(sp_after);
sp_after = stack_top_retreat(sp_after, 1);
let args_len_word = read_stack_word(sp_after);
sp_after = stack_top_retreat(sp_after, 1);
let ret_off_word = read_stack_word(sp_after);
sp_after = stack_top_retreat(sp_after, 1);
let ret_len_word = read_stack_word(sp_after);
sp_after = stack_top_retreat(sp_after, 1);
/* EIP-214: a value-bearing CALL inside a static context is a write and
raises WriteInStaticContext -- an exceptional halt that consumes all the
caller frame's gas. CALLCODE/DELEGATECALL/STATICCALL never trigger it
(CALLCODE has no static guard in the spec; the others force value = 0). */
if semantics.transfers_value & value_nonzero & carried_is_static then {
gas_after = GAS_ZERO;
let exceptional = exceptional_state(
state_gas_after,
state_spill_after,
carried_state_gas_reservoir,
WriteProtection,
);
state_gas_after = exceptional.state_gas_remaining;
…Executes a contract-creation instruction through its non-entering failure paths or installs the initcode child frame and returns its initial state.
function run_create(
carried_pc : code_pointer,
carried_gas : gas,
carried_state_gas : state_gas,
carried_state_spill : state_gas_spill,
carried_refund : gas_refund,
carried_sp : StackPointer,
carried_memory_base : memory_base,
carried_memory_height : memory_height,
carried_caller : address,
carried_address : address,
carried_code_address : address,
carried_value : word,
carried_state_gas_reservoir : state_gas,
carried_is_static : bool,
carried_depth : frame_depth,
carried_code : Code,
carried_calldata : CalldataSlice,
carried_returndata : OutputSlice,
kind : CreateKind,
) -> (
FrameTransition
) = {
let stack_inputs = create_stack_inputs(kind);
let stack_status = guard_stack(carried_sp, stack_inputs, 1);
match stack_status {
Failed(halt_kind) => {
let exceptional = exceptional_state(
carried_state_gas,
carried_state_spill,
carried_state_gas_reservoir,
halt_kind,
);
let state_gas_after = exceptional.state_gas_remaining;
let state_spill_after = exceptional.state_gas_spilled;
let status_after = exceptional.status;
struct {
pc = carried_pc,
gas_remaining = GAS_ZERO,
state_gas_remaining = state_gas_after,
state_gas_spilled = state_spill_after,
refund = carried_refund,
status = status_after,
stack_top = carried_sp,
memory_base = carried_memory_base,
memory_height = carried_memory_height,
message =
struct {
caller = carried_caller,
address = carried_address,
code_address = carried_code_address,
value = carried_value,
state_gas_reservoir = carried_state_gas_reservoir,
is_static = carried_is_static,
depth = carried_depth,
},
code = carried_code,
calldata = carried_calldata,
returndata = carried_returndata,
}
},
Continue() => {
let pc_after : code_pointer = carried_pc;
var gas_after : gas = carried_gas;
var state_gas_after : state_gas = carried_state_gas;
var state_spill_after : state_gas_spill = carried_state_spill;
var status_after : FrameStatus = Running();
var sp_after : StackPointer = carried_sp;
var memory_after : memory_height = carried_memory_height;
var returndata_after : OutputSlice = carried_returndata;
let parent_message : Message = struct {
caller = carried_caller,
address = carried_address,
code_address = carried_code_address,
value = carried_value,
state_gas_reservoir = carried_state_gas_reservoir,
is_static = carried_is_static,
depth = carried_depth,
};
let semantics = create_semantics(kind);
let execution_profile = k_execution_profile;
let profile = execution_profile.protocol;
let current_depth = carried_depth;
let creator = carried_address;
let value = read_stack_word(sp_after);
sp_after = stack_top_retreat(sp_after, 1);
let off_word = read_stack_word(sp_after);
sp_after = stack_top_retreat(sp_after, 1);
let len_word = read_stack_word(sp_after);
sp_after = stack_top_retreat(sp_after, 1);
let (salt, next_sp) : (word, StackPointer) =
if semantics.uses_salt then {
let salt = read_stack_word(sp_after);
(salt, stack_top_retreat(sp_after, 1))
} else {
(WORD_ZERO, sp_after)
};
sp_after = next_sp;
/* EIP-214: CREATE/CREATE2 modifies state and is forbidden in a static
context -- it raises WriteProtection (an exceptional halt consuming all
remaining gas), checked before any charge or child frame. */
if carried_is_static then {
gas_after = GAS_ZERO;
let exceptional = exceptional_state(
state_gas_after,
state_spill_after,
carried_state_gas_reservoir,
WriteProtection,
);
state_gas_after = exceptional.state_gas_remaining;
state_spill_after = exceptional.state_gas_spilled;
status_after = exceptional.status;
return struct {
pc = pc_after,
gas_remaining = gas_after,
state_gas_remaining = state_gas_after,
state_gas_spilled = state_spill_after,
refund = carried_refund,
status = status_after,
…let EMPTY_OUTPUT_SLICE : OutputSliceFields(0, 0) = output_slice(0, 0)let GAS_ZERO : int(0) = 0The active protocol policy and all gas limits derived from the executing header, selected together while decoding the stateless input.
register k_execution_profile : ExecutionProfile = DEFAULT_EXECUTION_PROFILEThe executing payload's header.
register k_header : BlockHeader =
struct {
number = 0,
timestamp = 0,
extra_data = EMPTY_STATELESS_INPUT_SLICE,
gas_limit = 0,
gas_used = 0,
prev_randao = ZERO_WORD,
base_fee = ZERO_WORD,
blob_gas_used = 0,
excess_blob_gas = 0,
state_root = ZERO_HASH,
receipts_root = ZERO_HASH,
logs_bloom = stateless_input_slice(0, 256),
fee_recipient = ZERO_ADDRESS,
parent_hash = ZERO_HASH,
parent_beacon_block_root = ZERO_HASH,
slot_number = 0,
}The semantic account identity carried while executing one frame.
struct AccountExecutionContext = {
address : address,
}The four CALL-family execution modes. Call is an ordinary call;
CallCode combines the caller's storage with the target's code;
DelegateCall additionally inherits the caller and value; and
StaticCall enters a read-only frame.
enum CallKind = { Call, CallCode, DelegateCall, StaticCall }Calldata is either the immutable top-level transaction input or a frozen range of the suspended caller's memory. The variants state the only two protocol-valid provenances instead of exposing the host's region enum.
union CalldataSlice = {
/* the immutable top-level transaction input */
InputCalldata : StatelessInputSlice,
/* a frozen range of the suspended caller's memory */
MemoryCalldata : EvmMemorySlice,
}Existential executable-code value whose concrete byte address and length
remain correlated inside CodeFields.
type Code = {
'off 'len,
code_region_valid_range('off, 'len) & code_valid_length('len).
CodeFields('off, 'len)
}The two contract-creation address schemes. CreateByNonce is ordinary
CREATE; CreateBySalt is EIP-1014 CREATE2. Keeping this as a closed
semantic tag prevents callers from encoding an execution mode in an
otherwise unexplained boolean.
enum CreateKind = { CreateByNonce, CreateBySalt }The reason a block fails validation; one variant per violated block-validity rule.
enum FatalError = {
/* chain config: wrong fork / inactive activation */
InvalidConfig,
/* witness ancestor headers not contiguous */
HeaderChainBroken,
/* a transaction failed to RLP-decode */
RlpDecode,
/* a tx signature did not authenticate its sender */
InvalidSignature,
/* header.gas_limit is outside the consensus domain */
InvalidGasLimit,
/* EIP-7778: a tx exceeds the block's remaining gas */
GasUsedExceedsLimit,
/* a tx exceeds the block's remaining blob gas */
BlobGasLimitExceeded,
/* an invalid tx or a failed block-end system call */
ExecutionInvalid,
/* recomputed cumulative gas != header.gas_used */
InvalidGasUsed,
/* recomputed blob gas != header.blob_gas_used */
InvalidBlobGasUsed,
/* header.excess_blob_gas != expected */
InvalidExcessBlobGas,
/* recomputed post-state root != header.state_root */
InvalidStateRoot,
/* recomputed receipts root != header.receipts_root */
InvalidReceiptsRoot,
/* recomputed logs bloom != header.logs_bloom */
InvalidLogsBloom,
/* recomputed block hash != payload expected hash */
InvalidBlockHash,
/* header.parent_hash != authenticated parent */
InvalidParentHash,
/* EIP-7928: BAL item count > gas_limit / 2000 */
BlockAccessListTooLarge,
/* reconstructed EIP-7928 BAL bytes mismatch */
InvalidBlockAccessList,
/* reconstructed EIP-7685 request bytes mismatch */
InvalidExecutionRequests,
/* a missing/inconsistent proof node (thrown at use) */
WitnessDeficient,
/* an exact protocol integer exceeds its bounded execution representation */
NumericOverflow,
}The pending action performed when a child frame finishes.
union FrameContinuation = {
/*! No suspended parent remains; the completed frame was top-level. */
Empty : unit,
/*! Resume a suspended message-call parent. */
ResumeCall : CallContinuation,
/*! Resume a suspended contract-creation parent. */
ResumeCreate : CreateContinuation
}Per-frame execution status: running, halted normally, or exceptionally halted.
union FrameStatus = {
/* mid-execution */
Running : unit,
/* halted normally (YP §9.4.4) */
Halted : HaltKind,
/* halted exceptionally: all frame gas consumed, effects void */
Exceptional : ExceptionKind
}A frame-output range with its coordinate and length packed existentially.
type OutputSlice = {
'off 'len,
output_region_valid_range('off, 'len).
OutputSliceFields('off, 'len)
}The operand-stack cursor for the active frame, threaded by value through
the interpreter in the state-passing convention and held in the
stack_top frame register at frame boundaries. storage is an opaque
host coordinate while height is the semantic stack height. Keeping the
height in the cursor makes stack validation independent of the host stack
representation. Optimized C refines storage to a native u256 *.
struct StackPointer = {
storage : bits(64),
height : operand_stack_height,
}A 20-byte account address (YP §4.1), in canonical protocol byte order.
type address = vector(20, inc, byte)One constructor per opcode. Immediates are carried inline: PUSH
holds its byte width (0–32) and value, DUP/SWAP hold the index
n, LOG holds its topic count. The constructor groups are labelled
with the opcode byte range they cover. Decoding code bytes into this
AST is fetch's job; undefined bytes decode to INVALID and halt
exceptionally when executed.
union ast = {
/* 0x00, 0x0b: arithmetic */
STOP : unit, ADD : unit, MUL : unit, SUB : unit, DIV : unit,
SDIV : unit, MOD : unit, SMOD : unit, ADDMOD : unit,
MULMOD : unit, EXP : unit, SIGNEXTEND : unit,
/* 0x10, 0x1e: comparison / bitwise */
LT : unit, GT : unit, SLT : unit, SGT : unit, EQ : unit,
ISZERO : unit, AND : unit, OR : unit, XOR : unit, NOT : unit,
BYTE : unit, SHL : unit, SHR : unit, SAR : unit, CLZ : unit,
/* 0x20: keccak */
KECCAK256 : unit,
/* 0x30, 0x3f: environment / account */
ADDRESS : unit, BALANCE : unit, ORIGIN : unit, CALLER : unit,
CALLVALUE : unit, CALLDATALOAD : unit, CALLDATASIZE : unit,
CALLDATACOPY : unit, CODESIZE : unit, CODECOPY : unit,
GASPRICE : unit, EXTCODESIZE : unit, EXTCODECOPY : unit,
RETURNDATASIZE : unit, RETURNDATACOPY : unit, EXTCODEHASH : unit,
/* 0x40, 0x4a: block */
BLOCKHASH : unit, COINBASE : unit, TIMESTAMP : unit, NUMBER : unit,
PREVRANDAO : unit, GASLIMIT : unit, CHAINID : unit,
SELFBALANCE : unit, BASEFEE : unit, BLOBHASH : unit, BLOBBASEFEE : unit,
/* EIP-7843 (0x4b) */
SLOTNUM : unit,
/* 0x50, 0x5e: stack / memory / storage / flow */
POP : unit, MLOAD : unit, MSTORE : unit, MSTORE8 : unit,
SLOAD : unit, SSTORE : unit, JUMP : unit, JUMPI : unit,
PC : unit, MSIZE : unit, GAS : unit, JUMPDEST : unit,
TLOAD : unit, TSTORE : unit, MCOPY : unit,
/* 0x5f, 0x7f: push (width 0..32, value) */
PUSH : (push_width, word),
/* 0x80, 0x9f: dup / swap (n) */
DUP : stack_operation_index, SWAP : stack_operation_index,
/* 0xa0, 0xa4: log (num topics) */
LOG : log_topic_count,
/* 0xe6, 0xe8: EIP-8024 deep-stack access (immediate byte) */
DUPN : byte, SWAPN : byte, EXCHANGE : byte,
/* 0xf0, 0xff: system */
opcode_CREATE : unit, CALL : unit, CALLCODE : unit, RETURN : unit,
DELEGATECALL : unit, CREATE2 : unit, STATICCALL : unit,
REVERT : unit, INVALID : unit, SELFDESTRUCT : unit
}A decreasing bound for the non-recursive interpreter's complete call tree.
type call_tree_steps = range(0, 3 * (2 * (2 ^ 64 - 1)) + 2)An absolute byte position in the code arena.
type code_pointer = range(0, code_region_bound)The accumulated excess blob gas carried between headers (EIP-4844).
type excess_blob_gas = range(0, excess_blob_gas_bound)The nesting depth of an execution frame.
type frame_depth = range(0, call_depth_limit)Available gas in a running EVM frame. Every admitted transaction gas
limit originates in the execution payload's SSZ uint64 gas-limit
domain, and child frames can only receive gas from their parent.
type gas = range(0, 2 ^ 64 - 1)The signed transaction refund accumulator before capping.
type gas_refund = range(
-gas_refund_bound,
gas_refund_bound,
)An absolute byte position in the shared EVM-memory arena.
type memory_base = range(0, memory_region_bound)The active EVM frame's exact relative byte high-water mark. It is carried
beside the frame's absolute memory_base; the host retains no hidden
frame coordinate or lifecycle state.
type memory_height = memory_lengthAmsterdam's per-frame state-gas reservoir. The transaction's total gas
allowance remains in the execution payload's uint64 domain; only the
regular-gas portion and state-gas spill into that portion are capped by
EIP-7825.
type state_gas = range(0, 2 ^ 64 - 1)Execution gas temporarily consumed by Amsterdam state charges. EIP-8037 draws spill only from the regular-gas pool, which is capped by EIP-7825.
type state_gas_spill = range(0, transaction_execution_gas_limit_value)The EVM 256-bit machine word (YP §9.1). A transparent range keeps the mathematical subtype relation visible: narrower non-negative ranges can be passed as words without a model-level conversion.
type word = range(0, 2 ^ 256 - 1)function frame_succeeded¶
Whether the just-finished frame ended successfully: a normal halt
succeeds; a REVERT and any exceptional halt do not (their world
effects are rolled back and CALL/CREATE reports failure).
function frame_succeeded(frame_status : FrameStatus) -> bool =
match frame_status {
Halted(HaltRevert(_)) => false,
Halted(_) => true,
Running() => true,
Exceptional(_) => false,
}Per-frame execution status: running, halted normally, or exceptionally halted.
union FrameStatus = {
/* mid-execution */
Running : unit,
/* halted normally (YP §9.4.4) */
Halted : HaltKind,
/* halted exceptionally: all frame gas consumed, effects void */
Exceptional : ExceptionKind
}Ordinary frame stops. Only RETURN and REVERT produce output bytes.
union HaltKind = {
/* STOP: success, empty return data */
HaltStop : unit,
/* RETURN: success with output data */
HaltReturn : OutputSlice,
/* REVERT: state unwound, output kept (EIP-140) */
HaltRevert : OutputSlice,
/* SELFDESTRUCT: success, empty return data */
HaltSelfDestruct : unit
}function executable_code¶
Selects the code a frame actually executes (EIP-7702). A delegated account runs the code at its delegation target, following exactly one hop; a delegation whose target is a precompile (or has no code) executes as empty code. An undelegated account runs its own code.
function executable_code(target : address, dele : bool, dtgt : address) -> Code =
if dele then {
let delegate_key = k_code_key(dtgt);
let delegate_code = code_db_resolve(delegate_key);
let delegate_precompile = precompile_id_for_address(dtgt);
if delegate_precompile != NotPrecompile then {
EMPTY_CODE
} else {
delegate_code
}
} else {
let target_key = k_code_key(target);
code_db_resolve(target_key)
}The code for a code hash; KECCAK_EMPTY resolves to empty code, and
an unwitnessed hash is a deficient witness.
function code_db_resolve(code_hash : hash) -> Code =
if code_hash == KECCAK_EMPTY then {
EMPTY_CODE
} else {
let code = code_db_lookup(code_hash);
if code.len == 0 then {
fatal_error(WitnessDeficient)
} else {
code
}
}The account's code hash — the code-store key.
function k_code_key(a : address) -> hash = k_aload(a).info.code_hashMaps an address to its active precompile identifier; any other address,
including one whose precompile is not yet active at the current fork,
is NotPrecompile.
function precompile_id_for_address(bytes : address) -> precompile_id = {
match bytes {
_ if bytes == PRECOMPILE_ADDRESS_1 => precompile_id_if_active(Ecrecover),
_ if bytes == PRECOMPILE_ADDRESS_2 => precompile_id_if_active(Sha256),
_ if bytes == PRECOMPILE_ADDRESS_3 => precompile_id_if_active(Ripemd160),
_ if bytes == PRECOMPILE_ADDRESS_4 => precompile_id_if_active(Identity),
_ if bytes == PRECOMPILE_ADDRESS_5 => precompile_id_if_active(Modexp),
_ if bytes == PRECOMPILE_ADDRESS_6 => precompile_id_if_active(Bn254Add),
_ if bytes == PRECOMPILE_ADDRESS_7 => precompile_id_if_active(Bn254Mul),
_ if bytes == PRECOMPILE_ADDRESS_8 => precompile_id_if_active(Bn254Pairing),
_ if bytes == PRECOMPILE_ADDRESS_9 => precompile_id_if_active(Blake2f),
_ if bytes == PRECOMPILE_ADDRESS_10 => precompile_id_if_active(KzgPointEvaluation),
_ if bytes == PRECOMPILE_ADDRESS_11 => precompile_id_if_active(BlsG1Add),
_ if bytes == PRECOMPILE_ADDRESS_12 => precompile_id_if_active(BlsG1Msm),
_ if bytes == PRECOMPILE_ADDRESS_13 => precompile_id_if_active(BlsG2Add),
_ if bytes == PRECOMPILE_ADDRESS_14 => precompile_id_if_active(BlsG2Msm),
_ if bytes == PRECOMPILE_ADDRESS_15 => precompile_id_if_active(BlsPairing),
_ if bytes == PRECOMPILE_ADDRESS_16 => precompile_id_if_active(BlsMapFpToG1),
_ if bytes == PRECOMPILE_ADDRESS_17 => precompile_id_if_active(BlsMapFp2ToG2),
_ if bytes == PRECOMPILE_ADDRESS_256 => precompile_id_if_active(P256Verify),
_ => NotPrecompile,
}
}let EMPTY_CODE : Code = analyzed_code(EMPTY_CODE_SLICE, EMPTY_JUMP_TABLE)Existential executable-code value whose concrete byte address and length
remain correlated inside CodeFields.
type Code = {
'off 'len,
code_region_valid_range('off, 'len) & code_valid_length('len).
CodeFields('off, 'len)
}The closed first-order selector for the precompile catalog. Availability, gas pricing, and execution are separate interpreters of this identifier so their protocol equations remain explicit without function-valued records.
enum PrecompileId = {
/* the closed sentinel: the address is not a precompiled contract */
NotPrecompile,
/* addresses 0x01-0x04: the original Frontier precompiles */
Ecrecover,
Sha256,
Ripemd160,
Identity,
/* address 0x05: EIP-198 modular exponentiation */
Modexp,
/* addresses 0x06-0x08: the EIP-196/EIP-197 BN254 curve precompiles */
Bn254Add,
Bn254Mul,
Bn254Pairing,
/* address 0x09: EIP-152 BLAKE2 compression */
Blake2f,
/* address 0x0a: EIP-4844 KZG point evaluation */
KzgPointEvaluation,
/* addresses 0x0b-0x11: the EIP-2537 BLS12-381 precompiles */
BlsG1Add,
BlsG1Msm,
BlsG2Add,
BlsG2Msm,
BlsPairing,
BlsMapFpToG1,
BlsMapFp2ToG2,
/* address 0x100: EIP-7951 secp256r1 signature verification */
P256Verify,
}A 20-byte account address (YP §4.1), in canonical protocol byte order.
type address = vector(20, inc, byte)The message calls¶
type CallSemantics¶
The behavior selected by one member of the closed CALL-family algebra.
Interpreting CallKind once keeps operand decoding, value transfer, child
identity, and static-context construction coupled instead of re-matching
the tag independently at every use site.
struct CallSemantics = {
takes_value : bool,
transfers_value : bool,
uses_target_address : bool,
inherits_caller_and_value : bool,
enters_static_context : bool,
}function call_semantics¶
Defunctionalizes each CALL-family opcode into the data consumed by the shared message-call interpreter.
function call_semantics(kind : CallKind) -> CallSemantics =
match kind {
Call => struct {
takes_value = true,
transfers_value = true,
uses_target_address = true,
inherits_caller_and_value = false,
enters_static_context = false,
},
CallCode => struct {
takes_value = true,
transfers_value = false,
uses_target_address = false,
inherits_caller_and_value = false,
enters_static_context = false,
},
DelegateCall => struct {
takes_value = false,
transfers_value = false,
uses_target_address = false,
inherits_caller_and_value = true,
enters_static_context = false,
},
StaticCall => struct {
takes_value = false,
transfers_value = false,
uses_target_address = true,
inherits_caller_and_value = false,
enters_static_context = true,
},
}The four CALL-family execution modes. Call is an ordinary call;
CallCode combines the caller's storage with the target's code;
DelegateCall additionally inherits the caller and value; and
StaticCall enters a read-only frame.
enum CallKind = { Call, CallCode, DelegateCall, StaticCall }The behavior selected by one member of the closed CALL-family algebra.
Interpreting CallKind once keeps operand decoding, value transfer, child
identity, and static-context construction coupled instead of re-matching
the tag independently at every use site.
struct CallSemantics = {
takes_value : bool,
transfers_value : bool,
uses_target_address : bool,
inherits_caller_and_value : bool,
enters_static_context : bool,
}function call_stack_inputs¶
The four call opcodes, multiplexed on mode.
0—CALL: a new frame attarget, may transfer value.1—CALLCODE: runs the target's code in the caller's account, may transfer.2—DELEGATECALL(EIP-7): runs the target's code in the caller's account, inheriting the parent's caller/value/static context.3—STATICCALL(EIP-214):CALLwith value 0 and a forced static context.
Operand layout (top of stack first): gas, target, value (for
CALL/CALLCODE), argsOffset, argsLen, retOffset, retLen.
Pushes 1 on success, 0 on failure. Takes the parent's carried machine
state; returns the parent's updated state on the non-entering paths
and the freshly installed child's state after a frame entry.
function call_stack_inputs(kind : CallKind) -> operand_stack_height =
match kind {
Call => 7,
CallCode => 7,
DelegateCall => 6,
StaticCall => 6,
}The four CALL-family execution modes. Call is an ordinary call;
CallCode combines the caller's storage with the target's code;
DelegateCall additionally inherits the caller and value; and
StaticCall enters a read-only frame.
enum CallKind = { Call, CallCode, DelegateCall, StaticCall }The number of words on an operand stack.
type operand_stack_height = range(0, 1024)function run_call¶
Executes a message-call instruction through its non-entering failure paths or installs the child frame and returns its initial carried machine state.
function run_call(
carried_pc : code_pointer,
carried_gas : gas,
carried_state_gas : state_gas,
carried_state_spill : state_gas_spill,
carried_refund : gas_refund,
carried_sp : StackPointer,
carried_memory_base : memory_base,
carried_memory_height : memory_height,
carried_caller : address,
carried_address : address,
carried_code_address : address,
carried_value : word,
carried_state_gas_reservoir : state_gas,
carried_is_static : bool,
carried_depth : frame_depth,
carried_code : Code,
carried_calldata : CalldataSlice,
carried_returndata : OutputSlice,
kind : CallKind,
) -> (
FrameTransition
) = {
let stack_inputs = call_stack_inputs(kind);
let stack_status = guard_stack(carried_sp, stack_inputs, 1);
match stack_status {
Failed(halt_kind) => {
let exceptional = exceptional_state(
carried_state_gas,
carried_state_spill,
carried_state_gas_reservoir,
halt_kind,
);
let state_gas_after = exceptional.state_gas_remaining;
let state_spill_after = exceptional.state_gas_spilled;
let status_after = exceptional.status;
struct {
pc = carried_pc,
gas_remaining = GAS_ZERO,
state_gas_remaining = state_gas_after,
state_gas_spilled = state_spill_after,
refund = carried_refund,
status = status_after,
stack_top = carried_sp,
memory_base = carried_memory_base,
memory_height = carried_memory_height,
message =
struct {
caller = carried_caller,
address = carried_address,
code_address = carried_code_address,
value = carried_value,
state_gas_reservoir = carried_state_gas_reservoir,
is_static = carried_is_static,
depth = carried_depth,
},
code = carried_code,
calldata = carried_calldata,
returndata = carried_returndata,
}
},
Continue() => {
let pc_after : code_pointer = carried_pc;
var gas_after : gas = carried_gas;
var state_gas_after : state_gas = carried_state_gas;
var state_spill_after : state_gas_spill = carried_state_spill;
var status_after : FrameStatus = Running();
var sp_after : StackPointer = carried_sp;
var memory_after : memory_height = carried_memory_height;
var returndata_after : OutputSlice = carried_returndata;
let parent_message : Message = struct {
caller = carried_caller,
address = carried_address,
code_address = carried_code_address,
value = carried_value,
state_gas_reservoir = carried_state_gas_reservoir,
is_static = carried_is_static,
depth = carried_depth,
};
let semantics = call_semantics(kind);
let execution_profile = k_execution_profile;
let profile = execution_profile.protocol;
let current_depth = carried_depth;
let caller = carried_address;
let gas_request = read_stack_word(sp_after);
sp_after = stack_top_retreat(sp_after, 1);
let target_word = read_stack_word(sp_after);
sp_after = stack_top_retreat(sp_after, 1);
let target = word_to_address(target_word);
let (value, next_sp) : (word, StackPointer) =
if semantics.takes_value then {
let value = read_stack_word(sp_after);
(value, stack_top_retreat(sp_after, 1))
} else {
(WORD_ZERO, sp_after)
};
sp_after = next_sp;
let value_nonzero = word_nonzero(value);
let args_off_word = read_stack_word(sp_after);
sp_after = stack_top_retreat(sp_after, 1);
let args_len_word = read_stack_word(sp_after);
sp_after = stack_top_retreat(sp_after, 1);
let ret_off_word = read_stack_word(sp_after);
sp_after = stack_top_retreat(sp_after, 1);
let ret_len_word = read_stack_word(sp_after);
sp_after = stack_top_retreat(sp_after, 1);
/* EIP-214: a value-bearing CALL inside a static context is a write and
raises WriteInStaticContext -- an exceptional halt that consumes all the
caller frame's gas. CALLCODE/DELEGATECALL/STATICCALL never trigger it
(CALLCODE has no static guard in the spec; the others force value = 0). */
if semantics.transfers_value & value_nonzero & carried_is_static then {
gas_after = GAS_ZERO;
let exceptional = exceptional_state(
state_gas_after,
state_spill_after,
carried_state_gas_reservoir,
WriteProtection,
);
state_gas_after = exceptional.state_gas_remaining;
state_spill_after = exceptional.state_gas_spilled;
status_after = exceptional.status;
return struct {
pc = pc_after,
gas_remaining = gas_after,
state_gas_remaining = state_gas_after,
state_gas_spilled = state_spill_after,
refund = carried_refund,
status = status_after,
stack_top = sp_after,
memory_base = carried_memory_base,
memory_height = memory_after,
message = parent_message,
code = carried_code,
calldata = carried_calldata,
returndata = returndata_after,
}
};
/* EIP-2929 access: inspect warmth without mutating state, then mark the
target only after the access charge has been established as payable. */
let warm = k_account_is_warm(target);
let target_cost : gas_constant = account_cost(warm);
let transfer_cost : gas_constant =
if value_nonzero then call_value_cost() else GAS_CONSTANT_ZERO;
/* Compute the mathematical endpoints before narrowing either host range.
Optimized C saturates only its endpoint representation, to a charge
which is greater than every representable live-gas value. */
let args_requested_height = memory_requested_height(args_off_word, args_len_word);
let ret_requested_height = memory_requested_height(ret_off_word, ret_len_word);
let requested_height =
if args_requested_height < ret_requested_height then ret_requested_height else args_requested_height;
let expansion_cost = memory_expansion_gas_cost(memory_after, requested_height, gas_after);
if not_bool(expansion_cost.affordable) then {
gas_after = GAS_ZERO;
let exceptional = exceptional_state(
state_gas_after,
state_spill_after,
carried_state_gas_reservoir,
OutOfGas,
);
state_gas_after = exceptional.state_gas_remaining;
state_spill_after = exceptional.state_gas_spilled;
status_after = exceptional.status;
return struct {
pc = pc_after,
gas_remaining = gas_after,
state_gas_remaining = state_gas_after,
state_gas_spilled = state_spill_after,
refund = carried_refund,
status = status_after,
stack_top = sp_after,
memory_base = carried_memory_base,
memory_height = memory_after,
message = parent_message,
code = carried_code,
calldata = carried_calldata,
returndata = returndata_after,
}
};
gas_after = gas_sub(gas_after, expansion_cost.cost);
/* Static gas is charged before any state access: the target's access and
value-transfer cost is checked after memory gas and before reading any
of the target's state. A frame that cannot afford it halts here, having
touched no account -- so the stateless witness need not prove the target.
(Resolving the EIP-7702 delegation or the empty-account check below reads
the target's account, which an OOG-before-access tx must never do.) */
let static_base : gas_cost = target_cost + transfer_cost;
if gas_after < static_base then {
gas_after = GAS_ZERO;
let exceptional = exceptional_state(
state_gas_after,
state_spill_after,
carried_state_gas_reservoir,
OutOfGas,
);
state_gas_after = exceptional.state_gas_remaining;
state_spill_after = exceptional.state_gas_spilled;
status_after = exceptional.status;
return struct {
pc = pc_after,
gas_remaining = gas_after,
state_gas_remaining = state_gas_after,
state_gas_spilled = state_spill_after,
refund = carried_refund,
status = status_after,
stack_top = sp_after,
memory_base = carried_memory_base,
memory_height = memory_after,
message = parent_message,
code = carried_code,
calldata = carried_calldata,
returndata = returndata_after,
}
};
gas_after = gas_sub(gas_after, static_base);
/* The target access happens once the static charge is affordable. Mark it
before pricing EIP-7702's delegation target: a self-delegation therefore
pays one cold target access followed by one warm delegation access. */
k_account_mark_warm(target);
/* STATE ACCESS (only now that the static gas is covered): the EIP-7702
delegation designation (reads the target's code) + new-account component. */
let (tg_deleg, tg_target) = k_deleg_target(target);
let delegation_cost : gas_constant =
if tg_deleg then {
let dw = k_account_is_warm(tg_target);
account_cost(dw)
} else {
GAS_CONSTANT_ZERO
};
let target_empty = k_account_is_empty(target);
let new_account_charged = profile.fork
>= Amsterdam
& value_nonzero
& semantics.transfers_value
& target_empty;
let create_cost : gas_constant =
if profile.fork < Amsterdam & value_nonzero & semantics.transfers_value & target_empty
then G_newaccount
else GAS_CONSTANT_ZERO;
let additional_cost : gas_cost = delegation_cost + create_cost;
if gas_after < additional_cost then {
gas_after = GAS_ZERO;
let exceptional = exceptional_state(
state_gas_after,
state_spill_after,
carried_state_gas_reservoir,
OutOfGas,
);
state_gas_after = exceptional.state_gas_remaining;
state_spill_after = exceptional.state_gas_spilled;
status_after = exceptional.status;
return struct {
pc = pc_after,
gas_remaining = gas_after,
state_gas_remaining = state_gas_after,
state_gas_spilled = state_spill_after,
refund = carried_refund,
status = status_after,
stack_top = sp_after,
memory_base = carried_memory_base,
memory_height = memory_after,
message = parent_message,
code = carried_code,
calldata = carried_calldata,
returndata = returndata_after,
}
};
gas_after = gas_sub(gas_after, additional_cost);
let stipend : gas =
if value_nonzero then G_callstipend else GAS_ZERO;
/* Forwarded child gas, per the EIP-150
calculate_message_call_gas. The EIP-150 63/64ths cap applies to the gas
left after the access, value-transfer, memory, and new-account costs. */
var base_child : gas = GAS_ZERO;
if profile.fork >= Amsterdam then {
/* Amsterdam charges execution work, then state growth, before
applying EIP-150 to the execution gas left for the child. */
if new_account_charged then {
let (state_gas_halt, next_gas, next_state_gas, next_state_spill) = charge_state_gas(
gas_after,
state_gas_after,
state_spill_after,
G_amsterdam_state_new_account,
);
gas_after = next_gas;
state_gas_after = next_state_gas;
state_spill_after = next_state_spill;
if state_gas_halt then {
gas_after = GAS_ZERO;
let exceptional = exceptional_state(
state_gas_after,
state_spill_after,
carried_state_gas_reservoir,
OutOfGas,
);
state_gas_after = exceptional.state_gas_remaining;
state_spill_after = exceptional.state_gas_spilled;
status_after = exceptional.status;
return struct {
pc = pc_after,
gas_remaining = gas_after,
state_gas_remaining = state_gas_after,
state_gas_spilled = state_spill_after,
refund = carried_refund,
status = status_after,
stack_top = sp_after,
memory_base = carried_memory_base,
memory_height = memory_after,
message = parent_message,
code = carried_code,
calldata = carried_calldata,
returndata = returndata_after,
}
}
};
base_child = call_gas_cap_word(gas_after, gas_request);
if gas_after < base_child then {
gas_after = GAS_ZERO;
let exceptional = exceptional_state(
state_gas_after,
state_spill_after,
carried_state_gas_reservoir,
OutOfGas,
);
state_gas_after = exceptional.state_gas_remaining;
state_spill_after = exceptional.state_gas_spilled;
status_after = exceptional.status;
return struct {
pc = pc_after,
gas_remaining = gas_after,
state_gas_remaining = state_gas_after,
state_gas_spilled = state_spill_after,
refund = carried_refund,
status = status_after,
stack_top = sp_after,
memory_base = carried_memory_base,
memory_height = memory_after,
message = parent_message,
code = carried_code,
calldata = carried_calldata,
returndata = returndata_after,
}
};
gas_after = gas_sub(gas_after, base_child)
} else {
base_child = call_gas_cap_word(gas_after, gas_request);
if gas_after < base_child then {
gas_after = GAS_ZERO;
let exceptional = exceptional_state(
state_gas_after,
state_spill_after,
carried_state_gas_reservoir,
OutOfGas,
);
state_gas_after = exceptional.state_gas_remaining;
state_spill_after = exceptional.state_gas_spilled;
status_after = exceptional.status;
return struct {
pc = pc_after,
gas_remaining = gas_after,
state_gas_remaining = state_gas_after,
state_gas_spilled = state_spill_after,
refund = carried_refund,
status = status_after,
stack_top = sp_after,
memory_base = carried_memory_base,
memory_height = memory_after,
message = parent_message,
code = carried_code,
calldata = carried_calldata,
returndata = returndata_after,
}
};
gas_after = gas_sub(gas_after, base_child)
};
if tg_deleg then {
k_account_mark_warm(tg_target)
};
/* gas is now covered; message preparation loads the
delegate's CODE -- BEFORE the depth/balance guards below. Resolve it now
(flagging a missing-code witness deficiency) so an insufficient-balance
call to a 7702-delegated target still detects an omitted delegate-code
proof; an OOG call returned above and never reaches here. */
if tg_deleg then {
let delegate_key = k_code_key(tg_target);
let _ = code_db_resolve(delegate_key);
/* The resolved delegate's account is read for its code, so the
delegate is touched (kept in the BAL account set). */
let _ = k_aload(tg_target)
};
let args_access = memory_access(args_off_word, args_len_word);
let ret_access = memory_access(ret_off_word, ret_len_word);
let materialized_required_size =
if args_access.requested_height < ret_access.requested_height
then ret_access.requested_height
else args_access.requested_height;
let mem1 = expand_memory(carried_memory_base, memory_after, materialized_required_size);
let args = args_access.range;
let ret = ret_access.range;
let child_gas : gas = conserved_gas_add(base_child, stipend);
/* The target's code hash is read for every call once gas is
* charged, before the depth/balance guards -- so the call target is always a
* state access (kept in the BAL account set), including precompiles and calls
* that then fail the depth/balance guard below. */
let _ = k_aload(target);
/* call-failure guards (checked AFTER gas is charged): the depth ceiling
(EIP-150, 1024 frames) and, for a value-bearing CALL/CALLCODE, the
caller's balance covering the transfer. */
let insufficient_balance : bool =
if semantics.takes_value & value_nonzero then {
let caller_balance = k_get_balance(caller);
let transfer_affordable = word_ule(value, caller_balance);
not_bool(transfer_affordable)
} else {
false
};
let depth_limit = sizeof(call_depth_limit);
if insufficient_balance | (current_depth == depth_limit) then {
returndata_after = returndata_clear();
gas_after = refund_gas(gas_after, child_gas);
if new_account_charged then {
(gas_after, state_gas_after, state_spill_after) = credit_state_gas_refund(
gas_after,
state_gas_after,
state_spill_after,
G_amsterdam_state_new_account,
)
};
sp_after = stack_top_advance(sp_after, 1);
write_stack_word(sp_after, WORD_ZERO);
memory_after = mem1;
struct {
pc = pc_after,
gas_remaining = gas_after,
state_gas_remaining = state_gas_after,
state_gas_spilled = state_spill_after,
refund = carried_refund,
status = status_after,
stack_top = sp_after,
memory_base = carried_memory_base,
memory_height = memory_after,
message = parent_message,
code = carried_code,
calldata = carried_calldata,
returndata = returndata_after,
}
} else {
let selected_precompile = precompile_id_for_address(target);
if selected_precompile != NotPrecompile then {
/* PRECOMPILE call. The precompile set is fork-gated (the highest is
0x100 P256VERIFY, EIP-7951/Osaka); an address outside the active
set is an ordinary code call. The precompile's input is the
child message's calldata: memory_after[args_off .. args_off+args_len).
Gas inspects the same memory source before execution. */
let input_memory = active_memory_slice(carried_memory_base, mem1, args.off, args.len);
let input = MemoryCalldata(input_memory);
/* Gas is checked before execution: an unpayable/OOG precompile call
* must not execute (a BLAKE2F request may carry an enormous round
* count). Failure consumes all child gas and exposes no returndata. */
let precompile_charge = precompile_gas(selected_precompile, input, child_gas);
if precompile_charge.affordable then {
let used = precompile_charge.cost;
let result = run_precompile_slice(selected_precompile, input);
if result.success then {
returndata_after = result.output;
/* the value transfer is part of the successful call */
if semantics.transfers_value & value_nonzero then {
k_transfer(caller, target, value)
};
let return_destination = memory_absolute(carried_memory_base, ret.off);
returndata_copy_prefix(returndata_after, return_destination, ret.len);
let unused : gas = gas_sub(child_gas, used);
gas_after = refund_gas(gas_after, unused);
sp_after = stack_top_advance(sp_after, 1);
write_stack_word(sp_after, WORD_ONE);
memory_after = mem1;
struct {
pc = pc_after,
gas_remaining = gas_after,
state_gas_remaining = state_gas_after,
state_gas_spilled = state_spill_after,
refund = carried_refund,
status = status_after,
stack_top = sp_after,
memory_base = carried_memory_base,
memory_height = memory_after,
message = parent_message,
code = carried_code,
calldata = carried_calldata,
returndata = returndata_after,
}
} else {
returndata_after = returndata_clear();
if new_account_charged then {
(gas_after, state_gas_after, state_spill_after) = credit_state_gas_refund(
gas_after,
state_gas_after,
state_spill_after,
G_amsterdam_state_new_account,
)
};
sp_after = stack_top_advance(sp_after, 1);
write_stack_word(sp_after, WORD_ZERO);
memory_after = mem1;
struct {
pc = pc_after,
gas_remaining = gas_after,
state_gas_remaining = state_gas_after,
state_gas_spilled = state_spill_after,
refund = carried_refund,
status = status_after,
stack_top = sp_after,
memory_base = carried_memory_base,
memory_height = memory_after,
message = parent_message,
code = carried_code,
calldata = carried_calldata,
returndata = returndata_after,
}
}
} else {
returndata_after = returndata_clear();
if new_account_charged then {
(gas_after, state_gas_after, state_spill_after) = credit_state_gas_refund(
gas_after,
state_gas_after,
state_spill_after,
G_amsterdam_state_new_account,
)
};
sp_after = stack_top_advance(sp_after, 1);
write_stack_word(sp_after, WORD_ZERO);
memory_after = mem1;
struct {
pc = pc_after,
gas_remaining = gas_after,
state_gas_remaining = state_gas_after,
state_gas_spilled = state_spill_after,
refund = carried_refund,
status = status_after,
stack_top = sp_after,
memory_base = carried_memory_base,
memory_height = memory_after,
message = parent_message,
code = carried_code,
calldata = carried_calldata,
returndata = returndata_after,
}
}
} else {
/* CODE call. Snapshot the world (so a reverting child can be rolled
back), then perform the CALL value transfer up front. */
let child_depth : frame_depth = current_depth + 1;
let child_code = executable_code(target, tg_deleg, tg_target);
/* the child message context, by call mode:
- address: target for CALL/STATICCALL; the CALLER's own address
for CALLCODE/DELEGATECALL (they run target's code in place);
- caller/value: inherited from the parent for DELEGATECALL,
else the immediate caller and the call's value;
- static: forced for STATICCALL, else inherited (a static frame
stays static for all of its sub-calls). */
let child_addr : address =
if semantics.uses_target_address then target else caller;
let child_caller : address =
if semantics.inherits_caller_and_value then carried_caller else caller;
let child_value : word =
if semantics.inherits_caller_and_value then carried_value else value;
let child_static : bool =
if semantics.enters_static_context then true else carried_is_static;
let bytes = active_memory_slice(carried_memory_base, mem1, args.off, args.len);
let child_memory = evm_memory_slice(bytes.bytes, bytes.len);
let child_calldata = MemoryCalldata(child_memory);
let child_state_gas = state_gas_after;
let running = Running();
let (checkpoint, child_stack, child_memory_base, child_memory_height) = suspend_frame(
pc_after,
gas_after,
sp_after,
carried_memory_base,
mem1,
STATE_GAS_ZERO,
state_spill_after,
carried_refund,
running,
parent_message,
carried_code,
carried_calldata,
);
let call_continuation : CallContinuation = struct {
checkpoint = checkpoint,
return_offset = ret.off,
return_length = ret.len,
new_account_charged = new_account_charged,
};
let continuation = ResumeCall(call_continuation);
frame_stack_push(continuation);
if semantics.transfers_value & value_nonzero then {
k_transfer(caller, target, value)
};
let child_returndata = returndata_clear();
struct {
pc = 0,
gas_remaining = child_gas,
state_gas_remaining = child_state_gas,
state_gas_spilled = STATE_GAS_SPILL_ZERO,
refund = GAS_REFUND_ZERO,
status = running,
stack_top = child_stack,
memory_base = child_memory_base,
memory_height = child_memory_height,
message =
struct {
caller = child_caller,
address = child_addr,
code_address = target,
value = child_value,
state_gas_reservoir = child_state_gas,
is_static = child_static,
depth = child_depth,
},
code = child_code,
calldata = child_calldata,
returndata = child_returndata,
}
}
}
},
}
}The account-access cost for a prior warm bit.
function account_cost(warm : bool) -> gas_constant = {
let execution_profile = k_execution_profile;
let profile = execution_profile.protocol;
if warm then {
G_warm_access
} else if profile.fork >= Amsterdam then {
G_amsterdam_cold_account_access
} else {
G_cold_account
}
}function active_memory_slice(base, mem, off, len) =
if len == 0 then {
EMPTY_EVM_MEMORY_SLICE
} else if mem <= sizeof(memory_region_bound) - base & off + len <= mem then {
let window = mem_view(base, mem, off + len);
sub_slice(window, off, len)
} else {
fatal_error(ExecutionInvalid)
}Applies the EIP-150 forwarding cap to a word-sized gas request.
function call_gas_cap_word(available : gas, requested : word) -> gas = {
let retained : gas = available / 64;
let all_but_64th : gas = gas_sub(available, retained);
if requested < all_but_64th then {
requested
} else {
all_but_64th
}
}Defunctionalizes each CALL-family opcode into the data consumed by the shared message-call interpreter.
function call_semantics(kind : CallKind) -> CallSemantics =
match kind {
Call => struct {
takes_value = true,
transfers_value = true,
uses_target_address = true,
inherits_caller_and_value = false,
enters_static_context = false,
},
CallCode => struct {
takes_value = true,
transfers_value = false,
uses_target_address = false,
inherits_caller_and_value = false,
enters_static_context = false,
},
DelegateCall => struct {
takes_value = false,
transfers_value = false,
uses_target_address = false,
inherits_caller_and_value = true,
enters_static_context = false,
},
StaticCall => struct {
takes_value = false,
transfers_value = false,
uses_target_address = true,
inherits_caller_and_value = false,
enters_static_context = true,
},
}The four call opcodes, multiplexed on mode.
0—CALL: a new frame attarget, may transfer value.1—CALLCODE: runs the target's code in the caller's account, may transfer.2—DELEGATECALL(EIP-7): runs the target's code in the caller's account, inheriting the parent's caller/value/static context.3—STATICCALL(EIP-214):CALLwith value 0 and a forced static context.
Operand layout (top of stack first): gas, target, value (for
CALL/CALLCODE), argsOffset, argsLen, retOffset, retLen.
Pushes 1 on success, 0 on failure. Takes the parent's carried machine
state; returns the parent's updated state on the non-entering paths
and the freshly installed child's state after a frame entry.
function call_stack_inputs(kind : CallKind) -> operand_stack_height =
match kind {
Call => 7,
CallCode => 7,
DelegateCall => 6,
StaticCall => 6,
}The execution-gas component of a value-bearing CALL/CALLCODE. Amsterdam reprices the account write while retaining the 2300 child stipend.
function call_value_cost() -> gas_constant = {
let execution_profile = k_execution_profile;
let profile = execution_profile.protocol;
if profile.fork >= Amsterdam then {
G_amsterdam_call_value
} else {
G_callvalue
}
}function charge_state_gas(g, state_gas_remaining, state_gas_spilled, amount) = {
if amount == 0 then {
return (false, g, state_gas_remaining, state_gas_spilled)
};
let state_left = state_gas_remaining;
if amount <= state_left then {
(false, g, state_left - amount, state_gas_spilled)
} else {
let remainder = amount - state_left;
if remainder <= g then {
let spilled = state_gas_spilled;
(false, g - remainder, STATE_GAS_ZERO, state_gas_spill_add(spilled, remainder))
} else {
(true, g, state_gas_remaining, state_gas_spilled)
}
}
}The code for a code hash; KECCAK_EMPTY resolves to empty code, and
an unwitnessed hash is a deficient witness.
function code_db_resolve(code_hash : hash) -> Code =
if code_hash == KECCAK_EMPTY then {
EMPTY_CODE
} else {
let code = code_db_lookup(code_hash);
if code.len == 0 then {
fatal_error(WitnessDeficient)
} else {
code
}
}function conserved_gas_add(available, credit) =
if credit <= (2 ^ 64 - 1) - available then {
available + credit
} else {
fatal_error(ExecutionInvalid)
}function credit_state_gas_refund(g, state_gas_remaining, state_gas_spilled, amount) = {
let spilled = state_gas_spilled;
if amount <= spilled then {
if amount != 0 then {
(conserved_gas_add(g, amount), state_gas_remaining, spilled - amount)
} else {
(g, state_gas_remaining, state_gas_spilled)
}
} else {
let credited =
if spilled != 0 then conserved_gas_add(g, spilled) else g;
let to_state : state_gas_spill = amount - spilled;
(credited, conserved_gas_add(state_gas_remaining, to_state), STATE_GAS_SPILL_ZERO)
}
}function evm_memory_slice(off, len) =
struct { bytes = off, len = len }function exceptional_state(state_gas_remaining, state_gas_spilled, state_gas_reservoir, k) = {
let execution_profile = k_execution_profile;
let profile = execution_profile.protocol;
if profile.fork >= Amsterdam then {
struct {
state_gas_remaining = state_gas_reservoir,
state_gas_spilled = STATE_GAS_SPILL_ZERO,
status = Exceptional(k),
}
} else {
struct {
state_gas_remaining = state_gas_remaining,
state_gas_spilled = state_gas_spilled,
status = Exceptional(k),
}
}
}Selects the code a frame actually executes (EIP-7702). A delegated account runs the code at its delegation target, following exactly one hop; a delegation whose target is a precompile (or has no code) executes as empty code. An undelegated account runs its own code.
function executable_code(target : address, dele : bool, dtgt : address) -> Code =
if dele then {
let delegate_key = k_code_key(dtgt);
let delegate_code = code_db_resolve(delegate_key);
let delegate_precompile = precompile_id_for_address(dtgt);
if delegate_precompile != NotPrecompile then {
EMPTY_CODE
} else {
delegate_code
}
} else {
let target_key = k_code_key(target);
code_db_resolve(target_key)
}Materializes an already-charged memory high-water mark and returns its updated scalar height.
function expand_memory(base : memory_base, height : memory_height, requested_height : memory_length) -> memory_height = {
if requested_height <= sizeof(memory_region_bound) - base then {
if height < requested_height then {
mem_expand(base, height, requested_height);
requested_height
} else {
height
}
} else {
fatal_error(ExecutionInvalid)
}
}Pushes one suspended-parent continuation.
val frame_stack_push = impure { c: "frame_stack_push" } : FrameContinuation -> unitTotal gas subtraction. Protocol callers establish affordability first; the saturated arm keeps the primitive representation-safe by construction.
function gas_sub(left : gas, right : gas_cost) -> gas =
if right <= left then {
left - right
} else {
GAS_ZERO
}Establishes an opcode's local stack precondition before gas charging or
any instruction side effect. The caller supplies the opcode's constant
Yellow Paper (delta, alpha) values.
function guard_stack(
carried_sp : StackPointer,
inputs : operand_stack_height,
outputs : operand_stack_height,
) -> (
OpcodeOutcome
) = {
let stack_validation = validate_stack(carried_sp, inputs, outputs);
match stack_validation {
StackValid => Continue(),
StackUnderflowFailure => Failed(StackUnderflow),
StackOverflowFailure => Failed(StackOverflow),
}
}The EIP-161 "empty" test on the live account: zero nonce, zero balance, no code.
function k_account_is_empty(a : address) -> bool = {
let account = k_aload(a);
account_info_empty(account.info)
}Returns the address's EIP-2929 warm bit without changing state. Active precompiles are warm independently of the BAL-derived account table.
function k_account_is_warm(a : address) -> bool = {
let precompile_id = precompile_id_for_address(a);
if precompile_id != NotPrecompile then {
true
} else {
account_is_warm(a)
}
}Marks an address warm after the caller has established that its access gas is affordable. Active precompiles need no host-table entry.
function k_account_mark_warm(a : address) -> unit = {
let precompile_id = precompile_id_for_address(a);
if precompile_id != NotPrecompile then {
return ()
};
account_mark_warm(a)
}Resolves an account through the transaction and block overlays before an authenticated witness read. A transaction-overlay hit was already touched when that row was established; misses record the EIP-7928 account touch before consulting block-scoped state.
function k_aload(a : address) -> Account = {
let tx_account = acct_tx_get(a);
if tx_account.found then {
return tx_account.account
};
bal_account_touch(a);
let block_account = acct_block_get(a);
if block_account.found then {
return block_account.account
};
let address_hash = keccak256_address(a);
let account = stateless_account_by_key(k_parent_state_root, address_hash);
acct_block_cache(a, address_hash, account);
account
}The account's code hash — the code-store key.
function k_code_key(a : address) -> hash = k_aload(a).info.code_hashThe delegation target of an account's code, with a validity flag — false when the code is not a designator.
function k_deleg_target(a : address) -> (bool, address) = {
let h : hash = k_code_key(a);
let r : AddressResult = code_db_read_delegation(h);
(r.success, r.address)
}The account balance (BALANCE, SELFBALANCE).
function k_get_balance(a : address) -> word = {
k_aload(a).info.balance
}Moves v wei from src to dst (both updates recorded for frame
rollback; the EVM checks sufficiency before calling) and emits the
EIP-7708 transfer log.
function k_transfer(src : address, dst : address, v : word) -> unit = {
let src_acc = k_aload(src);
let dst_acc = k_aload(dst);
let value_is_zero = word_is_zero(v);
if value_is_zero | (src == dst) then {
return ()
};
let source_balance = alu_sub(src_acc.info.balance, v);
store_account_info(src, src_acc, { src_acc.info with balance = source_balance });
let destination_balance = alu_add(dst_acc.info.balance, v);
store_account_info(dst, dst_acc, { dst_acc.info with balance = destination_balance });
k_emit_transfer_log(src, dst, v)
}Converts a frame-relative coordinate to an absolute arena coordinate.
function memory_absolute(base : memory_base, relative : memory_length) -> memory_base =
if relative <= sizeof(memory_region_bound) - base then {
base + relative
} else {
fatal_error(ExecutionInvalid)
}function memory_access(start, size) =
if size == 0 then {
EMPTY_MEMORY_ACCESS
} else if start <= sizeof(memory_region_bound) then {
let (bounded_start as 'bounded_start) :
{'bounded_start,
'bounded_start == 'start & 0 <= 'bounded_start & 'bounded_start <= memory_region_bound.
int('bounded_start)} = start;
if size <= sizeof(memory_region_bound) - bounded_start then {
let (bounded_size as 'bounded_size) :
{'bounded_size,
'bounded_size == 'size & 0 < 'bounded_size & 'bounded_size <= memory_region_bound - 'bounded_start.
int('bounded_size)} = size;
let range = memory_range(bounded_start, bounded_size);
let requested_height = bounded_start + bounded_size;
let access = struct { range = range, requested_height = requested_height } :
MemoryAccessFields('bounded_start, 'bounded_size, 'bounded_start + 'bounded_size);
access
} else {
fatal_error(ExecutionInvalid)
}
} else {
fatal_error(ExecutionInvalid)
}function memory_expansion_gas_cost(mem, requested_height, available) =
if requested_height <= sizeof(memory_region_bound) then {
let materialized_size : memory_length = requested_height;
let new_words = memory_word_count(materialized_size);
let old_size = memory_high_water(mem);
let old_words = memory_word_count(old_size);
if new_words <= old_words then {
gas_charge(GAS_COST_ZERO)
} else {
let old_cost = mem_cost(old_words);
let new_cost = mem_cost(new_words);
if old_cost <= new_cost then {
let exact_cost = new_cost - old_cost;
if exact_cost <= available then {
let cost : gas_cost = exact_cost;
gas_charge(cost)
} else {
GAS_CHARGE_UNAFFORDABLE
}
} else {
gas_charge(GAS_COST_ZERO)
}
}
} else {
GAS_CHARGE_UNAFFORDABLE
}function memory_requested_height(start, size) =
if size == 0 then {
0
} else if start <= sizeof(memory_region_bound) then {
let (bounded_start as 'bounded_start) :
{'bounded_start,
'bounded_start == 'start & 0 <= 'bounded_start & 'bounded_start <= memory_region_bound.
int('bounded_start)} = start;
if size <= sizeof(memory_region_bound) - bounded_start then {
let (bounded_size as 'bounded_size) :
{'bounded_size,
'bounded_size == 'size & 0 < 'bounded_size & 'bounded_size <= memory_region_bound - 'bounded_start.
int('bounded_size)} = size;
let requested_height : memory_required_endpoint = bounded_start + bounded_size;
requested_height
} else {
sizeof(memory_region_bound + 1)
}
} else {
sizeof(memory_region_bound + 1)
}val not_bool = pure {coq: "negb", lean: "_lean_not", _: "not"}: forall ('p : Bool). bool('p) -> bool(not('p))The gas of the precompile at address num for a given input. Gas is
protocol policy defined entirely here; implementations return only
output. Length-only costs
derive from the word count; the two input-dependent curves (MODEXP,
BLAKE2F rounds) read the input in place. The match arms are the
precompile catalog with their addresses and pricing EIPs.
function precompile_gas(num : precompile_id, input : CalldataSlice, available : gas) -> GasCharge = {
let input_len = calldata_slice_length(input);
let input_length = input_len;
let words = memory_word_count(input_len);
match num {
NotPrecompile => GAS_CHARGE_UNAFFORDABLE,
Ecrecover => fixed_precompile_gas(3000, available), /* ECRECOVER (0x01) */
Sha256 => linear_gas(60, 12, words, available),
Ripemd160 => linear_gas(600, 120, words, available),
Identity => linear_gas(15, 3, words, available),
Modexp => modexp_gas(input, available), /* MODEXP (0x05), EIP-2565/7883 */
Bn254Add => fixed_precompile_gas(150, available), /* BN_ADD (0x06), EIP-1108 */
Bn254Mul => fixed_precompile_gas(6000, available), /* BN_MUL (0x07), EIP-1108 */
Bn254Pairing => linear_gas(45000, 34000, input_length / 192, available), /* BN_PAIRING (0x08), EIP-1108 */
Blake2f => {
let rounds = pc_blake2_rounds(input);
fixed_precompile_gas(rounds, available)
}, /* BLAKE2F */
KzgPointEvaluation => fixed_precompile_gas(50000, available), /* POINT_EVALUATION */
/* EIP-2537 BLS12-381 */
BlsG1Add => fixed_precompile_gas(375, available), /* G1ADD */
BlsG1Msm => bls_msm_gas(BLS_G1_DISCOUNT, 12000, 519, input_length / 160, available),
BlsG2Add => fixed_precompile_gas(600, available), /* G2ADD */
BlsG2Msm => bls_msm_gas(BLS_G2_DISCOUNT, 22500, 524, input_length / 288, available),
BlsPairing => linear_gas(37700, 32600, input_length / 384, available), /* PAIRING_CHECK */
BlsMapFpToG1 => fixed_precompile_gas(5500, available), /* MAP_FP_TO_G1 */
BlsMapFp2ToG2 => fixed_precompile_gas(23800, available), /* MAP_FP2_TO_G2 */
P256Verify => fixed_precompile_gas(6900, available), /* P256VERIFY */
}
}Maps an address to its active precompile identifier; any other address,
including one whose precompile is not yet active at the current fork,
is NotPrecompile.
function precompile_id_for_address(bytes : address) -> precompile_id = {
match bytes {
_ if bytes == PRECOMPILE_ADDRESS_1 => precompile_id_if_active(Ecrecover),
_ if bytes == PRECOMPILE_ADDRESS_2 => precompile_id_if_active(Sha256),
_ if bytes == PRECOMPILE_ADDRESS_3 => precompile_id_if_active(Ripemd160),
_ if bytes == PRECOMPILE_ADDRESS_4 => precompile_id_if_active(Identity),
_ if bytes == PRECOMPILE_ADDRESS_5 => precompile_id_if_active(Modexp),
_ if bytes == PRECOMPILE_ADDRESS_6 => precompile_id_if_active(Bn254Add),
_ if bytes == PRECOMPILE_ADDRESS_7 => precompile_id_if_active(Bn254Mul),
_ if bytes == PRECOMPILE_ADDRESS_8 => precompile_id_if_active(Bn254Pairing),
_ if bytes == PRECOMPILE_ADDRESS_9 => precompile_id_if_active(Blake2f),
_ if bytes == PRECOMPILE_ADDRESS_10 => precompile_id_if_active(KzgPointEvaluation),
_ if bytes == PRECOMPILE_ADDRESS_11 => precompile_id_if_active(BlsG1Add),
_ if bytes == PRECOMPILE_ADDRESS_12 => precompile_id_if_active(BlsG1Msm),
_ if bytes == PRECOMPILE_ADDRESS_13 => precompile_id_if_active(BlsG2Add),
_ if bytes == PRECOMPILE_ADDRESS_14 => precompile_id_if_active(BlsG2Msm),
_ if bytes == PRECOMPILE_ADDRESS_15 => precompile_id_if_active(BlsPairing),
_ if bytes == PRECOMPILE_ADDRESS_16 => precompile_id_if_active(BlsMapFpToG1),
_ if bytes == PRECOMPILE_ADDRESS_17 => precompile_id_if_active(BlsMapFp2ToG2),
_ if bytes == PRECOMPILE_ADDRESS_256 => precompile_id_if_active(P256Verify),
_ => NotPrecompile,
}
}Reads the n=0 slot under a validated cursor.
function read_stack_word(sp : StackPointer) -> word = stack_slot_read(sp, 0)function refund_gas(g, amount) =
conserved_gas_add(g, amount)Clears the returndata buffer (a new sub-call begins).
function returndata_clear() -> OutputSlice = EMPTY_OUTPUT_SLICECopies min(want, size) returndata bytes — the CALL-family output
write-back.
function returndata_copy_prefix(returndata : OutputSlice, dst : memory_base, want : memory_length) -> unit = {
let wanted = want;
let available = returndata_size(returndata);
let copy_length : memory_length =
if wanted < available then wanted else available;
slice_copy(returndata, dst, 0, copy_length)
}Executes a message-call instruction through its non-entering failure paths or installs the child frame and returns its initial carried machine state.
function run_call(
carried_pc : code_pointer,
carried_gas : gas,
carried_state_gas : state_gas,
carried_state_spill : state_gas_spill,
carried_refund : gas_refund,
carried_sp : StackPointer,
carried_memory_base : memory_base,
carried_memory_height : memory_height,
carried_caller : address,
carried_address : address,
carried_code_address : address,
carried_value : word,
carried_state_gas_reservoir : state_gas,
carried_is_static : bool,
carried_depth : frame_depth,
carried_code : Code,
carried_calldata : CalldataSlice,
carried_returndata : OutputSlice,
kind : CallKind,
) -> (
FrameTransition
) = {
let stack_inputs = call_stack_inputs(kind);
let stack_status = guard_stack(carried_sp, stack_inputs, 1);
match stack_status {
Failed(halt_kind) => {
let exceptional = exceptional_state(
carried_state_gas,
carried_state_spill,
carried_state_gas_reservoir,
halt_kind,
);
let state_gas_after = exceptional.state_gas_remaining;
let state_spill_after = exceptional.state_gas_spilled;
let status_after = exceptional.status;
struct {
pc = carried_pc,
gas_remaining = GAS_ZERO,
state_gas_remaining = state_gas_after,
state_gas_spilled = state_spill_after,
refund = carried_refund,
status = status_after,
stack_top = carried_sp,
memory_base = carried_memory_base,
memory_height = carried_memory_height,
message =
struct {
caller = carried_caller,
address = carried_address,
code_address = carried_code_address,
value = carried_value,
state_gas_reservoir = carried_state_gas_reservoir,
is_static = carried_is_static,
depth = carried_depth,
},
code = carried_code,
calldata = carried_calldata,
returndata = carried_returndata,
}
},
Continue() => {
let pc_after : code_pointer = carried_pc;
var gas_after : gas = carried_gas;
var state_gas_after : state_gas = carried_state_gas;
var state_spill_after : state_gas_spill = carried_state_spill;
var status_after : FrameStatus = Running();
var sp_after : StackPointer = carried_sp;
var memory_after : memory_height = carried_memory_height;
var returndata_after : OutputSlice = carried_returndata;
let parent_message : Message = struct {
caller = carried_caller,
address = carried_address,
code_address = carried_code_address,
value = carried_value,
state_gas_reservoir = carried_state_gas_reservoir,
is_static = carried_is_static,
depth = carried_depth,
};
let semantics = call_semantics(kind);
let execution_profile = k_execution_profile;
let profile = execution_profile.protocol;
let current_depth = carried_depth;
let caller = carried_address;
let gas_request = read_stack_word(sp_after);
sp_after = stack_top_retreat(sp_after, 1);
let target_word = read_stack_word(sp_after);
sp_after = stack_top_retreat(sp_after, 1);
let target = word_to_address(target_word);
let (value, next_sp) : (word, StackPointer) =
if semantics.takes_value then {
let value = read_stack_word(sp_after);
(value, stack_top_retreat(sp_after, 1))
} else {
(WORD_ZERO, sp_after)
};
sp_after = next_sp;
let value_nonzero = word_nonzero(value);
let args_off_word = read_stack_word(sp_after);
sp_after = stack_top_retreat(sp_after, 1);
let args_len_word = read_stack_word(sp_after);
sp_after = stack_top_retreat(sp_after, 1);
let ret_off_word = read_stack_word(sp_after);
sp_after = stack_top_retreat(sp_after, 1);
let ret_len_word = read_stack_word(sp_after);
sp_after = stack_top_retreat(sp_after, 1);
/* EIP-214: a value-bearing CALL inside a static context is a write and
raises WriteInStaticContext -- an exceptional halt that consumes all the
caller frame's gas. CALLCODE/DELEGATECALL/STATICCALL never trigger it
(CALLCODE has no static guard in the spec; the others force value = 0). */
if semantics.transfers_value & value_nonzero & carried_is_static then {
gas_after = GAS_ZERO;
let exceptional = exceptional_state(
state_gas_after,
state_spill_after,
carried_state_gas_reservoir,
WriteProtection,
);
state_gas_after = exceptional.state_gas_remaining;
…The precompile dispatch: address to implementation. Gas has already been charged by the caller (precompile_gas).
function run_precompile_slice(num : precompile_id, input : CalldataSlice) -> PrecompileResult =
match num {
NotPrecompile => precompile_failure(),
Ecrecover => run_ecrecover(input),
Sha256 => run_sha256(input),
Ripemd160 => run_ripemd160(input),
Identity => copied_result(input),
Modexp => run_modexp(input),
Bn254Add => {
let success = accelerator_bn254_add(input);
accelerator_result(success, PRECOMPILE_DOUBLE_WORD_LENGTH)
},
Bn254Mul => {
let success = accelerator_bn254_mul(input);
accelerator_result(success, PRECOMPILE_DOUBLE_WORD_LENGTH)
},
Bn254Pairing => {
let input_length = calldata_slice_length(input);
let item_length = BN254_PAIRING_ITEM_LENGTH;
if input_length == (input_length / item_length) * item_length then {
let result = accelerator_bn254_pairing(input);
pairing_result(result)
} else {
precompile_failure()
}
},
Blake2f => run_blake2f(input),
KzgPointEvaluation => run_kzg_point_evaluation(input),
BlsG1Add => run_bls_g1_add(input),
BlsG1Msm => run_bls_g1_msm(input),
BlsG2Add => run_bls_g2_add(input),
BlsG2Msm => run_bls_g2_msm(input),
BlsPairing => run_bls_pairing(input),
BlsMapFpToG1 => run_bls_map_fp_to_g1(input),
BlsMapFp2ToG2 => run_bls_map_fp2_to_g2(input),
P256Verify => run_p256_verify(input),
}Advances a stack cursor by count slots and refreshes its semantic height.
function stack_top_advance(top : StackPointer, count : stack_slot_count) -> StackPointer = {
let storage = stack_top_advance_host(top.storage, count);
struct { storage = storage, height = stack_top_height_host(storage) }
}Retreats a stack cursor by count slots and refreshes its semantic height.
function stack_top_retreat(top : StackPointer, count : stack_slot_count) -> StackPointer = {
let storage = stack_top_retreat_host(top.storage, count);
struct { storage = storage, height = stack_top_height_host(storage) }
}Captures parent execution state and enters child stack and memory frames.
The caller has published its carried machine state to the frame
registers: this checkpoint reads them at the one authoritative
boundary. The host operand-frame cursor mirrors call_depth: push the
empty child operand stack before installing the child's semantic depth,
and pop it before restoring the parent's semantic depth.
function suspend_frame(
pc : code_pointer,
gas_remaining : gas,
stack_top : StackPointer,
memory_base : memory_base,
memory_height : memory_height,
state_gas_remaining : state_gas,
state_gas_spilled : state_gas_spill,
frame_refund : gas_refund,
frame_status : FrameStatus,
message : Message,
frame_code : Code,
calldata : CalldataSlice,
) -> (
(FrameCheckpoint, StackPointer, memory_base, memory_height)
) = {
k_journal_checkpoint();
let child_stack = operand_stack_push_empty_frame();
let child_memory_base = memory_absolute(memory_base, memory_height);
let child_memory_height = MEMORY_HEIGHT_ZERO;
let checkpoint : FrameCheckpoint = struct {
pc = pc,
gas_remaining = gas_remaining,
stack_top = stack_top,
state_gas_remaining = state_gas_remaining,
state_gas_spilled = state_gas_spilled,
refund = frame_refund,
status = frame_status,
message = message,
code = frame_code,
calldata = calldata,
memory_height = memory_height,
};
(checkpoint, child_stack, child_memory_base, child_memory_height)
}function word_nonzero(w) = {
let is_zero = word_is_zero(w);
not_bool(is_zero)
}Converts a word to its low 160-bit address in canonical byte order.
function word_to_address(value : word) -> address = {
let zero_bytes = vector_init(20, 0x00);
var result : address = Address(zero_bytes);
result[0] = get_slice_int(8, value, 152);
result[1] = get_slice_int(8, value, 144);
result[2] = get_slice_int(8, value, 136);
result[3] = get_slice_int(8, value, 128);
result[4] = get_slice_int(8, value, 120);
result[5] = get_slice_int(8, value, 112);
result[6] = get_slice_int(8, value, 104);
result[7] = get_slice_int(8, value, 96);
result[8] = get_slice_int(8, value, 88);
result[9] = get_slice_int(8, value, 80);
result[10] = get_slice_int(8, value, 72);
result[11] = get_slice_int(8, value, 64);
result[12] = get_slice_int(8, value, 56);
result[13] = get_slice_int(8, value, 48);
result[14] = get_slice_int(8, value, 40);
result[15] = get_slice_int(8, value, 32);
result[16] = get_slice_int(8, value, 24);
result[17] = get_slice_int(8, value, 16);
result[18] = get_slice_int(8, value, 8);
result[19] = get_slice_int(8, value, 0);
result
}function word_ule(a, b) = {
let greater = word_ult(b, a);
not_bool(greater)
}Writes the n=0 slot under a validated cursor.
function write_stack_word(sp : StackPointer, value : word) -> unit =
stack_slot_write(sp, 0, value)EIP-7954 code/initcode size bump (65536/131072).
let Amsterdam : int(amsterdam_fork_value) = sizeof(amsterdam_fork_value)let GAS_CONSTANT_ZERO : gas_constant = 0let GAS_REFUND_ZERO : gas_refund = 0let GAS_ZERO : int(0) = 0let G_amsterdam_state_new_account : state_gas_spill = 183600let G_callstipend : gas = 2300let G_newaccount : gas_constant = 25000let STATE_GAS_SPILL_ZERO : int(0) = 0let STATE_GAS_ZERO : int(0) = 0let WORD_ONE : word = word_from_bits(0x0000000000000000000000000000000000000000000000000000000000000001)let WORD_ZERO : word = word_from_bits(0x0000000000000000000000000000000000000000000000000000000000000000)The active protocol policy and all gas limits derived from the executing header, selected together while decoding the stateless input.
register k_execution_profile : ExecutionProfile = DEFAULT_EXECUTION_PROFILEThe suspended parent information needed after a message call returns.
struct CallContinuation = {
checkpoint : FrameCheckpoint,
return_offset : memory_base,
return_length : memory_length,
/* Amsterdam NEW_ACCOUNT state gas paid for the child target. */
new_account_charged : bool,
}The four CALL-family execution modes. Call is an ordinary call;
CallCode combines the caller's storage with the target's code;
DelegateCall additionally inherits the caller and value; and
StaticCall enters a read-only frame.
enum CallKind = { Call, CallCode, DelegateCall, StaticCall }Calldata is either the immutable top-level transaction input or a frozen range of the suspended caller's memory. The variants state the only two protocol-valid provenances instead of exposing the host's region enum.
union CalldataSlice = {
/* the immutable top-level transaction input */
InputCalldata : StatelessInputSlice,
/* a frozen range of the suspended caller's memory */
MemoryCalldata : EvmMemorySlice,
}Existential executable-code value whose concrete byte address and length
remain correlated inside CodeFields.
type Code = {
'off 'len,
code_region_valid_range('off, 'len) & code_valid_length('len).
CodeFields('off, 'len)
}Exceptional halts (YP §9.4.2): each consumes all remaining gas and reverts the frame's state changes.
enum ExceptionKind = {
/* an opcode pops more items than the stack holds */
StackUnderflow,
/* a push would exceed the 1024-item stack limit */
StackOverflow,
/* the operation's cost exceeds the remaining gas */
OutOfGas,
/* an unassigned or fork-inactive opcode, or INVALID (0xfe) */
InvalidOpcode,
/* a jump target that is not a valid JUMPDEST */
InvalidJump,
/* EIP-214: state-changing op inside a STATICCALL */
StaticViolation,
/* a call or create beyond depth 1024 */
CallDepthExceeded,
/* a value transfer exceeding the sender's balance */
InsufficientBalance,
/* EIP-214 write protection */
WriteProtection,
/* EIP-3860 */
InitCodeTooLarge,
/* a nonce at its maximum cannot be bumped (EIP-2681) */
NonceOverflow,
/* EIP-684: CREATE into an occupied account */
AddressCollision,
}Per-frame execution status: running, halted normally, or exceptionally halted.
union FrameStatus = {
/* mid-execution */
Running : unit,
/* halted normally (YP §9.4.4) */
Halted : HaltKind,
/* halted exceptionally: all frame gas consumed, effects void */
Exceptional : ExceptionKind
}The complete carried state installed after entering or resuming a frame. Named fields replace the former positional 19-tuple at this cold semantic boundary; the optimized interpreter immediately unpacks the record into its hot scalar locals.
struct FrameTransition = {
pc : code_pointer,
gas_remaining : gas,
state_gas_remaining : state_gas,
state_gas_spilled : state_gas_spill,
refund : gas_refund,
status : FrameStatus,
stack_top : StackPointer,
memory_base : memory_base,
memory_height : memory_height,
message : Message,
code : Code,
calldata : CalldataSlice,
returndata : OutputSlice,
}The per-frame call message (YP §8, the I tuple): caller, executing address, code owner, value, calldata length, static flag, and call depth.
struct Message = {
/* CALLER (I_s) */
caller : address,
/* account whose code runs (differs under DELEGATECALL/CALLCODE and
EIP-7702 delegation) */
code_address : address,
/* ADDRESS / storage owner = self (I_a) */
address : address,
/* CALLVALUE (I_v) */
value : word,
/* State-gas reservoir available when the frame was entered. */
state_gas_reservoir : state_gas,
/* inside a STATICCALL frame (EIP-214) */
is_static : bool,
/* call depth (YP I_e), bounded by call_depth_limit */
depth : frame_depth,
}Lightweight result of one opcode handler.
union OpcodeOutcome = {
/*! Continue executing the active frame. */
Continue : unit,
/*! Stop the active opcode path with the given exceptional halt. */
Failed : ExceptionKind,
}A frame-output range with its coordinate and length packed existentially.
type OutputSlice = {
'off 'len,
output_region_valid_range('off, 'len).
OutputSliceFields('off, 'len)
}The closed first-order selector for the precompile catalog. Availability, gas pricing, and execution are separate interpreters of this identifier so their protocol equations remain explicit without function-valued records.
enum PrecompileId = {
/* the closed sentinel: the address is not a precompiled contract */
NotPrecompile,
/* addresses 0x01-0x04: the original Frontier precompiles */
Ecrecover,
Sha256,
Ripemd160,
Identity,
/* address 0x05: EIP-198 modular exponentiation */
Modexp,
/* addresses 0x06-0x08: the EIP-196/EIP-197 BN254 curve precompiles */
Bn254Add,
Bn254Mul,
Bn254Pairing,
/* address 0x09: EIP-152 BLAKE2 compression */
Blake2f,
/* address 0x0a: EIP-4844 KZG point evaluation */
KzgPointEvaluation,
/* addresses 0x0b-0x11: the EIP-2537 BLS12-381 precompiles */
BlsG1Add,
BlsG1Msm,
BlsG2Add,
BlsG2Msm,
BlsPairing,
BlsMapFpToG1,
BlsMapFp2ToG2,
/* address 0x100: EIP-7951 secp256r1 signature verification */
P256Verify,
}The operand-stack cursor for the active frame, threaded by value through
the interpreter in the state-passing convention and held in the
stack_top frame register at frame boundaries. storage is an opaque
host coordinate while height is the semantic stack height. Keeping the
height in the cursor makes stack validation independent of the host stack
representation. Optimized C refines storage to a native u256 *.
struct StackPointer = {
storage : bits(64),
height : operand_stack_height,
}A 20-byte account address (YP §4.1), in canonical protocol byte order.
type address = vector(20, inc, byte)The EVM call-frame depth ceiling (Yellow Paper I_e).
type call_depth_limit : Int = 1024An absolute byte position in the code arena.
type code_pointer = range(0, code_region_bound)The nesting depth of an execution frame.
type frame_depth = range(0, call_depth_limit)Available gas in a running EVM frame. Every admitted transaction gas
limit originates in the execution payload's SSZ uint64 gas-limit
domain, and child frames can only receive gas from their parent.
type gas = range(0, 2 ^ 64 - 1)A fixed gas-schedule value used as an opcode or transaction base cost.
type gas_constant = range(0, 45000)A transient computed charge after its affordability or structural bound
has been established. Unaffordable larger computations are represented by
GasCharge.affordable = false rather than materialized as a cost.
type gas_cost = range(0, 2 ^ 64 - 1)The signed transaction refund accumulator before capping.
type gas_refund = range(
-gas_refund_bound,
gas_refund_bound,
)An absolute byte position in the shared EVM-memory arena.
type memory_base = range(0, memory_region_bound)The active EVM frame's exact relative byte high-water mark. It is carried
beside the frame's absolute memory_base; the host retains no hidden
frame coordinate or lifecycle state.
type memory_height = memory_lengthAmsterdam's per-frame state-gas reservoir. The transaction's total gas
allowance remains in the execution payload's uint64 domain; only the
regular-gas portion and state-gas spill into that portion are capped by
EIP-7825.
type state_gas = range(0, 2 ^ 64 - 1)Execution gas temporarily consumed by Amsterdam state charges. EIP-8037 draws spill only from the regular-gas pool, which is capped by EIP-7825.
type state_gas_spill = range(0, transaction_execution_gas_limit_value)The EVM 256-bit machine word (YP §9.1). A transparent range keeps the mathematical subtype relation visible: narrower non-negative ranges can be passed as words without a model-level conversion.
type word = range(0, 2 ^ 256 - 1)type CreateSemantics¶
The behavior selected by one member of the closed CREATE-family algebra.
Interpreting CreateKind once keeps operand decoding, hashing charges, and
address derivation coupled rather than passing an unexplained boolean
through the shared creation path.
struct CreateSemantics = {
uses_salt : bool,
}function create_semantics¶
Defunctionalizes each CREATE-family opcode into the data consumed by the shared contract-creation interpreter.
function create_semantics(kind : CreateKind) -> CreateSemantics =
match kind {
CreateByNonce => struct { uses_salt = false },
CreateBySalt => struct { uses_salt = true },
}The two contract-creation address schemes. CreateByNonce is ordinary
CREATE; CreateBySalt is EIP-1014 CREATE2. Keeping this as a closed
semantic tag prevents callers from encoding an execution mode in an
otherwise unexplained boolean.
enum CreateKind = { CreateByNonce, CreateBySalt }The behavior selected by one member of the closed CREATE-family algebra.
Interpreting CreateKind once keeps operand decoding, hashing charges, and
address derivation coupled rather than passing an unexplained boolean
through the shared creation path.
struct CreateSemantics = {
uses_salt : bool,
}function create_stack_inputs¶
CREATE (CreateByNonce) and CREATE2 (CreateBySalt, EIP-1014).
Both deploy a new contract by running the initcode supplied in
memory; the new address derives from (creator, nonce) for CREATE
or (creator, salt, keccak256(initcode)) for CREATE2. Operand
layout: value, offset, length, salt (for CREATE2). Pushes
the new address on success, 0 on failure. Takes the parent's carried
machine state; returns the parent's updated state on the non-entering
paths and the freshly installed child's state after a frame entry.
function create_stack_inputs(kind : CreateKind) -> operand_stack_height =
match kind {
CreateByNonce => 3,
CreateBySalt => 4,
}The two contract-creation address schemes. CreateByNonce is ordinary
CREATE; CreateBySalt is EIP-1014 CREATE2. Keeping this as a closed
semantic tag prevents callers from encoding an execution mode in an
otherwise unexplained boolean.
enum CreateKind = { CreateByNonce, CreateBySalt }The number of words on an operand stack.
type operand_stack_height = range(0, 1024)function run_create¶
Executes a contract-creation instruction through its non-entering failure paths or installs the initcode child frame and returns its initial state.
function run_create(
carried_pc : code_pointer,
carried_gas : gas,
carried_state_gas : state_gas,
carried_state_spill : state_gas_spill,
carried_refund : gas_refund,
carried_sp : StackPointer,
carried_memory_base : memory_base,
carried_memory_height : memory_height,
carried_caller : address,
carried_address : address,
carried_code_address : address,
carried_value : word,
carried_state_gas_reservoir : state_gas,
carried_is_static : bool,
carried_depth : frame_depth,
carried_code : Code,
carried_calldata : CalldataSlice,
carried_returndata : OutputSlice,
kind : CreateKind,
) -> (
FrameTransition
) = {
let stack_inputs = create_stack_inputs(kind);
let stack_status = guard_stack(carried_sp, stack_inputs, 1);
match stack_status {
Failed(halt_kind) => {
let exceptional = exceptional_state(
carried_state_gas,
carried_state_spill,
carried_state_gas_reservoir,
halt_kind,
);
let state_gas_after = exceptional.state_gas_remaining;
let state_spill_after = exceptional.state_gas_spilled;
let status_after = exceptional.status;
struct {
pc = carried_pc,
gas_remaining = GAS_ZERO,
state_gas_remaining = state_gas_after,
state_gas_spilled = state_spill_after,
refund = carried_refund,
status = status_after,
stack_top = carried_sp,
memory_base = carried_memory_base,
memory_height = carried_memory_height,
message =
struct {
caller = carried_caller,
address = carried_address,
code_address = carried_code_address,
value = carried_value,
state_gas_reservoir = carried_state_gas_reservoir,
is_static = carried_is_static,
depth = carried_depth,
},
code = carried_code,
calldata = carried_calldata,
returndata = carried_returndata,
}
},
Continue() => {
let pc_after : code_pointer = carried_pc;
var gas_after : gas = carried_gas;
var state_gas_after : state_gas = carried_state_gas;
var state_spill_after : state_gas_spill = carried_state_spill;
var status_after : FrameStatus = Running();
var sp_after : StackPointer = carried_sp;
var memory_after : memory_height = carried_memory_height;
var returndata_after : OutputSlice = carried_returndata;
let parent_message : Message = struct {
caller = carried_caller,
address = carried_address,
code_address = carried_code_address,
value = carried_value,
state_gas_reservoir = carried_state_gas_reservoir,
is_static = carried_is_static,
depth = carried_depth,
};
let semantics = create_semantics(kind);
let execution_profile = k_execution_profile;
let profile = execution_profile.protocol;
let current_depth = carried_depth;
let creator = carried_address;
let value = read_stack_word(sp_after);
sp_after = stack_top_retreat(sp_after, 1);
let off_word = read_stack_word(sp_after);
sp_after = stack_top_retreat(sp_after, 1);
let len_word = read_stack_word(sp_after);
sp_after = stack_top_retreat(sp_after, 1);
let (salt, next_sp) : (word, StackPointer) =
if semantics.uses_salt then {
let salt = read_stack_word(sp_after);
(salt, stack_top_retreat(sp_after, 1))
} else {
(WORD_ZERO, sp_after)
};
sp_after = next_sp;
/* EIP-214: CREATE/CREATE2 modifies state and is forbidden in a static
context -- it raises WriteProtection (an exceptional halt consuming all
remaining gas), checked before any charge or child frame. */
if carried_is_static then {
gas_after = GAS_ZERO;
let exceptional = exceptional_state(
state_gas_after,
state_spill_after,
carried_state_gas_reservoir,
WriteProtection,
);
state_gas_after = exceptional.state_gas_remaining;
state_spill_after = exceptional.state_gas_spilled;
status_after = exceptional.status;
return struct {
pc = pc_after,
gas_remaining = gas_after,
state_gas_remaining = state_gas_after,
state_gas_spilled = state_spill_after,
refund = carried_refund,
status = status_after,
stack_top = sp_after,
memory_base = carried_memory_base,
memory_height = memory_after,
message = parent_message,
code = carried_code,
calldata = carried_calldata,
returndata = returndata_after,
}
};
let requested_height = memory_requested_height(off_word, len_word);
let expansion_cost = memory_expansion_gas_cost(memory_after, requested_height, gas_after);
if not_bool(expansion_cost.affordable) then {
gas_after = GAS_ZERO;
let exceptional = exceptional_state(
state_gas_after,
state_spill_after,
carried_state_gas_reservoir,
OutOfGas,
);
state_gas_after = exceptional.state_gas_remaining;
state_spill_after = exceptional.state_gas_spilled;
status_after = exceptional.status;
return struct {
pc = pc_after,
gas_remaining = gas_after,
state_gas_remaining = state_gas_after,
state_gas_spilled = state_spill_after,
refund = carried_refund,
status = status_after,
stack_top = sp_after,
memory_base = carried_memory_base,
memory_height = memory_after,
message = parent_message,
code = carried_code,
calldata = carried_calldata,
returndata = returndata_after,
}
};
gas_after = gas_sub(gas_after, expansion_cost.cost);
let initcode_access = memory_access(off_word, len_word);
let mem1 = expand_memory(carried_memory_base, memory_after, initcode_access.requested_height);
let initcode = initcode_access.range;
/* Up-front charges: memory expansion over the initcode, the CREATE base
cost (G_create), the EIP-3860 per-initcode-word cost, and -- for
CREATE2 only -- the keccak word cost of hashing the initcode for the
address. */
let access_cost = create_access_cost();
if gas_after < access_cost then {
memory_after = mem1;
gas_after = GAS_ZERO;
let exceptional = exceptional_state(
state_gas_after,
state_spill_after,
carried_state_gas_reservoir,
OutOfGas,
);
state_gas_after = exceptional.state_gas_remaining;
state_spill_after = exceptional.state_gas_spilled;
status_after = exceptional.status;
return struct {
pc = pc_after,
gas_remaining = gas_after,
state_gas_remaining = state_gas_after,
state_gas_spilled = state_spill_after,
refund = carried_refund,
status = status_after,
stack_top = sp_after,
memory_base = carried_memory_base,
memory_height = memory_after,
message = parent_message,
code = carried_code,
calldata = carried_calldata,
returndata = returndata_after,
}
};
gas_after = gas_sub(gas_after, access_cost);
let initcode_word_count = memory_word_count_word(len_word);
if profile.fork >= Shanghai then {
let initcode_cost = word_scaled_gas_cost(G_initcode_word, initcode_word_count, gas_after);
if not_bool(initcode_cost.affordable) then {
memory_after = mem1;
gas_after = GAS_ZERO;
let exceptional = exceptional_state(
state_gas_after,
state_spill_after,
carried_state_gas_reservoir,
OutOfGas,
);
state_gas_after = exceptional.state_gas_remaining;
state_spill_after = exceptional.state_gas_spilled;
status_after = exceptional.status;
return struct {
pc = pc_after,
gas_remaining = gas_after,
state_gas_remaining = state_gas_after,
state_gas_spilled = state_spill_after,
refund = carried_refund,
status = status_after,
stack_top = sp_after,
memory_base = carried_memory_base,
memory_height = memory_after,
message = parent_message,
code = carried_code,
calldata = carried_calldata,
returndata = returndata_after,
}
};
gas_after = gas_sub(gas_after, initcode_cost.cost)
};
if semantics.uses_salt then {
let hashing_cost = word_scaled_gas_cost(G_keccak_word, initcode_word_count, gas_after);
if not_bool(hashing_cost.affordable) then {
memory_after = mem1;
gas_after = GAS_ZERO;
let exceptional = exceptional_state(
state_gas_after,
state_spill_after,
carried_state_gas_reservoir,
OutOfGas,
);
state_gas_after = exceptional.state_gas_remaining;
state_spill_after = exceptional.state_gas_spilled;
status_after = exceptional.status;
return struct {
pc = pc_after,
gas_remaining = gas_after,
state_gas_remaining = state_gas_after,
state_gas_spilled = state_spill_after,
refund = carried_refund,
status = status_after,
stack_top = sp_after,
memory_base = carried_memory_base,
memory_height = memory_after,
message = parent_message,
code = carried_code,
calldata = carried_calldata,
returndata = returndata_after,
}
};
gas_after = gas_sub(gas_after, hashing_cost.cost)
};
/* EIP-3860: initcode longer than MAX_INITCODE_SIZE is rejected outright as
an exceptional halt. */
let valid_initcode_size = initcode_size_allowed(initcode.len);
let invalid_initcode_size = not_bool(valid_initcode_size);
if invalid_initcode_size then {
memory_after = mem1;
gas_after = GAS_ZERO;
let exceptional = exceptional_state(
state_gas_after,
state_spill_after,
carried_state_gas_reservoir,
InitCodeTooLarge,
);
state_gas_after = exceptional.state_gas_remaining;
state_spill_after = exceptional.state_gas_spilled;
status_after = exceptional.status;
struct {
pc = pc_after,
gas_remaining = gas_after,
state_gas_remaining = state_gas_after,
state_gas_spilled = state_spill_after,
refund = carried_refund,
status = status_after,
stack_top = sp_after,
memory_base = carried_memory_base,
memory_height = memory_after,
message = parent_message,
code = carried_code,
calldata = carried_calldata,
returndata = returndata_after,
}
} else {
/* derive the new contract address: keccak(0xff, creator, salt,
keccak(initcode)) for CREATE2 (EIP-1014), else rlp(creator, nonce). */
let nonce = k_get_nonce(creator);
let new_addr : address =
if semantics.uses_salt then {
let initcode_digest_word = mem_keccak(carried_memory_base, mem1, initcode);
let initcode_digest = word_to_hash(initcode_digest_word);
k_create2_addr(creator, salt, initcode_digest)
} else {
k_create_addr(creator, nonce)
};
/* Before Amsterdam the child allocation is computed before the
early guards (and returned if they fail). Amsterdam first charges
any account-growth state gas, because a spill reduces the execution
gas to which the EIP-150 cap applies. */
var child_gas : gas = GAS_ZERO;
if profile.fork < Amsterdam then {
let avail = gas_after;
let retained_gas : gas = avail / 64;
child_gas = gas_sub(avail, retained_gas);
gas_after = retained_gas
};
/* early-abort guards (child gas refunded, 0 pushed, NO nonce bump): the
depth ceiling, the creator's balance covering the endowment, and the
creator nonce not already at the 2^64-1 ceiling (it must be
incrementable). */
let creator_balance = k_get_balance(creator);
let endowment_affordable = word_ule(value, creator_balance);
let insufficient_balance = not_bool(endowment_affordable);
let nonce_limit = sizeof(account_nonce_bound);
let depth_limit = sizeof(call_depth_limit);
if insufficient_balance
| (nonce == nonce_limit)
| /* EIP-2681 */
(current_depth == depth_limit) then {
returndata_after = returndata_clear();
gas_after =
if profile.fork < Amsterdam then {
refund_gas(gas_after, child_gas)
} else {
gas_after
};
sp_after = stack_top_advance(sp_after, 1);
write_stack_word(sp_after, WORD_ZERO);
memory_after = mem1;
struct {
pc = pc_after,
gas_remaining = gas_after,
state_gas_remaining = state_gas_after,
state_gas_spilled = state_spill_after,
refund = carried_refund,
status = status_after,
stack_top = sp_after,
memory_base = carried_memory_base,
memory_height = memory_after,
message = parent_message,
code = carried_code,
calldata = carried_calldata,
returndata = returndata_after,
}
} else {
/* warm the new address and bump the creator nonce BEFORE the child
* snapshot: both persist even if the initcode reverts (the
* the protocol runs them in the parent, before
* process_create_message). */
let child_depth : frame_depth = current_depth + 1;
k_account_mark_warm(new_addr);
var new_account_charged : bool = false;
if profile.fork >= Amsterdam then {
new_account_charged = k_account_is_empty(new_addr)
};
if new_account_charged then {
let (state_gas_halt, next_gas, next_state_gas, next_state_spill) = charge_state_gas(
gas_after,
state_gas_after,
state_spill_after,
G_amsterdam_state_new_account,
);
gas_after = next_gas;
state_gas_after = next_state_gas;
state_spill_after = next_state_spill;
if state_gas_halt then {
memory_after = mem1;
gas_after = GAS_ZERO;
let exceptional = exceptional_state(
state_gas_after,
state_spill_after,
carried_state_gas_reservoir,
OutOfGas,
);
state_gas_after = exceptional.state_gas_remaining;
state_spill_after = exceptional.state_gas_spilled;
status_after = exceptional.status;
return struct {
pc = pc_after,
gas_remaining = gas_after,
state_gas_remaining = state_gas_after,
state_gas_spilled = state_spill_after,
refund = carried_refund,
status = status_after,
stack_top = sp_after,
memory_base = carried_memory_base,
memory_height = memory_after,
message = parent_message,
code = carried_code,
calldata = carried_calldata,
returndata = returndata_after,
}
}
};
if profile.fork >= Amsterdam then {
let avail = gas_after;
let retained_gas : gas = avail / 64;
child_gas = gas_sub(avail, retained_gas);
gas_after = retained_gas
};
let occupied : bool = k_account_occupied(new_addr);
returndata_after = returndata_clear();
k_bump_nonce(creator);
if occupied then {
/* address collision (the target already has code, a nonzero
* nonce, or storage): no initcode runs, 0 is pushed, and the
* reserved child gas is NOT refunded (it stays deducted). */
if new_account_charged then {
(gas_after, state_gas_after, state_spill_after) = credit_state_gas_refund(
gas_after,
state_gas_after,
state_spill_after,
G_amsterdam_state_new_account,
)
};
sp_after = stack_top_advance(sp_after, 1);
write_stack_word(sp_after, WORD_ZERO);
memory_after = mem1;
struct {
pc = pc_after,
gas_remaining = gas_after,
state_gas_remaining = state_gas_after,
state_gas_spilled = state_spill_after,
refund = carried_refund,
status = status_after,
stack_top = sp_after,
memory_base = carried_memory_base,
memory_height = memory_after,
message = parent_message,
code = carried_code,
calldata = carried_calldata,
returndata = returndata_after,
}
} else {
/* Freeze the initcode while parent memory is active, then
suspend the complete parent before mutating the new account.
The child frame has no calldata. */
let initcode_bytes = memory_code_slice(carried_memory_base, mem1, initcode.off, initcode.len);
let child_code_id = code_db_insert(initcode_bytes, profile.fork);
let child_code = code_db_resolve(child_code_id);
let child_state_gas = state_gas_after;
let running = Running();
let (checkpoint, child_stack, child_memory_base, child_memory_height) = suspend_frame(
pc_after,
gas_after,
sp_after,
carried_memory_base,
mem1,
STATE_GAS_ZERO,
state_spill_after,
carried_refund,
running,
parent_message,
carried_code,
carried_calldata,
);
let create_continuation : CreateContinuation = struct {
checkpoint = checkpoint,
address = new_addr,
new_account_charged = new_account_charged,
};
let continuation = ResumeCreate(create_continuation);
frame_stack_push(continuation);
k_mark_created(new_addr); /* EIP-6780: created this tx */
k_clear_storage(new_addr);
k_bump_nonce(new_addr); /* EIP-161: a created contract starts at nonce 1 */
k_transfer(creator, new_addr, value);
let child_returndata = returndata_clear();
struct {
pc = 0,
gas_remaining = child_gas,
state_gas_remaining = child_state_gas,
state_gas_spilled = STATE_GAS_SPILL_ZERO,
refund = GAS_REFUND_ZERO,
status = running,
stack_top = child_stack,
memory_base = child_memory_base,
memory_height = child_memory_height,
message =
struct {
caller = creator,
address = new_addr,
code_address = new_addr,
value = value,
state_gas_reservoir = child_state_gas,
is_static = carried_is_static,
depth = child_depth,
},
code = child_code,
calldata = EMPTY_CALLDATA,
returndata = child_returndata,
}
}
}
}
},
}
}function charge_state_gas(g, state_gas_remaining, state_gas_spilled, amount) = {
if amount == 0 then {
return (false, g, state_gas_remaining, state_gas_spilled)
};
let state_left = state_gas_remaining;
if amount <= state_left then {
(false, g, state_left - amount, state_gas_spilled)
} else {
let remainder = amount - state_left;
if remainder <= g then {
let spilled = state_gas_spilled;
(false, g - remainder, STATE_GAS_ZERO, state_gas_spill_add(spilled, remainder))
} else {
(true, g, state_gas_remaining, state_gas_spilled)
}
}
}Analyzes and stores code, returning its content hash.
function code_db_insert(code : CodeSlice, fork : Fork) -> hash = {
let jumpdest_table = analyze_code(code, fork);
let analyzed = analyzed_code(code, jumpdest_table);
code_db_store(analyzed)
}The code for a code hash; KECCAK_EMPTY resolves to empty code, and
an unwitnessed hash is a deficient witness.
function code_db_resolve(code_hash : hash) -> Code =
if code_hash == KECCAK_EMPTY then {
EMPTY_CODE
} else {
let code = code_db_lookup(code_hash);
if code.len == 0 then {
fatal_error(WitnessDeficient)
} else {
code
}
}The CREATE/CREATE2 execution-access charge. Persistent account growth is charged separately as state gas at Amsterdam.
function create_access_cost() -> gas_constant = {
let execution_profile = k_execution_profile;
let profile = execution_profile.protocol;
if profile.fork >= Amsterdam then {
G_amsterdam_create_access
} else {
G_create
}
}Defunctionalizes each CREATE-family opcode into the data consumed by the shared contract-creation interpreter.
function create_semantics(kind : CreateKind) -> CreateSemantics =
match kind {
CreateByNonce => struct { uses_salt = false },
CreateBySalt => struct { uses_salt = true },
}CREATE (CreateByNonce) and CREATE2 (CreateBySalt, EIP-1014).
Both deploy a new contract by running the initcode supplied in
memory; the new address derives from (creator, nonce) for CREATE
or (creator, salt, keccak256(initcode)) for CREATE2. Operand
layout: value, offset, length, salt (for CREATE2). Pushes
the new address on success, 0 on failure. Takes the parent's carried
machine state; returns the parent's updated state on the non-entering
paths and the freshly installed child's state after a frame entry.
function create_stack_inputs(kind : CreateKind) -> operand_stack_height =
match kind {
CreateByNonce => 3,
CreateBySalt => 4,
}function credit_state_gas_refund(g, state_gas_remaining, state_gas_spilled, amount) = {
let spilled = state_gas_spilled;
if amount <= spilled then {
if amount != 0 then {
(conserved_gas_add(g, amount), state_gas_remaining, spilled - amount)
} else {
(g, state_gas_remaining, state_gas_spilled)
}
} else {
let credited =
if spilled != 0 then conserved_gas_add(g, spilled) else g;
let to_state : state_gas_spill = amount - spilled;
(credited, conserved_gas_add(state_gas_remaining, to_state), STATE_GAS_SPILL_ZERO)
}
}function exceptional_state(state_gas_remaining, state_gas_spilled, state_gas_reservoir, k) = {
let execution_profile = k_execution_profile;
let profile = execution_profile.protocol;
if profile.fork >= Amsterdam then {
struct {
state_gas_remaining = state_gas_reservoir,
state_gas_spilled = STATE_GAS_SPILL_ZERO,
status = Exceptional(k),
}
} else {
struct {
state_gas_remaining = state_gas_remaining,
state_gas_spilled = state_gas_spilled,
status = Exceptional(k),
}
}
}Materializes an already-charged memory high-water mark and returns its updated scalar height.
function expand_memory(base : memory_base, height : memory_height, requested_height : memory_length) -> memory_height = {
if requested_height <= sizeof(memory_region_bound) - base then {
if height < requested_height then {
mem_expand(base, height, requested_height);
requested_height
} else {
height
}
} else {
fatal_error(ExecutionInvalid)
}
}Pushes one suspended-parent continuation.
val frame_stack_push = impure { c: "frame_stack_push" } : FrameContinuation -> unitTotal gas subtraction. Protocol callers establish affordability first; the saturated arm keeps the primitive representation-safe by construction.
function gas_sub(left : gas, right : gas_cost) -> gas =
if right <= left then {
left - right
} else {
GAS_ZERO
}Establishes an opcode's local stack precondition before gas charging or
any instruction side effect. The caller supplies the opcode's constant
Yellow Paper (delta, alpha) values.
function guard_stack(
carried_sp : StackPointer,
inputs : operand_stack_height,
outputs : operand_stack_height,
) -> (
OpcodeOutcome
) = {
let stack_validation = validate_stack(carried_sp, inputs, outputs);
match stack_validation {
StackValid => Continue(),
StackUnderflowFailure => Failed(StackUnderflow),
StackOverflowFailure => Failed(StackOverflow),
}
}function initcode_size_allowed(size) = {
let execution_profile = k_execution_profile;
let profile = execution_profile.protocol;
let limit = profile.initcode_size_limit;
(limit == 0) | (size <= limit)
}The EIP-161 "empty" test on the live account: zero nonce, zero balance, no code.
function k_account_is_empty(a : address) -> bool = {
let account = k_aload(a);
account_info_empty(account.info)
}Marks an address warm after the caller has established that its access gas is affordable. Active precompiles need no host-table entry.
function k_account_mark_warm(a : address) -> unit = {
let precompile_id = precompile_id_for_address(a);
if precompile_id != NotPrecompile then {
return ()
};
account_mark_warm(a)
}The CREATE/CREATE2/create-transaction address-collision test
(EIP-684/EIP-7610): the target is occupied if it has code, a nonzero
nonce, or any storage.
function k_account_occupied(a : address) -> bool = {
let acc = k_aload(a);
let info = acc.info;
var anchored_storage : bool = false;
if not_bool(acc.storage_cleared) then {
anchored_storage = info.storage_root != EMPTY_TRIE_ROOT
};
let has_code = info.code_hash != KECCAK_EMPTY;
let has_nonce = info.nonce != 0;
if has_code | has_nonce | anchored_storage then {
true
} else {
storage_has_writes(a)
}
}Increments the account nonce. The u64 increment cannot wrap:
EIP-2681 guards every path that reaches a bump.
function k_bump_nonce(a : address) -> unit = {
let cur = k_aload(a);
let nonce = cur.info.nonce;
if nonce < sizeof(account_nonce_bound) then {
store_account_info(a, cur, { cur.info with nonce = nonce + 1 })
} else {
fatal_error(ExecutionInvalid)
}
}Clears the account's storage (create-time collision cleanup).
function k_clear_storage(a : address) -> unit = {
let cur = k_aload(a);
storage_tx_clear(a);
let cleared = account_clear_storage(cur);
store_account(a, cleared)
}The CREATE2 address rule, in kernel form.
function k_create2_addr(a : address, salt : word, inithash : hash) -> address =
create2_address(a, salt, inithash)The CREATE address rule, in kernel form.
function k_create_addr(a : address, nonce : account_nonce) -> address = create_address(a, nonce)The account balance (BALANCE, SELFBALANCE).
function k_get_balance(a : address) -> word = {
k_aload(a).info.balance
}The account nonce.
function k_get_nonce(a : address) -> account_nonce = {
k_aload(a).info.nonce
}Marks an account as created in this transaction (EIP-6780's same-transaction test).
function k_mark_created(a : address) -> unit = {
let cur = k_aload(a);
store_account(a, { cur with created = true })
}Moves v wei from src to dst (both updates recorded for frame
rollback; the EVM checks sufficiency before calling) and emits the
EIP-7708 transfer log.
function k_transfer(src : address, dst : address, v : word) -> unit = {
let src_acc = k_aload(src);
let dst_acc = k_aload(dst);
let value_is_zero = word_is_zero(v);
if value_is_zero | (src == dst) then {
return ()
};
let source_balance = alu_sub(src_acc.info.balance, v);
store_account_info(src, src_acc, { src_acc.info with balance = source_balance });
let destination_balance = alu_add(dst_acc.info.balance, v);
store_account_info(dst, dst_acc, { dst_acc.info with balance = destination_balance });
k_emit_transfer_log(src, dst, v)
}KECCAK256 over the already-expanded memory range [off, off+len).
function mem_keccak(base : memory_base, mem : memory_height, range : MemoryRange) -> word = {
let bytes = active_memory_slice(base, mem, range.off, range.len);
let digest = keccak256(bytes);
hash_to_word(digest)
}function memory_access(start, size) =
if size == 0 then {
EMPTY_MEMORY_ACCESS
} else if start <= sizeof(memory_region_bound) then {
let (bounded_start as 'bounded_start) :
{'bounded_start,
'bounded_start == 'start & 0 <= 'bounded_start & 'bounded_start <= memory_region_bound.
int('bounded_start)} = start;
if size <= sizeof(memory_region_bound) - bounded_start then {
let (bounded_size as 'bounded_size) :
{'bounded_size,
'bounded_size == 'size & 0 < 'bounded_size & 'bounded_size <= memory_region_bound - 'bounded_start.
int('bounded_size)} = size;
let range = memory_range(bounded_start, bounded_size);
let requested_height = bounded_start + bounded_size;
let access = struct { range = range, requested_height = requested_height } :
MemoryAccessFields('bounded_start, 'bounded_size, 'bounded_start + 'bounded_size);
access
} else {
fatal_error(ExecutionInvalid)
}
} else {
fatal_error(ExecutionInvalid)
}function memory_code_slice(base, mem, off, len) =
if len == 0 then {
EMPTY_CODE_SLICE
} else if mem <= sizeof(memory_region_bound) - base & off + len <= mem then {
let window = mem_view(base, mem, off + len);
let initcode = sub_slice(window, off, len);
code_db_intern_memory(initcode)
} else {
fatal_error(ExecutionInvalid)
}function memory_expansion_gas_cost(mem, requested_height, available) =
if requested_height <= sizeof(memory_region_bound) then {
let materialized_size : memory_length = requested_height;
let new_words = memory_word_count(materialized_size);
let old_size = memory_high_water(mem);
let old_words = memory_word_count(old_size);
if new_words <= old_words then {
gas_charge(GAS_COST_ZERO)
} else {
let old_cost = mem_cost(old_words);
let new_cost = mem_cost(new_words);
if old_cost <= new_cost then {
let exact_cost = new_cost - old_cost;
if exact_cost <= available then {
let cost : gas_cost = exact_cost;
gas_charge(cost)
} else {
GAS_CHARGE_UNAFFORDABLE
}
} else {
gas_charge(GAS_COST_ZERO)
}
}
} else {
GAS_CHARGE_UNAFFORDABLE
}function memory_requested_height(start, size) =
if size == 0 then {
0
} else if start <= sizeof(memory_region_bound) then {
let (bounded_start as 'bounded_start) :
{'bounded_start,
'bounded_start == 'start & 0 <= 'bounded_start & 'bounded_start <= memory_region_bound.
int('bounded_start)} = start;
if size <= sizeof(memory_region_bound) - bounded_start then {
let (bounded_size as 'bounded_size) :
{'bounded_size,
'bounded_size == 'size & 0 < 'bounded_size & 'bounded_size <= memory_region_bound - 'bounded_start.
int('bounded_size)} = size;
let requested_height : memory_required_endpoint = bounded_start + bounded_size;
requested_height
} else {
sizeof(memory_region_bound + 1)
}
} else {
sizeof(memory_region_bound + 1)
}Returns the number of words covering a word-sized byte length without
forming byte_len + 31, whose mathematical intermediate can require 257
bits even though the final quotient remains an EVM word.
function memory_word_count_word(byte_len : word) -> word = {
let word_bytes = u256(32);
let quotient = word_div(byte_len, word_bytes);
let remainder = word_mod(byte_len, word_bytes);
if remainder == WORD_ZERO then {
quotient
} else {
word_add(quotient, WORD_ONE)
}
}val not_bool = pure {coq: "negb", lean: "_lean_not", _: "not"}: forall ('p : Bool). bool('p) -> bool(not('p))Reads the n=0 slot under a validated cursor.
function read_stack_word(sp : StackPointer) -> word = stack_slot_read(sp, 0)function refund_gas(g, amount) =
conserved_gas_add(g, amount)Clears the returndata buffer (a new sub-call begins).
function returndata_clear() -> OutputSlice = EMPTY_OUTPUT_SLICEExecutes a contract-creation instruction through its non-entering failure paths or installs the initcode child frame and returns its initial state.
function run_create(
carried_pc : code_pointer,
carried_gas : gas,
carried_state_gas : state_gas,
carried_state_spill : state_gas_spill,
carried_refund : gas_refund,
carried_sp : StackPointer,
carried_memory_base : memory_base,
carried_memory_height : memory_height,
carried_caller : address,
carried_address : address,
carried_code_address : address,
carried_value : word,
carried_state_gas_reservoir : state_gas,
carried_is_static : bool,
carried_depth : frame_depth,
carried_code : Code,
carried_calldata : CalldataSlice,
carried_returndata : OutputSlice,
kind : CreateKind,
) -> (
FrameTransition
) = {
let stack_inputs = create_stack_inputs(kind);
let stack_status = guard_stack(carried_sp, stack_inputs, 1);
match stack_status {
Failed(halt_kind) => {
let exceptional = exceptional_state(
carried_state_gas,
carried_state_spill,
carried_state_gas_reservoir,
halt_kind,
);
let state_gas_after = exceptional.state_gas_remaining;
let state_spill_after = exceptional.state_gas_spilled;
let status_after = exceptional.status;
struct {
pc = carried_pc,
gas_remaining = GAS_ZERO,
state_gas_remaining = state_gas_after,
state_gas_spilled = state_spill_after,
refund = carried_refund,
status = status_after,
stack_top = carried_sp,
memory_base = carried_memory_base,
memory_height = carried_memory_height,
message =
struct {
caller = carried_caller,
address = carried_address,
code_address = carried_code_address,
value = carried_value,
state_gas_reservoir = carried_state_gas_reservoir,
is_static = carried_is_static,
depth = carried_depth,
},
code = carried_code,
calldata = carried_calldata,
returndata = carried_returndata,
}
},
Continue() => {
let pc_after : code_pointer = carried_pc;
var gas_after : gas = carried_gas;
var state_gas_after : state_gas = carried_state_gas;
var state_spill_after : state_gas_spill = carried_state_spill;
var status_after : FrameStatus = Running();
var sp_after : StackPointer = carried_sp;
var memory_after : memory_height = carried_memory_height;
var returndata_after : OutputSlice = carried_returndata;
let parent_message : Message = struct {
caller = carried_caller,
address = carried_address,
code_address = carried_code_address,
value = carried_value,
state_gas_reservoir = carried_state_gas_reservoir,
is_static = carried_is_static,
depth = carried_depth,
};
let semantics = create_semantics(kind);
let execution_profile = k_execution_profile;
let profile = execution_profile.protocol;
let current_depth = carried_depth;
let creator = carried_address;
let value = read_stack_word(sp_after);
sp_after = stack_top_retreat(sp_after, 1);
let off_word = read_stack_word(sp_after);
sp_after = stack_top_retreat(sp_after, 1);
let len_word = read_stack_word(sp_after);
sp_after = stack_top_retreat(sp_after, 1);
let (salt, next_sp) : (word, StackPointer) =
if semantics.uses_salt then {
let salt = read_stack_word(sp_after);
(salt, stack_top_retreat(sp_after, 1))
} else {
(WORD_ZERO, sp_after)
};
sp_after = next_sp;
/* EIP-214: CREATE/CREATE2 modifies state and is forbidden in a static
context -- it raises WriteProtection (an exceptional halt consuming all
remaining gas), checked before any charge or child frame. */
if carried_is_static then {
gas_after = GAS_ZERO;
let exceptional = exceptional_state(
state_gas_after,
state_spill_after,
carried_state_gas_reservoir,
WriteProtection,
);
state_gas_after = exceptional.state_gas_remaining;
state_spill_after = exceptional.state_gas_spilled;
status_after = exceptional.status;
return struct {
pc = pc_after,
gas_remaining = gas_after,
state_gas_remaining = state_gas_after,
state_gas_spilled = state_spill_after,
refund = carried_refund,
status = status_after,
…Advances a stack cursor by count slots and refreshes its semantic height.
function stack_top_advance(top : StackPointer, count : stack_slot_count) -> StackPointer = {
let storage = stack_top_advance_host(top.storage, count);
struct { storage = storage, height = stack_top_height_host(storage) }
}Retreats a stack cursor by count slots and refreshes its semantic height.
function stack_top_retreat(top : StackPointer, count : stack_slot_count) -> StackPointer = {
let storage = stack_top_retreat_host(top.storage, count);
struct { storage = storage, height = stack_top_height_host(storage) }
}Captures parent execution state and enters child stack and memory frames.
The caller has published its carried machine state to the frame
registers: this checkpoint reads them at the one authoritative
boundary. The host operand-frame cursor mirrors call_depth: push the
empty child operand stack before installing the child's semantic depth,
and pop it before restoring the parent's semantic depth.
function suspend_frame(
pc : code_pointer,
gas_remaining : gas,
stack_top : StackPointer,
memory_base : memory_base,
memory_height : memory_height,
state_gas_remaining : state_gas,
state_gas_spilled : state_gas_spill,
frame_refund : gas_refund,
frame_status : FrameStatus,
message : Message,
frame_code : Code,
calldata : CalldataSlice,
) -> (
(FrameCheckpoint, StackPointer, memory_base, memory_height)
) = {
k_journal_checkpoint();
let child_stack = operand_stack_push_empty_frame();
let child_memory_base = memory_absolute(memory_base, memory_height);
let child_memory_height = MEMORY_HEIGHT_ZERO;
let checkpoint : FrameCheckpoint = struct {
pc = pc,
gas_remaining = gas_remaining,
stack_top = stack_top,
state_gas_remaining = state_gas_remaining,
state_gas_spilled = state_gas_spilled,
refund = frame_refund,
status = frame_status,
message = message,
code = frame_code,
calldata = calldata,
memory_height = memory_height,
};
(checkpoint, child_stack, child_memory_base, child_memory_height)
}Computes a word-sized unit cost only after proving the product affordable, so optimized builds never materialize an overflowing native gas cost.
function word_scaled_gas_cost(per_unit : gas_constant, units : word, available : gas) -> GasCharge = {
if per_unit == 0 | units == 0 then {
return gas_charge(GAS_COST_ZERO)
};
if units <= available then {
let affordable_units : gas_cost = units;
let exact_cost : linear_gas_variable_product = per_unit * affordable_units;
if exact_cost <= available then {
let cost : gas_cost = exact_cost;
gas_charge(cost)
} else {
GAS_CHARGE_UNAFFORDABLE
}
} else {
GAS_CHARGE_UNAFFORDABLE
}
}Serializes an EVM word as a 32-byte big-endian digest.
function word_to_hash(value : word) -> hash = {
let zero_bytes = vector_init(32, 0x00);
var result : hash = B256(zero_bytes);
result[0] = get_slice_int(8, value, 248);
result[1] = get_slice_int(8, value, 240);
result[2] = get_slice_int(8, value, 232);
result[3] = get_slice_int(8, value, 224);
result[4] = get_slice_int(8, value, 216);
result[5] = get_slice_int(8, value, 208);
result[6] = get_slice_int(8, value, 200);
result[7] = get_slice_int(8, value, 192);
result[8] = get_slice_int(8, value, 184);
result[9] = get_slice_int(8, value, 176);
result[10] = get_slice_int(8, value, 168);
result[11] = get_slice_int(8, value, 160);
result[12] = get_slice_int(8, value, 152);
result[13] = get_slice_int(8, value, 144);
result[14] = get_slice_int(8, value, 136);
result[15] = get_slice_int(8, value, 128);
result[16] = get_slice_int(8, value, 120);
result[17] = get_slice_int(8, value, 112);
result[18] = get_slice_int(8, value, 104);
result[19] = get_slice_int(8, value, 96);
result[20] = get_slice_int(8, value, 88);
result[21] = get_slice_int(8, value, 80);
result[22] = get_slice_int(8, value, 72);
result[23] = get_slice_int(8, value, 64);
result[24] = get_slice_int(8, value, 56);
result[25] = get_slice_int(8, value, 48);
result[26] = get_slice_int(8, value, 40);
result[27] = get_slice_int(8, value, 32);
result[28] = get_slice_int(8, value, 24);
result[29] = get_slice_int(8, value, 16);
result[30] = get_slice_int(8, value, 8);
result[31] = get_slice_int(8, value, 0);
result
}function word_ule(a, b) = {
let greater = word_ult(b, a);
not_bool(greater)
}Writes the n=0 slot under a validated cursor.
function write_stack_word(sp : StackPointer, value : word) -> unit =
stack_slot_write(sp, 0, value)EIP-7954 code/initcode size bump (65536/131072).
let Amsterdam : int(amsterdam_fork_value) = sizeof(amsterdam_fork_value)let EMPTY_CALLDATA : CalldataSlice = InputCalldata(EMPTY_STATELESS_INPUT_SLICE)let GAS_REFUND_ZERO : gas_refund = 0let GAS_ZERO : int(0) = 0let G_amsterdam_state_new_account : state_gas_spill = 183600let G_initcode_word : int(2) = 2let G_keccak_word : int(6) = 6let STATE_GAS_SPILL_ZERO : int(0) = 0let STATE_GAS_ZERO : int(0) = 0EIP-3651 warm coinbase, EIP-3855 PUSH0, EIP-3860 initcode.
let Shanghai : int(shanghai_fork_value) = sizeof(shanghai_fork_value)let WORD_ZERO : word = word_from_bits(0x0000000000000000000000000000000000000000000000000000000000000000)The active protocol policy and all gas limits derived from the executing header, selected together while decoding the stateless input.
register k_execution_profile : ExecutionProfile = DEFAULT_EXECUTION_PROFILECalldata is either the immutable top-level transaction input or a frozen range of the suspended caller's memory. The variants state the only two protocol-valid provenances instead of exposing the host's region enum.
union CalldataSlice = {
/* the immutable top-level transaction input */
InputCalldata : StatelessInputSlice,
/* a frozen range of the suspended caller's memory */
MemoryCalldata : EvmMemorySlice,
}Existential executable-code value whose concrete byte address and length
remain correlated inside CodeFields.
type Code = {
'off 'len,
code_region_valid_range('off, 'len) & code_valid_length('len).
CodeFields('off, 'len)
}The suspended parent information needed after initcode returns.
struct CreateContinuation = {
checkpoint : FrameCheckpoint,
address : address,
/* Amsterdam NEW_ACCOUNT state gas paid for the created address. */
new_account_charged : bool,
}The two contract-creation address schemes. CreateByNonce is ordinary
CREATE; CreateBySalt is EIP-1014 CREATE2. Keeping this as a closed
semantic tag prevents callers from encoding an execution mode in an
otherwise unexplained boolean.
enum CreateKind = { CreateByNonce, CreateBySalt }Exceptional halts (YP §9.4.2): each consumes all remaining gas and reverts the frame's state changes.
enum ExceptionKind = {
/* an opcode pops more items than the stack holds */
StackUnderflow,
/* a push would exceed the 1024-item stack limit */
StackOverflow,
/* the operation's cost exceeds the remaining gas */
OutOfGas,
/* an unassigned or fork-inactive opcode, or INVALID (0xfe) */
InvalidOpcode,
/* a jump target that is not a valid JUMPDEST */
InvalidJump,
/* EIP-214: state-changing op inside a STATICCALL */
StaticViolation,
/* a call or create beyond depth 1024 */
CallDepthExceeded,
/* a value transfer exceeding the sender's balance */
InsufficientBalance,
/* EIP-214 write protection */
WriteProtection,
/* EIP-3860 */
InitCodeTooLarge,
/* a nonce at its maximum cannot be bumped (EIP-2681) */
NonceOverflow,
/* EIP-684: CREATE into an occupied account */
AddressCollision,
}Per-frame execution status: running, halted normally, or exceptionally halted.
union FrameStatus = {
/* mid-execution */
Running : unit,
/* halted normally (YP §9.4.4) */
Halted : HaltKind,
/* halted exceptionally: all frame gas consumed, effects void */
Exceptional : ExceptionKind
}The complete carried state installed after entering or resuming a frame. Named fields replace the former positional 19-tuple at this cold semantic boundary; the optimized interpreter immediately unpacks the record into its hot scalar locals.
struct FrameTransition = {
pc : code_pointer,
gas_remaining : gas,
state_gas_remaining : state_gas,
state_gas_spilled : state_gas_spill,
refund : gas_refund,
status : FrameStatus,
stack_top : StackPointer,
memory_base : memory_base,
memory_height : memory_height,
message : Message,
code : Code,
calldata : CalldataSlice,
returndata : OutputSlice,
}The per-frame call message (YP §8, the I tuple): caller, executing address, code owner, value, calldata length, static flag, and call depth.
struct Message = {
/* CALLER (I_s) */
caller : address,
/* account whose code runs (differs under DELEGATECALL/CALLCODE and
EIP-7702 delegation) */
code_address : address,
/* ADDRESS / storage owner = self (I_a) */
address : address,
/* CALLVALUE (I_v) */
value : word,
/* State-gas reservoir available when the frame was entered. */
state_gas_reservoir : state_gas,
/* inside a STATICCALL frame (EIP-214) */
is_static : bool,
/* call depth (YP I_e), bounded by call_depth_limit */
depth : frame_depth,
}Lightweight result of one opcode handler.
union OpcodeOutcome = {
/*! Continue executing the active frame. */
Continue : unit,
/*! Stop the active opcode path with the given exceptional halt. */
Failed : ExceptionKind,
}A frame-output range with its coordinate and length packed existentially.
type OutputSlice = {
'off 'len,
output_region_valid_range('off, 'len).
OutputSliceFields('off, 'len)
}The operand-stack cursor for the active frame, threaded by value through
the interpreter in the state-passing convention and held in the
stack_top frame register at frame boundaries. storage is an opaque
host coordinate while height is the semantic stack height. Keeping the
height in the cursor makes stack validation independent of the host stack
representation. Optimized C refines storage to a native u256 *.
struct StackPointer = {
storage : bits(64),
height : operand_stack_height,
}The largest account nonce admitted by EIP-2681.
type account_nonce_bound : Int = 2 ^ 64 - 1A 20-byte account address (YP §4.1), in canonical protocol byte order.
type address = vector(20, inc, byte)The EVM call-frame depth ceiling (Yellow Paper I_e).
type call_depth_limit : Int = 1024An absolute byte position in the code arena.
type code_pointer = range(0, code_region_bound)The nesting depth of an execution frame.
type frame_depth = range(0, call_depth_limit)Available gas in a running EVM frame. Every admitted transaction gas
limit originates in the execution payload's SSZ uint64 gas-limit
domain, and child frames can only receive gas from their parent.
type gas = range(0, 2 ^ 64 - 1)The signed transaction refund accumulator before capping.
type gas_refund = range(
-gas_refund_bound,
gas_refund_bound,
)An absolute byte position in the shared EVM-memory arena.
type memory_base = range(0, memory_region_bound)The active EVM frame's exact relative byte high-water mark. It is carried
beside the frame's absolute memory_base; the host retains no hidden
frame coordinate or lifecycle state.
type memory_height = memory_lengthAmsterdam's per-frame state-gas reservoir. The transaction's total gas
allowance remains in the execution payload's uint64 domain; only the
regular-gas portion and state-gas spill into that portion are capped by
EIP-7825.
type state_gas = range(0, 2 ^ 64 - 1)Execution gas temporarily consumed by Amsterdam state charges. EIP-8037 draws spill only from the regular-gas pool, which is capped by EIP-7825.
type state_gas_spill = range(0, transaction_execution_gas_limit_value)The EVM 256-bit machine word (YP §9.1). A transparent range keeps the mathematical subtype relation visible: narrower non-negative ranges can be passed as words without a model-level conversion.
type word = range(0, 2 ^ 256 - 1)function resume_call¶
Restores a message-call parent and applies the child's outcome.
function resume_call(
continuation : CallContinuation,
output : OutputSlice,
child_memory_base : memory_base,
child_gas : gas,
child_state_gas : state_gas,
child_state_spill : state_gas_spill,
child_refund : gas_refund,
child_status : FrameStatus,
) -> (
FrameTransition
) = {
let checkpoint = continuation.checkpoint;
let succeeded = frame_succeeded(child_status);
operand_stack_pop_frame();
let parent_memory_base = memory_parent_base(child_memory_base, checkpoint.memory_height);
var parent_gas = refund_gas(checkpoint.gas_remaining, child_gas);
var parent_state_gas : state_gas = checkpoint.state_gas_remaining;
var parent_state_spill : state_gas_spill = checkpoint.state_gas_spilled;
(parent_state_gas, parent_state_spill) = return_child_state_gas(
parent_state_gas,
parent_state_spill,
child_state_gas,
child_state_spill,
);
var parent_refund : gas_refund = checkpoint.refund;
var parent_sp : StackPointer = checkpoint.stack_top;
/* Both RETURN and REVERT copy their output; exceptional halts carry the
* empty slice. Successful effects remain, while every failure reverts to
* the saved kernel checkpoint. */
let return_destination = memory_absolute(parent_memory_base, continuation.return_offset);
returndata_copy_prefix(output, return_destination, continuation.return_length);
if succeeded then {
parent_refund = record_refund(parent_refund, child_refund);
k_journal_commit();
parent_sp = stack_top_advance(parent_sp, 1);
write_stack_word(parent_sp, WORD_ONE)
} else {
k_journal_revert();
if continuation.new_account_charged then {
(parent_gas, parent_state_gas, parent_state_spill) = credit_state_gas_refund(
parent_gas,
parent_state_gas,
parent_state_spill,
G_amsterdam_state_new_account,
)
};
parent_sp = stack_top_advance(parent_sp, 1);
write_stack_word(parent_sp, WORD_ZERO)
};
struct {
pc = checkpoint.pc,
gas_remaining = parent_gas,
state_gas_remaining = parent_state_gas,
state_gas_spilled = parent_state_spill,
refund = parent_refund,
status = checkpoint.status,
stack_top = parent_sp,
memory_base = parent_memory_base,
memory_height = checkpoint.memory_height,
message = checkpoint.message,
code = checkpoint.code,
calldata = checkpoint.calldata,
returndata = output,
}
}function credit_state_gas_refund(g, state_gas_remaining, state_gas_spilled, amount) = {
let spilled = state_gas_spilled;
if amount <= spilled then {
if amount != 0 then {
(conserved_gas_add(g, amount), state_gas_remaining, spilled - amount)
} else {
(g, state_gas_remaining, state_gas_spilled)
}
} else {
let credited =
if spilled != 0 then conserved_gas_add(g, spilled) else g;
let to_state : state_gas_spill = amount - spilled;
(credited, conserved_gas_add(state_gas_remaining, to_state), STATE_GAS_SPILL_ZERO)
}
}Whether the just-finished frame ended successfully: a normal halt
succeeds; a REVERT and any exceptional halt do not (their world
effects are rolled back and CALL/CREATE reports failure).
function frame_succeeded(frame_status : FrameStatus) -> bool =
match frame_status {
Halted(HaltRevert(_)) => false,
Halted(_) => true,
Running() => true,
Exceptional(_) => false,
}Records a successful child frame without discarding its reversible entries.
function k_journal_commit() -> unit = state_journal_commit()Replays the state journal backwards to its innermost open frame boundary.
function k_journal_revert() -> unit = state_journal_revert()Converts a frame-relative coordinate to an absolute arena coordinate.
function memory_absolute(base : memory_base, relative : memory_length) -> memory_base =
if relative <= sizeof(memory_region_bound) - base then {
base + relative
} else {
fatal_error(ExecutionInvalid)
}Restores the parent arena cursor from the current child cursor and the parent's frame-scoped memory height.
function memory_parent_base(child_base : memory_base, parent_height : memory_height) -> memory_base =
if parent_height <= child_base then {
child_base - parent_height
} else {
fatal_error(ExecutionInvalid)
}Pops the current frame's stack storage, restoring the caller's; the caller's cursor is restored from its frame checkpoint.
val operand_stack_pop_frame = impure { c: "operand_stack_pop_frame" } : unit -> unitfunction record_refund(refund, delta) =
validated_refund_add(refund, delta)function refund_gas(g, amount) =
conserved_gas_add(g, amount)function return_child_state_gas(parent_remaining, parent_spilled, child_remaining, child_spilled) = {
let state_room = (2 ^ 64 - 1) - parent_remaining;
if child_remaining <= state_room then {
(parent_remaining + child_remaining, state_gas_spill_add(parent_spilled, child_spilled))
} else {
fatal_error(ExecutionInvalid)
}
}Copies min(want, size) returndata bytes — the CALL-family output
write-back.
function returndata_copy_prefix(returndata : OutputSlice, dst : memory_base, want : memory_length) -> unit = {
let wanted = want;
let available = returndata_size(returndata);
let copy_length : memory_length =
if wanted < available then wanted else available;
slice_copy(returndata, dst, 0, copy_length)
}Advances a stack cursor by count slots and refreshes its semantic height.
function stack_top_advance(top : StackPointer, count : stack_slot_count) -> StackPointer = {
let storage = stack_top_advance_host(top.storage, count);
struct { storage = storage, height = stack_top_height_host(storage) }
}Writes the n=0 slot under a validated cursor.
function write_stack_word(sp : StackPointer, value : word) -> unit =
stack_slot_write(sp, 0, value)let G_amsterdam_state_new_account : state_gas_spill = 183600let WORD_ONE : word = word_from_bits(0x0000000000000000000000000000000000000000000000000000000000000001)let WORD_ZERO : word = word_from_bits(0x0000000000000000000000000000000000000000000000000000000000000000)The suspended parent information needed after a message call returns.
struct CallContinuation = {
checkpoint : FrameCheckpoint,
return_offset : memory_base,
return_length : memory_length,
/* Amsterdam NEW_ACCOUNT state gas paid for the child target. */
new_account_charged : bool,
}Per-frame execution status: running, halted normally, or exceptionally halted.
union FrameStatus = {
/* mid-execution */
Running : unit,
/* halted normally (YP §9.4.4) */
Halted : HaltKind,
/* halted exceptionally: all frame gas consumed, effects void */
Exceptional : ExceptionKind
}The complete carried state installed after entering or resuming a frame. Named fields replace the former positional 19-tuple at this cold semantic boundary; the optimized interpreter immediately unpacks the record into its hot scalar locals.
struct FrameTransition = {
pc : code_pointer,
gas_remaining : gas,
state_gas_remaining : state_gas,
state_gas_spilled : state_gas_spill,
refund : gas_refund,
status : FrameStatus,
stack_top : StackPointer,
memory_base : memory_base,
memory_height : memory_height,
message : Message,
code : Code,
calldata : CalldataSlice,
returndata : OutputSlice,
}A frame-output range with its coordinate and length packed existentially.
type OutputSlice = {
'off 'len,
output_region_valid_range('off, 'len).
OutputSliceFields('off, 'len)
}The operand-stack cursor for the active frame, threaded by value through
the interpreter in the state-passing convention and held in the
stack_top frame register at frame boundaries. storage is an opaque
host coordinate while height is the semantic stack height. Keeping the
height in the cursor makes stack validation independent of the host stack
representation. Optimized C refines storage to a native u256 *.
struct StackPointer = {
storage : bits(64),
height : operand_stack_height,
}Available gas in a running EVM frame. Every admitted transaction gas
limit originates in the execution payload's SSZ uint64 gas-limit
domain, and child frames can only receive gas from their parent.
type gas = range(0, 2 ^ 64 - 1)The signed transaction refund accumulator before capping.
type gas_refund = range(
-gas_refund_bound,
gas_refund_bound,
)An absolute byte position in the shared EVM-memory arena.
type memory_base = range(0, memory_region_bound)The active EVM frame's exact relative byte high-water mark. It is carried
beside the frame's absolute memory_base; the host retains no hidden
frame coordinate or lifecycle state.
type memory_height = memory_lengthAmsterdam's per-frame state-gas reservoir. The transaction's total gas
allowance remains in the execution payload's uint64 domain; only the
regular-gas portion and state-gas spill into that portion are capped by
EIP-7825.
type state_gas = range(0, 2 ^ 64 - 1)Execution gas temporarily consumed by Amsterdam state charges. EIP-8037 draws spill only from the regular-gas pool, which is capped by EIP-7825.
type state_gas_spill = range(0, transaction_execution_gas_limit_value)function resume_create¶
Restores a create parent and either deploys or rolls back the child.
function resume_create(
continuation : CreateContinuation,
output : OutputSlice,
child_memory_base : memory_base,
child_gas : gas,
child_state_gas : state_gas,
child_state_spill : state_gas_spill,
child_refund : gas_refund,
child_status : FrameStatus,
child_state_gas_reservoir : state_gas,
) -> (
FrameTransition
) = {
let execution_profile = k_execution_profile;
let profile = execution_profile.protocol;
let checkpoint = continuation.checkpoint;
let initcode_succeeded = frame_succeeded(child_status);
let deployed_length = returndata_size(output);
let deployed_size = deployed_length;
var frontier_empty_deposit : bool = false;
var settled_child_gas : gas = child_gas;
var settled_child_state_gas : state_gas = child_state_gas;
var settled_child_state_spill : state_gas_spill = child_state_spill;
var settled_child_status : FrameStatus = child_status;
if initcode_succeeded then {
let deployed_size_allowed = deployed_code_size_allowed(deployed_size);
let invalid_deployed_size = not_bool(deployed_size_allowed);
let prohibited_prefix =
if deployed_size != 0 then {
let first_byte = slice_byte(output, 0);
first_byte == 0xef
} else {
false
};
if invalid_deployed_size | ((profile.fork >= London) & prohibited_prefix) then {
/* EIP-170 and EIP-3541 failures use the post-Homestead
* exceptional creation path. Neither rule is active on
* Frontier/Homestead in a way that reaches this branch. */
settled_child_gas = GAS_ZERO;
let exceptional = exceptional_state(
settled_child_state_gas,
settled_child_state_spill,
child_state_gas_reservoir,
OutOfGas,
);
settled_child_state_gas = exceptional.state_gas_remaining;
settled_child_state_spill = exceptional.state_gas_spilled;
settled_child_status = exceptional.status
} else {
let deployment_charge = code_deployment_execution_cost(deployed_length, settled_child_gas);
if deployment_charge.affordable then {
let execution_deposit = deployment_charge.cost;
settled_child_gas = gas_sub(settled_child_gas, execution_deposit);
let state_deposit = code_deployment_state_cost(deployed_length);
var deployment_halt : bool = false;
(deployment_halt, settled_child_gas, settled_child_state_gas, settled_child_state_spill) = charge_state_gas(
settled_child_gas,
settled_child_state_gas,
settled_child_state_spill,
state_deposit,
);
if deployment_halt then {
settled_child_gas = GAS_ZERO;
let exceptional = exceptional_state(
settled_child_state_gas,
settled_child_state_spill,
child_state_gas_reservoir,
OutOfGas,
);
settled_child_state_gas = exceptional.state_gas_remaining;
settled_child_state_spill = exceptional.state_gas_spilled;
settled_child_status = exceptional.status
}
} else if profile.fork < Homestead then {
/* Frontier consumed the remaining child gas but kept the
* creation successful with empty deployed code. EIP-2
* changed this to exceptional failure at Homestead. */
settled_child_gas = GAS_ZERO;
frontier_empty_deposit = true
} else {
settled_child_gas = GAS_ZERO;
let exceptional = exceptional_state(
settled_child_state_gas,
settled_child_state_spill,
child_state_gas_reservoir,
OutOfGas,
);
settled_child_state_gas = exceptional.state_gas_remaining;
settled_child_state_spill = exceptional.state_gas_spilled;
settled_child_status = exceptional.status
}
}
};
var deploy_succeeds : bool = false;
if initcode_succeeded then {
deploy_succeeds = frame_succeeded(settled_child_status)
};
operand_stack_pop_frame();
let parent_memory_base = memory_parent_base(child_memory_base, checkpoint.memory_height);
var parent_gas = refund_gas(checkpoint.gas_remaining, settled_child_gas);
var parent_state_gas : state_gas = checkpoint.state_gas_remaining;
var parent_state_spill : state_gas_spill = checkpoint.state_gas_spilled;
(parent_state_gas, parent_state_spill) = return_child_state_gas(
parent_state_gas,
parent_state_spill,
settled_child_state_gas,
settled_child_state_spill,
);
var parent_refund : gas_refund = checkpoint.refund;
var parent_sp : StackPointer = checkpoint.stack_top;
if deploy_succeeds then {
parent_refund = record_refund(parent_refund, child_refund);
let deployed_bytes : OutputSlice =
if frontier_empty_deposit then EMPTY_OUTPUT_SLICE else output;
let deployed_code = code_db_intern_output(deployed_bytes);
k_deploy_code(continuation.address, deployed_code);
k_journal_commit();
let deployed_address = address_to_word(continuation.address);
parent_sp = stack_top_advance(parent_sp, 1);
write_stack_word(parent_sp, deployed_address)
} else {
k_journal_revert();
if continuation.new_account_charged then {
(parent_gas, parent_state_gas, parent_state_spill) = credit_state_gas_refund(
parent_gas,
parent_state_gas,
parent_state_spill,
G_amsterdam_state_new_account,
)
};
parent_sp = stack_top_advance(parent_sp, 1);
write_stack_word(parent_sp, WORD_ZERO)
};
/* Successful initcode output has become deployed code. REVERT output
* remains visible to RETURNDATA opcodes in the parent. */
let parent_returndata =
if initcode_succeeded then returndata_clear() else output;
struct {
pc = checkpoint.pc,
gas_remaining = parent_gas,
state_gas_remaining = parent_state_gas,
state_gas_spilled = parent_state_spill,
refund = parent_refund,
status = checkpoint.status,
stack_top = parent_sp,
memory_base = parent_memory_base,
memory_height = checkpoint.memory_height,
message = checkpoint.message,
code = checkpoint.code,
calldata = checkpoint.calldata,
returndata = parent_returndata,
}
}Embeds a canonical-order address into the low 160 bits of an EVM word.
function address_to_word(bytes : address) -> word =
unsigned(
bytes[0]
@ bytes[1]
@ bytes[2]
@ bytes[3]
@ bytes[4]
@ bytes[5]
@ bytes[6]
@ bytes[7]
@ bytes[8]
@ bytes[9]
@ bytes[10]
@ bytes[11]
@ bytes[12]
@ bytes[13]
@ bytes[14]
@ bytes[15]
@ bytes[16]
@ bytes[17]
@ bytes[18]
@ bytes[19],
)function charge_state_gas(g, state_gas_remaining, state_gas_spilled, amount) = {
if amount == 0 then {
return (false, g, state_gas_remaining, state_gas_spilled)
};
let state_left = state_gas_remaining;
if amount <= state_left then {
(false, g, state_left - amount, state_gas_spilled)
} else {
let remainder = amount - state_left;
if remainder <= g then {
let spilled = state_gas_spilled;
(false, g - remainder, STATE_GAS_ZERO, state_gas_spill_add(spilled, remainder))
} else {
(true, g, state_gas_remaining, state_gas_spilled)
}
}
}Normalizes frozen creation output into the code arena before deployment.
function code_db_intern_output(bytes : OutputSlice) -> CodeSlice = {
let region = code_region_from_output(bytes);
validated_code_slice(region)
}Returns the affordable execution-gas charge after successful initcode. Legacy forks charge per byte; Amsterdam charges the keccak word cost. The affordability guard bounds the native product without imposing a protocol code-size limit on Frontier or Homestead.
function code_deployment_execution_cost(byte_len : code_length, available : gas) -> GasCharge = {
let execution_profile = k_execution_profile;
let profile = execution_profile.protocol;
if profile.fork >= Amsterdam then {
if byte_len <= profile.deployed_code_size_limit then {
let words = memory_word_count(byte_len);
if words <= available / G_keccak_word then {
let cost : gas_cost = G_keccak_word * words;
gas_charge(cost)
} else {
GAS_CHARGE_UNAFFORDABLE
}
} else {
GAS_CHARGE_UNAFFORDABLE
}
} else if byte_len <= available / G_codedeposit then {
let cost : gas_cost = G_codedeposit * byte_len;
gas_charge(cost)
} else {
GAS_CHARGE_UNAFFORDABLE
}
}Amsterdam state gas charged for each byte of newly deployed code.
function code_deployment_state_cost(byte_len : code_length) -> gas_cost = {
let execution_profile = k_execution_profile;
let profile = execution_profile.protocol;
if profile.fork >= Amsterdam then {
if byte_len <= profile.deployed_code_size_limit then {
G_amsterdam_state_byte * byte_len
} else {
fatal_error(ExecutionInvalid)
}
} else {
GAS_COST_ZERO
}
}function credit_state_gas_refund(g, state_gas_remaining, state_gas_spilled, amount) = {
let spilled = state_gas_spilled;
if amount <= spilled then {
if amount != 0 then {
(conserved_gas_add(g, amount), state_gas_remaining, spilled - amount)
} else {
(g, state_gas_remaining, state_gas_spilled)
}
} else {
let credited =
if spilled != 0 then conserved_gas_add(g, spilled) else g;
let to_state : state_gas_spill = amount - spilled;
(credited, conserved_gas_add(state_gas_remaining, to_state), STATE_GAS_SPILL_ZERO)
}
}function deployed_code_size_allowed(size) = {
let execution_profile = k_execution_profile;
let profile = execution_profile.protocol;
size <= profile.deployed_code_size_limit
}function exceptional_state(state_gas_remaining, state_gas_spilled, state_gas_reservoir, k) = {
let execution_profile = k_execution_profile;
let profile = execution_profile.protocol;
if profile.fork >= Amsterdam then {
struct {
state_gas_remaining = state_gas_reservoir,
state_gas_spilled = STATE_GAS_SPILL_ZERO,
status = Exceptional(k),
}
} else {
struct {
state_gas_remaining = state_gas_remaining,
state_gas_spilled = state_gas_spilled,
status = Exceptional(k),
}
}
}Whether the just-finished frame ended successfully: a normal halt
succeeds; a REVERT and any exceptional halt do not (their world
effects are rolled back and CALL/CREATE reports failure).
function frame_succeeded(frame_status : FrameStatus) -> bool =
match frame_status {
Halted(HaltRevert(_)) => false,
Halted(_) => true,
Running() => true,
Exceptional(_) => false,
}Total gas subtraction. Protocol callers establish affordability first; the saturated arm keeps the primitive representation-safe by construction.
function gas_sub(left : gas, right : gas_cost) -> gas =
if right <= left then {
left - right
} else {
GAS_ZERO
}Deploys code to an account: analyzes, stores, and binds its hash.
function k_deploy_code(a : address, code : CodeSlice) -> unit = {
let execution_profile = k_execution_profile;
let profile = execution_profile.protocol;
let cur = k_aload(a);
let h : hash = code_db_insert(code, profile.fork);
store_account_info(a, cur, { cur.info with code_hash = h })
}Records a successful child frame without discarding its reversible entries.
function k_journal_commit() -> unit = state_journal_commit()Replays the state journal backwards to its innermost open frame boundary.
function k_journal_revert() -> unit = state_journal_revert()Restores the parent arena cursor from the current child cursor and the parent's frame-scoped memory height.
function memory_parent_base(child_base : memory_base, parent_height : memory_height) -> memory_base =
if parent_height <= child_base then {
child_base - parent_height
} else {
fatal_error(ExecutionInvalid)
}val not_bool = pure {coq: "negb", lean: "_lean_not", _: "not"}: forall ('p : Bool). bool('p) -> bool(not('p))Pops the current frame's stack storage, restoring the caller's; the caller's cursor is restored from its frame checkpoint.
val operand_stack_pop_frame = impure { c: "operand_stack_pop_frame" } : unit -> unitfunction record_refund(refund, delta) =
validated_refund_add(refund, delta)function refund_gas(g, amount) =
conserved_gas_add(g, amount)function return_child_state_gas(parent_remaining, parent_spilled, child_remaining, child_spilled) = {
let state_room = (2 ^ 64 - 1) - parent_remaining;
if child_remaining <= state_room then {
(parent_remaining + child_remaining, state_gas_spill_add(parent_spilled, child_spilled))
} else {
fatal_error(ExecutionInvalid)
}
}Clears the returndata buffer (a new sub-call begins).
function returndata_clear() -> OutputSlice = EMPTY_OUTPUT_SLICERETURNDATASIZE.
function returndata_size(returndata : OutputSlice) -> source_pointer = {
let data = returndata;
data.len
}Advances a stack cursor by count slots and refreshes its semantic height.
function stack_top_advance(top : StackPointer, count : stack_slot_count) -> StackPointer = {
let storage = stack_top_advance_host(top.storage, count);
struct { storage = storage, height = stack_top_height_host(storage) }
}Writes the n=0 slot under a validated cursor.
function write_stack_word(sp : StackPointer, value : word) -> unit =
stack_slot_write(sp, 0, value)let EMPTY_OUTPUT_SLICE : OutputSliceFields(0, 0) = output_slice(0, 0)let GAS_ZERO : int(0) = 0let G_amsterdam_state_new_account : state_gas_spill = 183600EIP-2 create rules, EIP-7 DELEGATECALL.
let Homestead : int(homestead_fork_value) = sizeof(homestead_fork_value)EIP-1559 fee market and EIP-3529 refund reduction.
let London : int(london_fork_value) = sizeof(london_fork_value)let WORD_ZERO : word = word_from_bits(0x0000000000000000000000000000000000000000000000000000000000000000)The active protocol policy and all gas limits derived from the executing header, selected together while decoding the stateless input.
register k_execution_profile : ExecutionProfile = DEFAULT_EXECUTION_PROFILEThe suspended parent information needed after initcode returns.
struct CreateContinuation = {
checkpoint : FrameCheckpoint,
address : address,
/* Amsterdam NEW_ACCOUNT state gas paid for the created address. */
new_account_charged : bool,
}Exceptional halts (YP §9.4.2): each consumes all remaining gas and reverts the frame's state changes.
enum ExceptionKind = {
/* an opcode pops more items than the stack holds */
StackUnderflow,
/* a push would exceed the 1024-item stack limit */
StackOverflow,
/* the operation's cost exceeds the remaining gas */
OutOfGas,
/* an unassigned or fork-inactive opcode, or INVALID (0xfe) */
InvalidOpcode,
/* a jump target that is not a valid JUMPDEST */
InvalidJump,
/* EIP-214: state-changing op inside a STATICCALL */
StaticViolation,
/* a call or create beyond depth 1024 */
CallDepthExceeded,
/* a value transfer exceeding the sender's balance */
InsufficientBalance,
/* EIP-214 write protection */
WriteProtection,
/* EIP-3860 */
InitCodeTooLarge,
/* a nonce at its maximum cannot be bumped (EIP-2681) */
NonceOverflow,
/* EIP-684: CREATE into an occupied account */
AddressCollision,
}Per-frame execution status: running, halted normally, or exceptionally halted.
union FrameStatus = {
/* mid-execution */
Running : unit,
/* halted normally (YP §9.4.4) */
Halted : HaltKind,
/* halted exceptionally: all frame gas consumed, effects void */
Exceptional : ExceptionKind
}The complete carried state installed after entering or resuming a frame. Named fields replace the former positional 19-tuple at this cold semantic boundary; the optimized interpreter immediately unpacks the record into its hot scalar locals.
struct FrameTransition = {
pc : code_pointer,
gas_remaining : gas,
state_gas_remaining : state_gas,
state_gas_spilled : state_gas_spill,
refund : gas_refund,
status : FrameStatus,
stack_top : StackPointer,
memory_base : memory_base,
memory_height : memory_height,
message : Message,
code : Code,
calldata : CalldataSlice,
returndata : OutputSlice,
}A frame-output range with its coordinate and length packed existentially.
type OutputSlice = {
'off 'len,
output_region_valid_range('off, 'len).
OutputSliceFields('off, 'len)
}The operand-stack cursor for the active frame, threaded by value through
the interpreter in the state-passing convention and held in the
stack_top frame register at frame boundaries. storage is an opaque
host coordinate while height is the semantic stack height. Keeping the
height in the cursor makes stack validation independent of the host stack
representation. Optimized C refines storage to a native u256 *.
struct StackPointer = {
storage : bits(64),
height : operand_stack_height,
}A 20-byte account address (YP §4.1), in canonical protocol byte order.
type address = vector(20, inc, byte)Available gas in a running EVM frame. Every admitted transaction gas
limit originates in the execution payload's SSZ uint64 gas-limit
domain, and child frames can only receive gas from their parent.
type gas = range(0, 2 ^ 64 - 1)The signed transaction refund accumulator before capping.
type gas_refund = range(
-gas_refund_bound,
gas_refund_bound,
)An absolute byte position in the shared EVM-memory arena.
type memory_base = range(0, memory_region_bound)The active EVM frame's exact relative byte high-water mark. It is carried
beside the frame's absolute memory_base; the host retains no hidden
frame coordinate or lifecycle state.
type memory_height = memory_lengthAmsterdam's per-frame state-gas reservoir. The transaction's total gas
allowance remains in the execution payload's uint64 domain; only the
regular-gas portion and state-gas spill into that portion are capped by
EIP-7825.
type state_gas = range(0, 2 ^ 64 - 1)Execution gas temporarily consumed by Amsterdam state charges. EIP-8037 draws spill only from the regular-gas pool, which is capped by EIP-7825.
type state_gas_spill = range(0, transaction_execution_gas_limit_value)function resume_frame¶
Applies the pending operation for one completed child frame.
function resume_frame(
continuation : FrameContinuation,
output : OutputSlice,
child_memory_base : memory_base,
child_gas : gas,
child_state_gas : state_gas,
child_state_spill : state_gas_spill,
child_refund : gas_refund,
child_status : FrameStatus,
child_state_gas_reservoir : state_gas,
) -> (
FrameTransition
) =
match continuation {
Empty() => fatal_error(ExecutionInvalid),
ResumeCall(call) => resume_call(
call,
output,
child_memory_base,
child_gas,
child_state_gas,
child_state_spill,
child_refund,
child_status,
),
ResumeCreate(create) => resume_create(
create,
output,
child_memory_base,
child_gas,
child_state_gas,
child_state_spill,
child_refund,
child_status,
child_state_gas_reservoir,
),
}function fatal_error(_reason) = exit(())Restores a message-call parent and applies the child's outcome.
function resume_call(
continuation : CallContinuation,
output : OutputSlice,
child_memory_base : memory_base,
child_gas : gas,
child_state_gas : state_gas,
child_state_spill : state_gas_spill,
child_refund : gas_refund,
child_status : FrameStatus,
) -> (
FrameTransition
) = {
let checkpoint = continuation.checkpoint;
let succeeded = frame_succeeded(child_status);
operand_stack_pop_frame();
let parent_memory_base = memory_parent_base(child_memory_base, checkpoint.memory_height);
var parent_gas = refund_gas(checkpoint.gas_remaining, child_gas);
var parent_state_gas : state_gas = checkpoint.state_gas_remaining;
var parent_state_spill : state_gas_spill = checkpoint.state_gas_spilled;
(parent_state_gas, parent_state_spill) = return_child_state_gas(
parent_state_gas,
parent_state_spill,
child_state_gas,
child_state_spill,
);
var parent_refund : gas_refund = checkpoint.refund;
var parent_sp : StackPointer = checkpoint.stack_top;
/* Both RETURN and REVERT copy their output; exceptional halts carry the
* empty slice. Successful effects remain, while every failure reverts to
* the saved kernel checkpoint. */
let return_destination = memory_absolute(parent_memory_base, continuation.return_offset);
returndata_copy_prefix(output, return_destination, continuation.return_length);
if succeeded then {
parent_refund = record_refund(parent_refund, child_refund);
k_journal_commit();
parent_sp = stack_top_advance(parent_sp, 1);
write_stack_word(parent_sp, WORD_ONE)
} else {
k_journal_revert();
if continuation.new_account_charged then {
(parent_gas, parent_state_gas, parent_state_spill) = credit_state_gas_refund(
parent_gas,
parent_state_gas,
parent_state_spill,
G_amsterdam_state_new_account,
)
};
parent_sp = stack_top_advance(parent_sp, 1);
write_stack_word(parent_sp, WORD_ZERO)
};
struct {
pc = checkpoint.pc,
gas_remaining = parent_gas,
state_gas_remaining = parent_state_gas,
state_gas_spilled = parent_state_spill,
refund = parent_refund,
status = checkpoint.status,
stack_top = parent_sp,
memory_base = parent_memory_base,
memory_height = checkpoint.memory_height,
message = checkpoint.message,
code = checkpoint.code,
calldata = checkpoint.calldata,
returndata = output,
}
}Restores a create parent and either deploys or rolls back the child.
function resume_create(
continuation : CreateContinuation,
output : OutputSlice,
child_memory_base : memory_base,
child_gas : gas,
child_state_gas : state_gas,
child_state_spill : state_gas_spill,
child_refund : gas_refund,
child_status : FrameStatus,
child_state_gas_reservoir : state_gas,
) -> (
FrameTransition
) = {
let execution_profile = k_execution_profile;
let profile = execution_profile.protocol;
let checkpoint = continuation.checkpoint;
let initcode_succeeded = frame_succeeded(child_status);
let deployed_length = returndata_size(output);
let deployed_size = deployed_length;
var frontier_empty_deposit : bool = false;
var settled_child_gas : gas = child_gas;
var settled_child_state_gas : state_gas = child_state_gas;
var settled_child_state_spill : state_gas_spill = child_state_spill;
var settled_child_status : FrameStatus = child_status;
if initcode_succeeded then {
let deployed_size_allowed = deployed_code_size_allowed(deployed_size);
let invalid_deployed_size = not_bool(deployed_size_allowed);
let prohibited_prefix =
if deployed_size != 0 then {
let first_byte = slice_byte(output, 0);
first_byte == 0xef
} else {
false
};
if invalid_deployed_size | ((profile.fork >= London) & prohibited_prefix) then {
/* EIP-170 and EIP-3541 failures use the post-Homestead
* exceptional creation path. Neither rule is active on
* Frontier/Homestead in a way that reaches this branch. */
settled_child_gas = GAS_ZERO;
let exceptional = exceptional_state(
settled_child_state_gas,
settled_child_state_spill,
child_state_gas_reservoir,
OutOfGas,
);
settled_child_state_gas = exceptional.state_gas_remaining;
settled_child_state_spill = exceptional.state_gas_spilled;
settled_child_status = exceptional.status
} else {
let deployment_charge = code_deployment_execution_cost(deployed_length, settled_child_gas);
if deployment_charge.affordable then {
let execution_deposit = deployment_charge.cost;
settled_child_gas = gas_sub(settled_child_gas, execution_deposit);
let state_deposit = code_deployment_state_cost(deployed_length);
var deployment_halt : bool = false;
(deployment_halt, settled_child_gas, settled_child_state_gas, settled_child_state_spill) = charge_state_gas(
settled_child_gas,
settled_child_state_gas,
settled_child_state_spill,
state_deposit,
);
if deployment_halt then {
settled_child_gas = GAS_ZERO;
let exceptional = exceptional_state(
settled_child_state_gas,
settled_child_state_spill,
child_state_gas_reservoir,
OutOfGas,
);
settled_child_state_gas = exceptional.state_gas_remaining;
settled_child_state_spill = exceptional.state_gas_spilled;
settled_child_status = exceptional.status
}
} else if profile.fork < Homestead then {
/* Frontier consumed the remaining child gas but kept the
* creation successful with empty deployed code. EIP-2
* changed this to exceptional failure at Homestead. */
settled_child_gas = GAS_ZERO;
frontier_empty_deposit = true
} else {
settled_child_gas = GAS_ZERO;
let exceptional = exceptional_state(
settled_child_state_gas,
settled_child_state_spill,
child_state_gas_reservoir,
OutOfGas,
);
settled_child_state_gas = exceptional.state_gas_remaining;
settled_child_state_spill = exceptional.state_gas_spilled;
settled_child_status = exceptional.status
}
}
};
var deploy_succeeds : bool = false;
if initcode_succeeded then {
deploy_succeeds = frame_succeeded(settled_child_status)
};
operand_stack_pop_frame();
let parent_memory_base = memory_parent_base(child_memory_base, checkpoint.memory_height);
var parent_gas = refund_gas(checkpoint.gas_remaining, settled_child_gas);
var parent_state_gas : state_gas = checkpoint.state_gas_remaining;
var parent_state_spill : state_gas_spill = checkpoint.state_gas_spilled;
(parent_state_gas, parent_state_spill) = return_child_state_gas(
parent_state_gas,
parent_state_spill,
settled_child_state_gas,
settled_child_state_spill,
);
var parent_refund : gas_refund = checkpoint.refund;
var parent_sp : StackPointer = checkpoint.stack_top;
if deploy_succeeds then {
parent_refund = record_refund(parent_refund, child_refund);
let deployed_bytes : OutputSlice =
if frontier_empty_deposit then EMPTY_OUTPUT_SLICE else output;
let deployed_code = code_db_intern_output(deployed_bytes);
k_deploy_code(continuation.address, deployed_code);
k_journal_commit();
let deployed_address = address_to_word(continuation.address);
…Applies the pending operation for one completed child frame.
function resume_frame(
continuation : FrameContinuation,
output : OutputSlice,
child_memory_base : memory_base,
child_gas : gas,
child_state_gas : state_gas,
child_state_spill : state_gas_spill,
child_refund : gas_refund,
child_status : FrameStatus,
child_state_gas_reservoir : state_gas,
) -> (
FrameTransition
) =
match continuation {
Empty() => fatal_error(ExecutionInvalid),
ResumeCall(call) => resume_call(
call,
output,
child_memory_base,
child_gas,
child_state_gas,
child_state_spill,
child_refund,
child_status,
),
ResumeCreate(create) => resume_create(
create,
output,
child_memory_base,
child_gas,
child_state_gas,
child_state_spill,
child_refund,
child_status,
child_state_gas_reservoir,
),
}The reason a block fails validation; one variant per violated block-validity rule.
enum FatalError = {
/* chain config: wrong fork / inactive activation */
InvalidConfig,
/* witness ancestor headers not contiguous */
HeaderChainBroken,
/* a transaction failed to RLP-decode */
RlpDecode,
/* a tx signature did not authenticate its sender */
InvalidSignature,
/* header.gas_limit is outside the consensus domain */
InvalidGasLimit,
/* EIP-7778: a tx exceeds the block's remaining gas */
GasUsedExceedsLimit,
/* a tx exceeds the block's remaining blob gas */
BlobGasLimitExceeded,
/* an invalid tx or a failed block-end system call */
ExecutionInvalid,
/* recomputed cumulative gas != header.gas_used */
InvalidGasUsed,
/* recomputed blob gas != header.blob_gas_used */
InvalidBlobGasUsed,
/* header.excess_blob_gas != expected */
InvalidExcessBlobGas,
/* recomputed post-state root != header.state_root */
InvalidStateRoot,
/* recomputed receipts root != header.receipts_root */
InvalidReceiptsRoot,
/* recomputed logs bloom != header.logs_bloom */
InvalidLogsBloom,
/* recomputed block hash != payload expected hash */
InvalidBlockHash,
/* header.parent_hash != authenticated parent */
InvalidParentHash,
/* EIP-7928: BAL item count > gas_limit / 2000 */
BlockAccessListTooLarge,
/* reconstructed EIP-7928 BAL bytes mismatch */
InvalidBlockAccessList,
/* reconstructed EIP-7685 request bytes mismatch */
InvalidExecutionRequests,
/* a missing/inconsistent proof node (thrown at use) */
WitnessDeficient,
/* an exact protocol integer exceeds its bounded execution representation */
NumericOverflow,
}The pending action performed when a child frame finishes.
union FrameContinuation = {
/*! No suspended parent remains; the completed frame was top-level. */
Empty : unit,
/*! Resume a suspended message-call parent. */
ResumeCall : CallContinuation,
/*! Resume a suspended contract-creation parent. */
ResumeCreate : CreateContinuation
}Per-frame execution status: running, halted normally, or exceptionally halted.
union FrameStatus = {
/* mid-execution */
Running : unit,
/* halted normally (YP §9.4.4) */
Halted : HaltKind,
/* halted exceptionally: all frame gas consumed, effects void */
Exceptional : ExceptionKind
}The complete carried state installed after entering or resuming a frame. Named fields replace the former positional 19-tuple at this cold semantic boundary; the optimized interpreter immediately unpacks the record into its hot scalar locals.
struct FrameTransition = {
pc : code_pointer,
gas_remaining : gas,
state_gas_remaining : state_gas,
state_gas_spilled : state_gas_spill,
refund : gas_refund,
status : FrameStatus,
stack_top : StackPointer,
memory_base : memory_base,
memory_height : memory_height,
message : Message,
code : Code,
calldata : CalldataSlice,
returndata : OutputSlice,
}A frame-output range with its coordinate and length packed existentially.
type OutputSlice = {
'off 'len,
output_region_valid_range('off, 'len).
OutputSliceFields('off, 'len)
}Available gas in a running EVM frame. Every admitted transaction gas
limit originates in the execution payload's SSZ uint64 gas-limit
domain, and child frames can only receive gas from their parent.
type gas = range(0, 2 ^ 64 - 1)The signed transaction refund accumulator before capping.
type gas_refund = range(
-gas_refund_bound,
gas_refund_bound,
)An absolute byte position in the shared EVM-memory arena.
type memory_base = range(0, memory_region_bound)Amsterdam's per-frame state-gas reservoir. The transaction's total gas
allowance remains in the execution payload's uint64 domain; only the
regular-gas portion and state-gas spill into that portion are capped by
EIP-7825.
type state_gas = range(0, 2 ^ 64 - 1)Execution gas temporarily consumed by Amsterdam state charges. EIP-8037 draws spill only from the regular-gas pool, which is capped by EIP-7825.
type state_gas_spill = range(0, transaction_execution_gas_limit_value)function run_frame_entry_encoded¶
Selects the canonical frame-entering operation from its encoded opcode. The optimized threaded dispatcher therefore owns only label routing; CALL and CREATE classification remains part of the executable specification.
function run_frame_entry_encoded(
carried_pc : code_pointer,
carried_gas : gas,
carried_state_gas : state_gas,
carried_state_spill : state_gas_spill,
carried_refund : gas_refund,
carried_sp : StackPointer,
carried_memory_base : memory_base,
carried_memory_height : memory_height,
carried_caller : address,
carried_address : address,
carried_code_address : address,
carried_value : word,
carried_state_gas_reservoir : state_gas,
carried_is_static : bool,
carried_depth : frame_depth,
carried_code : Code,
carried_calldata : CalldataSlice,
carried_returndata : OutputSlice,
opcode : opcode,
) -> (
FrameTransition
) = {
match opcode {
240 => run_create(
carried_pc,
carried_gas,
carried_state_gas,
carried_state_spill,
carried_refund,
carried_sp,
carried_memory_base,
carried_memory_height,
carried_caller,
carried_address,
carried_code_address,
carried_value,
carried_state_gas_reservoir,
carried_is_static,
carried_depth,
carried_code,
carried_calldata,
carried_returndata,
CreateByNonce,
),
241 => run_call(
carried_pc,
carried_gas,
carried_state_gas,
carried_state_spill,
carried_refund,
carried_sp,
carried_memory_base,
carried_memory_height,
carried_caller,
carried_address,
carried_code_address,
carried_value,
carried_state_gas_reservoir,
carried_is_static,
carried_depth,
carried_code,
carried_calldata,
carried_returndata,
Call,
),
242 => run_call(
carried_pc,
carried_gas,
carried_state_gas,
carried_state_spill,
carried_refund,
carried_sp,
carried_memory_base,
carried_memory_height,
carried_caller,
carried_address,
carried_code_address,
carried_value,
carried_state_gas_reservoir,
carried_is_static,
carried_depth,
carried_code,
carried_calldata,
carried_returndata,
CallCode,
),
244 => run_call(
carried_pc,
carried_gas,
carried_state_gas,
carried_state_spill,
carried_refund,
carried_sp,
carried_memory_base,
carried_memory_height,
carried_caller,
carried_address,
carried_code_address,
carried_value,
carried_state_gas_reservoir,
carried_is_static,
carried_depth,
carried_code,
carried_calldata,
carried_returndata,
DelegateCall,
),
245 => run_create(
carried_pc,
carried_gas,
carried_state_gas,
carried_state_spill,
carried_refund,
carried_sp,
carried_memory_base,
carried_memory_height,
carried_caller,
carried_address,
carried_code_address,
carried_value,
carried_state_gas_reservoir,
carried_is_static,
carried_depth,
carried_code,
carried_calldata,
carried_returndata,
CreateBySalt,
),
250 => run_call(
carried_pc,
carried_gas,
carried_state_gas,
carried_state_spill,
carried_refund,
carried_sp,
carried_memory_base,
carried_memory_height,
carried_caller,
carried_address,
carried_code_address,
carried_value,
carried_state_gas_reservoir,
carried_is_static,
carried_depth,
carried_code,
carried_calldata,
carried_returndata,
StaticCall,
),
_ => {
let exceptional = exceptional_state(
carried_state_gas,
carried_state_spill,
carried_state_gas_reservoir,
InvalidOpcode,
);
let state_gas_after = exceptional.state_gas_remaining;
let state_spill_after = exceptional.state_gas_spilled;
let status_after = exceptional.status;
struct {
pc = carried_pc,
gas_remaining = GAS_ZERO,
state_gas_remaining = state_gas_after,
state_gas_spilled = state_spill_after,
refund = carried_refund,
status = status_after,
stack_top = carried_sp,
memory_base = carried_memory_base,
memory_height = carried_memory_height,
message =
struct {
caller = carried_caller,
address = carried_address,
code_address = carried_code_address,
value = carried_value,
state_gas_reservoir = carried_state_gas_reservoir,
is_static = carried_is_static,
depth = carried_depth,
},
code = carried_code,
calldata = carried_calldata,
returndata = carried_returndata,
}
},
}
}function exceptional_state(state_gas_remaining, state_gas_spilled, state_gas_reservoir, k) = {
let execution_profile = k_execution_profile;
let profile = execution_profile.protocol;
if profile.fork >= Amsterdam then {
struct {
state_gas_remaining = state_gas_reservoir,
state_gas_spilled = STATE_GAS_SPILL_ZERO,
status = Exceptional(k),
}
} else {
struct {
state_gas_remaining = state_gas_remaining,
state_gas_spilled = state_gas_spilled,
status = Exceptional(k),
}
}
}Executes a message-call instruction through its non-entering failure paths or installs the child frame and returns its initial carried machine state.
function run_call(
carried_pc : code_pointer,
carried_gas : gas,
carried_state_gas : state_gas,
carried_state_spill : state_gas_spill,
carried_refund : gas_refund,
carried_sp : StackPointer,
carried_memory_base : memory_base,
carried_memory_height : memory_height,
carried_caller : address,
carried_address : address,
carried_code_address : address,
carried_value : word,
carried_state_gas_reservoir : state_gas,
carried_is_static : bool,
carried_depth : frame_depth,
carried_code : Code,
carried_calldata : CalldataSlice,
carried_returndata : OutputSlice,
kind : CallKind,
) -> (
FrameTransition
) = {
let stack_inputs = call_stack_inputs(kind);
let stack_status = guard_stack(carried_sp, stack_inputs, 1);
match stack_status {
Failed(halt_kind) => {
let exceptional = exceptional_state(
carried_state_gas,
carried_state_spill,
carried_state_gas_reservoir,
halt_kind,
);
let state_gas_after = exceptional.state_gas_remaining;
let state_spill_after = exceptional.state_gas_spilled;
let status_after = exceptional.status;
struct {
pc = carried_pc,
gas_remaining = GAS_ZERO,
state_gas_remaining = state_gas_after,
state_gas_spilled = state_spill_after,
refund = carried_refund,
status = status_after,
stack_top = carried_sp,
memory_base = carried_memory_base,
memory_height = carried_memory_height,
message =
struct {
caller = carried_caller,
address = carried_address,
code_address = carried_code_address,
value = carried_value,
state_gas_reservoir = carried_state_gas_reservoir,
is_static = carried_is_static,
depth = carried_depth,
},
code = carried_code,
calldata = carried_calldata,
returndata = carried_returndata,
}
},
Continue() => {
let pc_after : code_pointer = carried_pc;
var gas_after : gas = carried_gas;
var state_gas_after : state_gas = carried_state_gas;
var state_spill_after : state_gas_spill = carried_state_spill;
var status_after : FrameStatus = Running();
var sp_after : StackPointer = carried_sp;
var memory_after : memory_height = carried_memory_height;
var returndata_after : OutputSlice = carried_returndata;
let parent_message : Message = struct {
caller = carried_caller,
address = carried_address,
code_address = carried_code_address,
value = carried_value,
state_gas_reservoir = carried_state_gas_reservoir,
is_static = carried_is_static,
depth = carried_depth,
};
let semantics = call_semantics(kind);
let execution_profile = k_execution_profile;
let profile = execution_profile.protocol;
let current_depth = carried_depth;
let caller = carried_address;
let gas_request = read_stack_word(sp_after);
sp_after = stack_top_retreat(sp_after, 1);
let target_word = read_stack_word(sp_after);
sp_after = stack_top_retreat(sp_after, 1);
let target = word_to_address(target_word);
let (value, next_sp) : (word, StackPointer) =
if semantics.takes_value then {
let value = read_stack_word(sp_after);
(value, stack_top_retreat(sp_after, 1))
} else {
(WORD_ZERO, sp_after)
};
sp_after = next_sp;
let value_nonzero = word_nonzero(value);
let args_off_word = read_stack_word(sp_after);
sp_after = stack_top_retreat(sp_after, 1);
let args_len_word = read_stack_word(sp_after);
sp_after = stack_top_retreat(sp_after, 1);
let ret_off_word = read_stack_word(sp_after);
sp_after = stack_top_retreat(sp_after, 1);
let ret_len_word = read_stack_word(sp_after);
sp_after = stack_top_retreat(sp_after, 1);
/* EIP-214: a value-bearing CALL inside a static context is a write and
raises WriteInStaticContext -- an exceptional halt that consumes all the
caller frame's gas. CALLCODE/DELEGATECALL/STATICCALL never trigger it
(CALLCODE has no static guard in the spec; the others force value = 0). */
if semantics.transfers_value & value_nonzero & carried_is_static then {
gas_after = GAS_ZERO;
let exceptional = exceptional_state(
state_gas_after,
state_spill_after,
carried_state_gas_reservoir,
WriteProtection,
);
state_gas_after = exceptional.state_gas_remaining;
…Executes a contract-creation instruction through its non-entering failure paths or installs the initcode child frame and returns its initial state.
function run_create(
carried_pc : code_pointer,
carried_gas : gas,
carried_state_gas : state_gas,
carried_state_spill : state_gas_spill,
carried_refund : gas_refund,
carried_sp : StackPointer,
carried_memory_base : memory_base,
carried_memory_height : memory_height,
carried_caller : address,
carried_address : address,
carried_code_address : address,
carried_value : word,
carried_state_gas_reservoir : state_gas,
carried_is_static : bool,
carried_depth : frame_depth,
carried_code : Code,
carried_calldata : CalldataSlice,
carried_returndata : OutputSlice,
kind : CreateKind,
) -> (
FrameTransition
) = {
let stack_inputs = create_stack_inputs(kind);
let stack_status = guard_stack(carried_sp, stack_inputs, 1);
match stack_status {
Failed(halt_kind) => {
let exceptional = exceptional_state(
carried_state_gas,
carried_state_spill,
carried_state_gas_reservoir,
halt_kind,
);
let state_gas_after = exceptional.state_gas_remaining;
let state_spill_after = exceptional.state_gas_spilled;
let status_after = exceptional.status;
struct {
pc = carried_pc,
gas_remaining = GAS_ZERO,
state_gas_remaining = state_gas_after,
state_gas_spilled = state_spill_after,
refund = carried_refund,
status = status_after,
stack_top = carried_sp,
memory_base = carried_memory_base,
memory_height = carried_memory_height,
message =
struct {
caller = carried_caller,
address = carried_address,
code_address = carried_code_address,
value = carried_value,
state_gas_reservoir = carried_state_gas_reservoir,
is_static = carried_is_static,
depth = carried_depth,
},
code = carried_code,
calldata = carried_calldata,
returndata = carried_returndata,
}
},
Continue() => {
let pc_after : code_pointer = carried_pc;
var gas_after : gas = carried_gas;
var state_gas_after : state_gas = carried_state_gas;
var state_spill_after : state_gas_spill = carried_state_spill;
var status_after : FrameStatus = Running();
var sp_after : StackPointer = carried_sp;
var memory_after : memory_height = carried_memory_height;
var returndata_after : OutputSlice = carried_returndata;
let parent_message : Message = struct {
caller = carried_caller,
address = carried_address,
code_address = carried_code_address,
value = carried_value,
state_gas_reservoir = carried_state_gas_reservoir,
is_static = carried_is_static,
depth = carried_depth,
};
let semantics = create_semantics(kind);
let execution_profile = k_execution_profile;
let profile = execution_profile.protocol;
let current_depth = carried_depth;
let creator = carried_address;
let value = read_stack_word(sp_after);
sp_after = stack_top_retreat(sp_after, 1);
let off_word = read_stack_word(sp_after);
sp_after = stack_top_retreat(sp_after, 1);
let len_word = read_stack_word(sp_after);
sp_after = stack_top_retreat(sp_after, 1);
let (salt, next_sp) : (word, StackPointer) =
if semantics.uses_salt then {
let salt = read_stack_word(sp_after);
(salt, stack_top_retreat(sp_after, 1))
} else {
(WORD_ZERO, sp_after)
};
sp_after = next_sp;
/* EIP-214: CREATE/CREATE2 modifies state and is forbidden in a static
context -- it raises WriteProtection (an exceptional halt consuming all
remaining gas), checked before any charge or child frame. */
if carried_is_static then {
gas_after = GAS_ZERO;
let exceptional = exceptional_state(
state_gas_after,
state_spill_after,
carried_state_gas_reservoir,
WriteProtection,
);
state_gas_after = exceptional.state_gas_remaining;
state_spill_after = exceptional.state_gas_spilled;
status_after = exceptional.status;
return struct {
pc = pc_after,
gas_remaining = gas_after,
state_gas_remaining = state_gas_after,
state_gas_spilled = state_spill_after,
refund = carried_refund,
status = status_after,
…let GAS_ZERO : int(0) = 0The four CALL-family execution modes. Call is an ordinary call;
CallCode combines the caller's storage with the target's code;
DelegateCall additionally inherits the caller and value; and
StaticCall enters a read-only frame.
enum CallKind = { Call, CallCode, DelegateCall, StaticCall }Calldata is either the immutable top-level transaction input or a frozen range of the suspended caller's memory. The variants state the only two protocol-valid provenances instead of exposing the host's region enum.
union CalldataSlice = {
/* the immutable top-level transaction input */
InputCalldata : StatelessInputSlice,
/* a frozen range of the suspended caller's memory */
MemoryCalldata : EvmMemorySlice,
}Existential executable-code value whose concrete byte address and length
remain correlated inside CodeFields.
type Code = {
'off 'len,
code_region_valid_range('off, 'len) & code_valid_length('len).
CodeFields('off, 'len)
}The two contract-creation address schemes. CreateByNonce is ordinary
CREATE; CreateBySalt is EIP-1014 CREATE2. Keeping this as a closed
semantic tag prevents callers from encoding an execution mode in an
otherwise unexplained boolean.
enum CreateKind = { CreateByNonce, CreateBySalt }Exceptional halts (YP §9.4.2): each consumes all remaining gas and reverts the frame's state changes.
enum ExceptionKind = {
/* an opcode pops more items than the stack holds */
StackUnderflow,
/* a push would exceed the 1024-item stack limit */
StackOverflow,
/* the operation's cost exceeds the remaining gas */
OutOfGas,
/* an unassigned or fork-inactive opcode, or INVALID (0xfe) */
InvalidOpcode,
/* a jump target that is not a valid JUMPDEST */
InvalidJump,
/* EIP-214: state-changing op inside a STATICCALL */
StaticViolation,
/* a call or create beyond depth 1024 */
CallDepthExceeded,
/* a value transfer exceeding the sender's balance */
InsufficientBalance,
/* EIP-214 write protection */
WriteProtection,
/* EIP-3860 */
InitCodeTooLarge,
/* a nonce at its maximum cannot be bumped (EIP-2681) */
NonceOverflow,
/* EIP-684: CREATE into an occupied account */
AddressCollision,
}The complete carried state installed after entering or resuming a frame. Named fields replace the former positional 19-tuple at this cold semantic boundary; the optimized interpreter immediately unpacks the record into its hot scalar locals.
struct FrameTransition = {
pc : code_pointer,
gas_remaining : gas,
state_gas_remaining : state_gas,
state_gas_spilled : state_gas_spill,
refund : gas_refund,
status : FrameStatus,
stack_top : StackPointer,
memory_base : memory_base,
memory_height : memory_height,
message : Message,
code : Code,
calldata : CalldataSlice,
returndata : OutputSlice,
}A frame-output range with its coordinate and length packed existentially.
type OutputSlice = {
'off 'len,
output_region_valid_range('off, 'len).
OutputSliceFields('off, 'len)
}The operand-stack cursor for the active frame, threaded by value through
the interpreter in the state-passing convention and held in the
stack_top frame register at frame boundaries. storage is an opaque
host coordinate while height is the semantic stack height. Keeping the
height in the cursor makes stack validation independent of the host stack
representation. Optimized C refines storage to a native u256 *.
struct StackPointer = {
storage : bits(64),
height : operand_stack_height,
}A 20-byte account address (YP §4.1), in canonical protocol byte order.
type address = vector(20, inc, byte)An absolute byte position in the code arena.
type code_pointer = range(0, code_region_bound)The nesting depth of an execution frame.
type frame_depth = range(0, call_depth_limit)Available gas in a running EVM frame. Every admitted transaction gas
limit originates in the execution payload's SSZ uint64 gas-limit
domain, and child frames can only receive gas from their parent.
type gas = range(0, 2 ^ 64 - 1)The signed transaction refund accumulator before capping.
type gas_refund = range(
-gas_refund_bound,
gas_refund_bound,
)An absolute byte position in the shared EVM-memory arena.
type memory_base = range(0, memory_region_bound)The active EVM frame's exact relative byte high-water mark. It is carried
beside the frame's absolute memory_base; the host retains no hidden
frame coordinate or lifecycle state.
type memory_height = memory_lengthAn EVM instruction byte.
type opcode = range(0, 255)Amsterdam's per-frame state-gas reservoir. The transaction's total gas
allowance remains in the execution payload's uint64 domain; only the
regular-gas portion and state-gas spill into that portion are capped by
EIP-7825.
type state_gas = range(0, 2 ^ 64 - 1)Execution gas temporarily consumed by Amsterdam state charges. EIP-8037 draws spill only from the regular-gas pool, which is capped by EIP-7825.
type state_gas_spill = range(0, transaction_execution_gas_limit_value)The EVM 256-bit machine word (YP §9.1). A transparent range keeps the mathematical subtype relation visible: narrower non-negative ranges can be passed as words without a model-level conversion.
type word = range(0, 2 ^ 256 - 1)