Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
e632e9a
feat(specs): Implement EIP-8282
marioevz Jun 15, 2026
0af20af
feat(test-forks): Implement EIP-8282 framework changes
marioevz Jun 16, 2026
fd1024a
feat(tests): Implement EIP-8282 tests
marioevz Jun 16, 2026
1626b71
feat(tests): Add EIP-8282 to EIP-7685 tests
marioevz Jun 16, 2026
2f4115a
fix(test-forks): Update EIP-8282 deposit contract to 537b9c1
marioevz Jun 16, 2026
efa416d
fix(tests): Update EIP-8282 builder deposit contract target/max to 32…
marioevz Jun 16, 2026
f945ac2
fix(tests): Failing tests after update
marioevz Jun 16, 2026
88c532c
refactor(test-forks): Introduce `minimum_block_gas_limit`
marioevz Jun 16, 2026
328f86b
refactor(test-forks): Update EIP-8282 `empty_block_bal_item_count`
marioevz Jun 16, 2026
f3aa7b7
refactor(test-forks): Use pkgutil to load contract binaries
marioevz Jun 18, 2026
8929767
refactor(test-forks): Use pkgutil to load EIP-8282 contract binaries
marioevz Jun 18, 2026
49baec2
refactor(test-forks): Remove pre_allocation_blockchain type: ignore c…
marioevz Jun 18, 2026
0205991
refactor(test-forks): Remove EIP-8282 pre_allocation_blockchain type:…
marioevz Jun 18, 2026
4b13b2a
Update tests/amsterdam/eip8282_builder_execution_requests/__init__.py
marioevz Jun 18, 2026
3c12670
Update tests/amsterdam/eip8282_builder_execution_requests/spec.py
marioevz Jun 18, 2026
a3dbfd7
chore(tests): Remove `dataclass` from `Spec` class EIP-8282
marioevz Jun 18, 2026
5670288
chore(tests): Make test behavior more explicit
marioevz Jun 18, 2026
0c4dac3
chore(spec,tests): set EIP-8282 builder addresses for glamsterdam-dev…
spencer-tb Jun 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/testing/src/execution_testing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@
Blob,
BlockAccessList,
BlockAccessListExpectation,
BuilderDepositRequest,
BuilderExitRequest,
ChainConfig,
ConsolidationRequest,
DepositRequest,
Expand Down Expand Up @@ -154,6 +156,8 @@
"BlockchainTest",
"BlockchainTestFiller",
"BlockException",
"BuilderDepositRequest",
"BuilderExitRequest",
"Bytecode",
"Bytes",
"BytesConcatenation",
Expand Down
6 changes: 6 additions & 0 deletions packages/testing/src/execution_testing/forks/base_fork.py
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,12 @@ def gas_costs(cls) -> GasCosts:
"""Return dataclass with the gas costs constants for the fork."""
pass

@classmethod
@abstractmethod
def minimum_block_gas_limit(cls) -> int:
"""Return the minimum block gas limit for it to be considered valid."""
pass

@classmethod
@abstractmethod
def opcode_gas_map(
Expand Down
19 changes: 19 additions & 0 deletions packages/testing/src/execution_testing/forks/bytecode.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"""Helper to load predeploy contract bytecode bundled as package data."""

import pkgutil


def load_contract_bytecode(module_name: str, filename: str) -> bytes:
"""
Load predeploy contract bytecode bundled as package data.

`module_name` is the importing module's `__name__`; the bytecode is read
from a `contracts/<filename>` resource located alongside that module.
"""
resource = f"contracts/{filename}"
bytecode = pkgutil.get_data(module_name, resource)
if bytecode is None:
raise FileNotFoundError(
f"Unable to read bytecode `{resource}` from `{module_name}`"
)
return bytecode
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,6 @@ def gas_costs(cls) -> GasCosts:
BLOCK_ACCESS_LIST_ITEM=2000,
)

