Snapshot/commit semantics for nested EVM call frames.
When a child frame halts normally (:stopped), its mutations to the
parent's revertible fields are promoted; on any other halt they are
discarded and the parent observes the pre-call state.
Revertible fields (the db plus all of substate) participate in the
merge:
db— the unified world database (accounts + storage)substate.logs— emitted log entries (parent's first, then child's)substate.accessed_addresses— EIP-2929 access listsubstate.accessed_storage_keys— EIP-2929 access listsubstate.created_addresses— EIP-6780 set of contracts created in this txsubstate.touched_addresses— EIP-161 cleanup setframe.refund— the gas refund counter. A child starts at 0, so on success its (possibly negative) counter is added to the parent's; on failure it is dropped.
The tracer field is not revertible — trace events accumulate
monotonically and are always preserved across child boundaries regardless
of the child's outcome.
What this module does NOT cover
- Stack and memory: those are local to a frame and never propagate child → parent on success or failure.
- Gas accounting: returning the child's unused
child.gasto the parent happens at the call site, not here, because the math depends on opcode-specific bookkeeping (e.g. CREATE's deposit cost). - The
:dboverride in CREATE: the success path replaceschild.dbwith a post-deploy db that has the runtime code installed. Call sites apply that override aftermerge_child_result/2.
Summary
Functions
Merge the result of a child frame back into its parent.
Functions
@spec merge_child_result( EEVM.Interpreter.MachineState.t(), EEVM.Interpreter.MachineState.t() ) :: EEVM.Interpreter.MachineState.t()
Merge the result of a child frame back into its parent.
- On success (
status: :stopped): the child's mutations to revertible fields are promoted into the parent. Logs are appended, with the parent's existing logs preceding the child's in emission order, and the child's refund counter is added to the parent's. - On any other halt: the parent's revertible fields are kept untouched.
In both cases the child's tracer state is propagated into the parent, because tracing accumulates regardless of execution outcome.