Skip to main content

Execution Context

REMINDER

Many terms and definitions here are borrowed from the Ethereum Yellow Paper.

An execution context contains the information and state relevant to a contract call's execution. When a contract call is made, an execution context is initialized before the contract code's execution begins.

AvmContext

FieldType
environmentExecutionEnvironment
machineStateMachineState
worldStateAvmWorldState
worldStateAccessTraceWorldStateAccessTrace
accruedSubstateAccruedSubstate
resultsContractCallResults

Execution Environment

A context's execution environment remains constant throughout a contract call's execution. When a contract call initializes its execution context, it fully specifies the execution environment.

ExecutionEnvironment

FieldTypeDescription
addressAztecAddress
storageAddressAztecAddress
originAztecAddress
senderAztecAddress
portalEthAddress
feePerL1Gasfield
feePerL2Gasfield
feePerDaGasfield
contractCallDepthfieldDepth of the current call (how many nested calls deep is it).
contractCallPointerfieldUniquely identifies each contract call processed by an AVM session. An initial call is assigned pointer value of 1 (expanded on in the AVM circuit section's "Call Pointer" subsection).
globalsPublicGlobalVariables
isStaticCallboolean
isDelegateCallboolean
calldata[field; <calldata-length>]

Contract Call Results

When a contract call halts, it sets the context's contract call results to communicate results to the caller.

ContractCallResults

FieldTypeDescription
revertedboolean
output[field; <output-length>]

Context initialization

Initial contract calls

An initial contract call initializes a new execution context from a public execution request.

context = AvmContext {
environment = INITIAL_EXECUTION_ENVIRONMENT,
machineState = INITIAL_MACHINE_STATE,
worldState = <latest world state>,
worldStateAccessTrace = INITIAL_WORLD_STATE_ACCESS_TRACE,
accruedSubstate = { [], ... [], }, // all substate vectors empty
results = INITIAL_CONTRACT_CALL_RESULTS,
}

Since world state persists between transactions, the latest state is injected into a new AVM context.

Given a PublicCallRequest and its parent TxRequest, these above-listed "INITIAL_*" entries are defined as follows:

INITIAL_EXECUTION_ENVIRONMENT = ExecutionEnvironment {
address: PublicCallRequest.contractAddress,
storageAddress: PublicCallRequest.CallContext.storageContractAddress,
origin: TxRequest.origin,
sender: PublicCallRequest.CallContext.msgSender,
portal: PublicCallRequest.CallContext.portalContractAddress,
feePerL1Gas: TxRequest.feePerL1Gas,
feePerL2Gas: TxRequest.feePerL2Gas,
feePerDaGas: TxRequest.feePerDaGas,
contractCallDepth: 0,
contractCallPointer: 1,
globals: <current block's global variables>
isStaticCall: PublicCallRequest.CallContext.isStaticCall,
isDelegateCall: PublicCallRequest.CallContext.isDelegateCall,
calldata: PublicCallRequest.args,
}

INITIAL_MACHINE_STATE = MachineState {
l1GasLeft: TxRequest.l1GasLimit,
l2GasLeft: TxRequest.l2GasLimit,
daGasLeft: TxRequest.daGasLimit,
pc: 0,
internalCallStack: [], // initialized as empty
memory: [0, ..., 0], // all 2^32 entries are initialized to zero
}

INITIAL_WORLD_STATE_ACCESS_TRACE = WorldStateAccessTrace {
accessCounter: 1,
contractCalls: [ // initial contract call is traced
TracedContractCall {
callPointer: nestedContext.environment.callPointer,
address: nestedContext.address,
storageAddress: nestedContext.storageAddress,
counter: 0,
endLifetime: 0, // The call's end-lifetime will be updated later if it or its caller reverts
}
],
[], ... [], // remaining entries are empty
},

INITIAL_CONTRACT_CALL_RESULTS = ContractCallResults {
reverted = false,
output = [], // initialized as empty
}

Nested contract calls

See the dedicated "Nested Contract Calls" page for a detailed explanation of nested contract calls.

The nested call's execution context is derived from the caller's context and the call instruction's arguments.

The following shorthand syntax is used to refer to nested context derivation in the "Instruction Set" and other sections:

// instr.args are { gasOffset, addrOffset, argsOffset, retOffset, retSize }

isStaticCall = instr.opcode == STATICCALL
isDelegateCall = instr.opcode == DELEGATECALL

nestedContext = deriveContext(context, instr.args, isStaticCall, isDelegateCall)

Nested context derivation is defined as follows:

nestedExecutionEnvironment = ExecutionEnvironment {
origin: context.origin,
sender: isDelegateCall ? context.sender : context.address,
address: M[addrOffset],
storageAddress: isDelegateCall ? context.storageAddress : M[addrOffset],
portal: callingContext.worldState.contracts[M[addrOffset]].portal,
feePerL1Gas: context.environment.feePerL1Gas,
feePerL2Gas: context.environment.feePerL2Gas,
feePerDaGas: context.environment.feePerDaGas,
contractCallDepth: context.contractCallDepth + 1,
contractCallPointer: context.worldStateAccessTrace.contractCalls.length + 1,
globals: context.globals,
isStaticCall: isStaticCall,
isDelegateCall: isDelegateCall,
calldata: context.memory[M[argsOffset]:M[argsOffset]+argsSize],
}

nestedMachineState = MachineState {
l1GasLeft: context.machineState.memory[M[gasOffset]],
l2GasLeft: context.machineState.memory[M[gasOffset+1]],
daGasLeft: context.machineState.memory[M[gasOffset+2]],
pc = 0,
internalCallStack = [], // initialized as empty
memory = [0, ..., 0], // all 2^32 entries are initialized to zero
}
nestedContext = AvmContext {
environment: nestedExecutionEnvironment,
machineState: nestedMachineState,
worldState: context.worldState,
worldStateAccessTrace: context.worldStateAccessTrace,
accruedSubstate: { [], ... [], }, // all empty
results: {reverted: false, output: []},
}

M[offset] notation is shorthand for context.machineState.memory[offset]