@classmethod
def empty_block_bal_item_count(cls) -> int:
"""
Return the BAL item count for an empty EIP-7928 block.

Four system contracts produce 15 items:
EIP-4788 beacon roots: 1 address + 1 write + 1 read = 3
EIP-2935 history storage: 1 address + 1 write = 2
EIP-7002 withdrawal requests: 1 address + 4 reads = 5
EIP-7251 consolidation requests: 1 address + 4 reads = 5
"""
return 15

@classmethod
def engine_execution_payload_block_access_list(cls) -> bool:
"""
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
"""
EIP-8282: Builder Execution Requests.

Predeploy builder deposit and exit request contracts for EIP-7732 builders on
the EIP-7685 request bus.

https://eips.ethereum.org/EIPS/eip-8282
"""

from typing import List, Mapping

from execution_testing.base_types import Address

from ....base_fork import BaseFork
from ....bytecode import load_contract_bytecode

BUILDER_DEPOSIT_REQUEST_PREDEPLOY_ADDRESS = (
0x0000884D2AA32EAA155F59A2F24EFA73D9008282
)
BUILDER_DEPOSIT_REQUEST_PREDEPLOY_BYTECODE = load_contract_bytecode(
__name__, "builder_deposit_request.bin"
)

BUILDER_EXIT_REQUEST_PREDEPLOY_ADDRESS = (
0x000014574A74C805590AFF9499FC7A690F008282
)
BUILDER_EXIT_REQUEST_PREDEPLOY_BYTECODE = load_contract_bytecode(
__name__, "builder_exit_request.bin"
)


class EIP8282(BaseFork):
"""EIP-8282 class."""

@classmethod
def max_request_type(cls) -> int:
"""
Two request types are introduced: builder deposit requests (0x03)
and builder exit requests (0x04).
"""
return super(EIP8282, cls).max_request_type() + 2

@classmethod
def empty_block_bal_item_count(cls) -> int:
"""Add block-level access list elements for an empty block."""
# Builder contracts: 2 addresses + 8 reads = 10
return super(EIP8282, cls).empty_block_bal_item_count() + 10

@classmethod
def system_contracts(cls) -> List[Address]:
"""Add the builder deposit and exit request predeploy contracts."""
return [
Address(
BUILDER_DEPOSIT_REQUEST_PREDEPLOY_ADDRESS,
label="BUILDER_DEPOSIT_REQUEST_PREDEPLOY_ADDRESS",
),
Address(
BUILDER_EXIT_REQUEST_PREDEPLOY_ADDRESS,
label="BUILDER_EXIT_REQUEST_PREDEPLOY_ADDRESS",
),
] + super(EIP8282, cls).system_contracts()

@classmethod
def pre_allocation_blockchain(cls) -> Mapping:
"""Pre-allocate the builder deposit and exit request contracts."""
return {
BUILDER_DEPOSIT_REQUEST_PREDEPLOY_ADDRESS: {
"nonce": 1,
"code": BUILDER_DEPOSIT_REQUEST_PREDEPLOY_BYTECODE,
},
BUILDER_EXIT_REQUEST_PREDEPLOY_ADDRESS: {
"nonce": 1,
"code": BUILDER_EXIT_REQUEST_PREDEPLOY_BYTECODE,
},
**super(EIP8282, cls).pre_allocation_blockchain(),
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@
class EIP4788(BaseFork):
"""EIP-4788 class."""

@classmethod
def empty_block_bal_item_count(cls) -> int:
"""Add block-level access list elements for an empty block."""
# Beacon roots contract: 1 address + 1 write + 1 read = 3
return super(EIP4788, cls).empty_block_bal_item_count() + 3

@classmethod
def header_beacon_root_required(cls) -> bool:
"""Parent beacon block root is required."""
Expand All @@ -43,8 +49,9 @@ def pre_allocation_blockchain(cls) -> Mapping:
"57602036146024575f5ffd5b5f35801560495762001fff810690"
"815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd"
"5b62001fff42064281555f359062001fff015500",
}
} | super(EIP4788, cls).pre_allocation_blockchain() # type: ignore
},
**super(EIP4788, cls).pre_allocation_blockchain(),
}

@classmethod
def engine_new_payload_beacon_root(cls) -> bool:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,28 @@
https://eips.ethereum.org/EIPS/eip-2935
"""

