EEVM.Handler.PreExecution (eevm v0.1.0)

Copy Markdown View Source

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

  • Charge upfront — check that the sender can afford the maximum fee, then debit gas_limit * effective_price 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 effective_gas_price/2, which PostExecution settlement shares, 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 gas_limit * effective_gas_price and the blob gas fee from the sender, after checking that the effective gas price meets the basefee and that the balance covers the maximum fee plus tx.value.

Price per gas the sender pays: min(max_fee, basefee + priority_fee) for EIP-1559-style transactions (EIP-1559), or the legacy gas price.

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

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)

@spec apply_authorizations(EEVM.Database.t(), EEVM.TxEnv.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.

blob_gas_fee(tx_env, block_env)

@spec blob_gas_fee(EEVM.TxEnv.t(), EEVM.BlockEnv.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.TxEnv.new(type: :eip4844, blob_hashes: [1, 2])
iex> EEVM.Handler.PreExecution.blob_gas_fee(tx, EEVM.BlockEnv.new(blob_base_fee: 3))
786_432

charge_upfront(db, tx, block, config)

@spec charge_upfront(
  EEVM.Database.t(),
  EEVM.TxEnv.t(),
  EEVM.BlockEnv.t(),
  EEVM.Config.t()
) ::
  {:ok, EEVM.Database.t()} | {:error, atom()}

Debit gas_limit * effective_gas_price and the blob gas fee from the sender, after checking that the effective gas price meets the basefee and that the balance covers the maximum fee plus tx.value.

The maximum fee prices gas at max_fee_per_gas (EIP-1559) and blob gas at max_fee_per_blob_gas (EIP-4844), but only the effective fee is debited, so BALANCE of the sender during execution already excludes the unspent fee cap (execution-specs process_transaction).

config's disable_balance_check and disable_base_fee simulation switches skip the matching check. With the balance check off, the sender keeps at least tx.value and pays the gas charge only as far as its balance allows, as revm's CfgEnv does.

effective_gas_price(tx, block)

@spec effective_gas_price(EEVM.TxEnv.t(), EEVM.BlockEnv.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.

initial_accessed_addresses(tx, config, contract_ctx, block)

@spec initial_accessed_addresses(
  EEVM.TxEnv.t(),
  EEVM.Config.t(),
  EEVM.Context.Contract.t(),
  EEVM.BlockEnv.t()
) :: MapSet.t(non_neg_integer())

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

initial_accessed_storage_keys(tx)

@spec initial_accessed_storage_keys(EEVM.TxEnv.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.TxEnv.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.