EEVM.Handler.PreExecution (eevm v0.1.0)

Copy Markdown View Source

Steps 4-7 of the transaction pipeline (Yellow Paper §6):

  • Charge upfront — debit gas_limit * max_price from the sender, plus the EIP-4844 blob gas fee, which is burned and never refunded. The value portion is not debited here; it is moved by Execution when 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_addresses and accessed_storage_keys with 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

authorization_outcome()

@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

apply_authorizations(db, tx, chain_id)

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.

blob_gas_fee(transaction, block)

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

charge_upfront(db, tx, block)

@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.

effective_gas_price(tx, block)

@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.

gas_upfront(tx)

@spec gas_upfront(EEVM.Context.Transaction.t()) :: non_neg_integer()

Maximum gas fee debited upfront: gas_limit times the fee cap (or gas price).

initial_accessed_addresses(tx, config, contract_ctx, block)

Addresses warm at transaction start (EIP-2929, EIP-2930, EIP-3651): the callee, caller, origin, coinbase, precompiles, and access-list entries.

initial_accessed_storage_keys(tx)

@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).

prepare_execution(db, tx, hardfork)

@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.