from os.path import realpath
from pathlib import Path
from typing import List, Mapping

from execution_testing.base_types import Address

from ....base_fork import BaseFork
from ....bytecode import load_contract_bytecode

BYTECODE_FILE = (
Path(realpath(__file__)).parent / "contracts" / "history_contract.bin"
)
HISTORY_STORAGE_ADDRESS = 0x0000F90827F1C53A10CB7A02335B175320002935
HISTORY_STORAGE_BYTECODE = BYTECODE_FILE.read_bytes()
HISTORY_STORAGE_BYTECODE = load_contract_bytecode(
__name__, "history_contract.bin"
)


class EIP2935(BaseFork):
"""EIP-2935 class."""

@classmethod
def empty_block_bal_item_count(cls) -> int:
"""Add block-level access list elements for an empty block."""
# History contract: 1 address + 1 write = 2
return super(EIP2935, cls).empty_block_bal_item_count() + 2

@classmethod
def system_contracts(cls) -> List[Address]:
"""Add the history storage contract."""
Expand All @@ -42,5 +46,6 @@ def pre_allocation_blockchain(cls) -> Mapping:
HISTORY_STORAGE_ADDRESS: {
"nonce": 1,
"code": HISTORY_STORAGE_BYTECODE,
}
} | super(EIP2935, cls).pre_allocation_blockchain() # type: ignore
},
**super(EIP2935, cls).pre_allocation_blockchain(),
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,17 @@
"""

from hashlib import sha256
from os.path import realpath
from pathlib import Path
from typing import List, Mapping

from execution_testing.base_types import Address

from ....base_fork import BaseFork
from ....bytecode import load_contract_bytecode

BYTECODE_FILE = (
Path(realpath(__file__)).parent / "contracts" / "deposit_contract.bin"
)
DEPOSIT_CONTRACT_ADDRESS = 0x00000000219AB540356CBB839CBE05303D7705FA
DEPOSIT_CONTRACT_BYTECODE = BYTECODE_FILE.read_bytes()
DEPOSIT_CONTRACT_BYTECODE = load_contract_bytecode(
__name__, "deposit_contract.bin"
)


class EIP6110(BaseFork):
Expand Down Expand Up @@ -54,5 +52,6 @@ def pre_allocation_blockchain(cls) -> Mapping:
"nonce": 1,
"code": DEPOSIT_CONTRACT_BYTECODE,
"storage": storage,
}
} | super(EIP6110, cls).pre_allocation_blockchain() # type: ignore
},
**super(EIP6110, cls).pre_allocation_blockchain(),
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,30 @@
https://eips.ethereum.org/EIPS/eip-7002
"""

from os.path import realpath
from pathlib import Path
from typing import List, Mapping

from execution_testing.base_types import Address

from ....base_fork import BaseFork
from ....bytecode import load_contract_bytecode

BYTECODE_FILE = (
Path(realpath(__file__)).parent / "contracts" / "withdrawal_request.bin"
)
WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS = (
0x00000961EF480EB55E80D19AD83579A64C007002
)
WITHDRAWAL_REQUEST_PREDEPLOY_BYTECODE = BYTECODE_FILE.read_bytes()
WITHDRAWAL_REQUEST_PREDEPLOY_BYTECODE = load_contract_bytecode(
__name__, "withdrawal_request.bin"
)


class EIP7002(BaseFork):
"""EIP-7002 class."""

