Steps 4-7 of the transaction pipeline (Yellow Paper §6):
- Charge upfront — debit
gas_limit * max_pricefrom the sender, plus the EIP-4844 blob gas fee, which is burned and never refunded. Thevalueportion is not debited here; it is moved byExecutionwhen the call frame is set up so the same wei is not deducted twice. - Intrinsic gas — verify
tx.gas_limit >= IntrinsicGas.calculate(tx, hardfork)and compute the gas available to the EVM as the difference. - Bump nonce — increment the sender's nonce.
- Apply authorizations (EIP-7702) — write delegation designators for each valid authorization tuple. These writes happen before execution and persist even if the call reverts.
- Pre-warm access set — seed
accessed_addressesandaccessed_storage_keyswith EIP-2929 / EIP-3651 entries plus the transaction's access list.
Also exposes gas_upfront/1 and effective_gas_price/2 because both
PostExecution settlement and the upfront balance check share the same
fee-market formula, and blob_gas_fee/2 for runners that settle fees
themselves.
Summary
Types
Result of applying an EIP-7702 authorization list: every authority whose signature recovered (warm for the rest of the transaction) and the gas refund owed for authorities that already existed.
Functions
Apply the EIP-7702 authorization list of tx to db, after the sender's
nonce bump and before execution.
EIP-4844 blob gas fee: GAS_PER_BLOB * blob count * blob_base_fee. Zero for
transactions without blobs.
Debit the maximum gas fee from the sender after checking that the balance
also covers tx.value and that the effective gas price meets the basefee.
Price per gas the sender pays: min(max_fee, basefee + priority_fee) for
EIP-1559-style transactions (EIP-1559), or the legacy gas price.
Maximum gas fee debited upfront: gas_limit times the fee cap (or gas price).
Addresses warm at transaction start (EIP-2929, EIP-2930, EIP-3651): the callee, caller, origin, coinbase, precompiles, and access-list entries.
Storage keys warm at transaction start: every access-list slot (EIP-2930).
Verify intrinsic gas, bump the sender nonce, and return the gas available to the EVM.
Types
@type authorization_outcome() :: %{ authorities: MapSet.t(non_neg_integer()), refund: non_neg_integer() }
Result of applying an EIP-7702 authorization list: every authority whose signature recovered (warm for the rest of the transaction) and the gas refund owed for authorities that already existed.
Functions
@spec apply_authorizations( EEVM.Database.t(), EEVM.Context.Transaction.t(), non_neg_integer() ) :: {EEVM.Database.t(), authorization_outcome()}
Apply the EIP-7702 authorization list of tx to db, after the sender's
nonce bump and before execution.
Each tuple is skipped, without failing the transaction, when its chain id
is neither 0 nor chain_id, its nonce is 2^64 - 1, its signature does not
recover, the authority has code other than a designator, or the
authority's nonce differs. Otherwise the authority's code becomes a
designator for address (or is cleared when address is zero) and its
nonce is incremented. Tuples apply in order, so a later tuple from the
same authority sees the earlier one's nonce bump.
@spec blob_gas_fee(EEVM.Context.Transaction.t(), EEVM.Context.Block.t()) :: non_neg_integer()
EIP-4844 blob gas fee: GAS_PER_BLOB * blob count * blob_base_fee. Zero for
transactions without blobs.
Example
iex> tx = EEVM.Context.Transaction.new(type: :eip4844, blob_hashes: [1, 2])
iex> EEVM.Handler.PreExecution.blob_gas_fee(tx, EEVM.Context.Block.new(blob_base_fee: 3))
786_432
@spec charge_upfront( EEVM.Database.t(), EEVM.Context.Transaction.t(), EEVM.Context.Block.t() ) :: {:ok, EEVM.Database.t()} | {:error, atom()}
Debit the maximum gas fee from the sender after checking that the balance
also covers tx.value and that the effective gas price meets the basefee.
@spec effective_gas_price(EEVM.Context.Transaction.t(), EEVM.Context.Block.t()) :: non_neg_integer()
Price per gas the sender pays: min(max_fee, basefee + priority_fee) for
EIP-1559-style transactions (EIP-1559), or the legacy gas price.
@spec gas_upfront(EEVM.Context.Transaction.t()) :: non_neg_integer()
Maximum gas fee debited upfront: gas_limit times the fee cap (or gas price).
@spec initial_accessed_addresses( EEVM.Context.Transaction.t(), EEVM.Config.t(), EEVM.Context.Contract.t(), EEVM.Context.Block.t() ) :: MapSet.t(non_neg_integer())
Addresses warm at transaction start (EIP-2929, EIP-2930, EIP-3651): the callee, caller, origin, coinbase, precompiles, and access-list entries.
@spec initial_accessed_storage_keys(EEVM.Context.Transaction.t()) :: MapSet.t({non_neg_integer(), non_neg_integer()})
Storage keys warm at transaction start: every access-list slot (EIP-2930).
@spec prepare_execution( EEVM.Database.t(), EEVM.Context.Transaction.t(), EEVM.HardforkConfig.t() ) :: {:ok, EEVM.Database.t(), non_neg_integer()} | {:error, :intrinsic_gas_too_low}
Verify intrinsic gas, bump the sender nonce, and return the gas available to the EVM.