@classmethod
def empty_block_bal_item_count(cls) -> int:
"""Add block-level access list elements for an empty block."""
# Withdrawals contract: 1 address + 4 reads = 5
return super(EIP7002, cls).empty_block_bal_item_count() + 5

@classmethod
def system_contracts(cls) -> List[Address]:
"""Add the withdrawal request predeploy contract."""
Expand All @@ -45,4 +49,5 @@ def pre_allocation_blockchain(cls) -> Mapping:
"nonce": 1,
"code": WITHDRAWAL_REQUEST_PREDEPLOY_BYTECODE,
},
} | super(EIP7002, cls).pre_allocation_blockchain() # type: ignore
**super(EIP7002, cls).pre_allocation_blockchain(),
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,30 @@
https://eips.ethereum.org/EIPS/eip-7251
"""

from os.path import realpath
from pathlib import Path
from typing import List, Mapping

from execution_testing.base_types import Address

from ....base_fork import BaseFork
from ....bytecode import load_contract_bytecode

BYTECODE_FILE = (
Path(realpath(__file__)).parent / "contracts" / "consolidation_request.bin"
)
CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS = (
0x0000BBDDC7CE488642FB579F8B00F3A590007251
)
CONSOLIDATION_REQUEST_PREDEPLOY_BYTECODE = BYTECODE_FILE.read_bytes()
CONSOLIDATION_REQUEST_PREDEPLOY_BYTECODE = load_contract_bytecode(
__name__, "consolidation_request.bin"
)


class EIP7251(BaseFork):
"""EIP-7251 class."""

@classmethod
def empty_block_bal_item_count(cls) -> int:
"""Add block-level access list elements for an empty block."""
# Consolidations contract: 1 address + 4 reads = 5
return super(EIP7251, cls).empty_block_bal_item_count() + 5

@classmethod
def system_contracts(cls) -> List[Address]:
"""Add the consolidation request predeploy contract."""
Expand All @@ -44,4 +48,5 @@ def pre_allocation_blockchain(cls) -> Mapping:
"nonce": 1,
"code": CONSOLIDATION_REQUEST_PREDEPLOY_BYTECODE,
},
} | super(EIP7251, cls).pre_allocation_blockchain() # type: ignore
**super(EIP7251, cls).pre_allocation_blockchain(),
}
16 changes: 16 additions & 0 deletions packages/testing/src/execution_testing/forks/forks/forks.py
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,22 @@ def wrapper(opcode: OpcodeBase) -> int:

return wrapper

@classmethod
def minimum_block_gas_limit(cls) -> int:
"""
Return the minimum gas limit for the block to be considered valid.
"""
minimum_block_gas_limit = 5_000

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: could we change it to GasCosts.LIMIT_MINIMUM

bal_minimum_block_gas_limit = 0
if cls.header_bal_hash_required():
# The block gas limit is influenced by the minimum amount of
# block level access elements the system contracts contain.
bal_minimum_block_gas_limit = (
cls.empty_block_bal_item_count()
* cls.gas_costs().BLOCK_ACCESS_LIST_ITEM
)
return max(minimum_block_gas_limit, bal_minimum_block_gas_limit)

@classmethod
def opcode_gas_map(
cls,
Expand Down
4 changes: 4 additions & 0 deletions packages/testing/src/execution_testing/test_types/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
from .phase_manager import TestPhase, TestPhaseManager
from .receipt_types import TransactionLog, TransactionReceipt
from .request_types import (
BuilderDepositRequest,
BuilderExitRequest,
ConsolidationRequest,
DepositRequest,
Requests,
Expand Down Expand Up @@ -74,6 +76,8 @@
"Blob",
"BlockAccessList",
"BlockAccessListExpectation",
"BuilderDepositRequest",
"BuilderExitRequest",
"ChainConfig",
"ChainConfigDefaults",
"ConsolidationRequest",
Expand Down
Loading
Loading