From 22d34cc75a6aa432feaed149707afba2c7481ff3 Mon Sep 17 00:00:00 2001 From: pdobacz <5735525+pdobacz@users.noreply.github.com> Date: Tue, 20 Oct 2026 12:24:59 +0000 Subject: [PATCH 1/2] fix(tests): exclude the EIP-7981 value shortfall case on Monad Monad includes a transaction whose sender covers the gas fees but not the value. Co-Authored-By: Claude --- .../test_transaction_validity.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/amsterdam/eip7981_increase_access_list_cost/test_transaction_validity.py b/tests/amsterdam/eip7981_increase_access_list_cost/test_transaction_validity.py index f548c021c3a..bad4522dd02 100644 --- a/tests/amsterdam/eip7981_increase_access_list_cost/test_transaction_validity.py +++ b/tests/amsterdam/eip7981_increase_access_list_cost/test_transaction_validity.py @@ -405,6 +405,13 @@ def test_contract_creation_with_access_list( id="value_non_zero_insufficient_balance", marks=[ pytest.mark.exception_test, + # Monad includes a transaction once the sender's balance + # covers its gas fees; the value it cannot cover fails the + # top-level frame instead, which the reserve balance tests + # cover. + pytest.mark.not_valid_for( + "MONAD_EIGHT", subsequent_forks=True + ), EIPChecklist.TransactionType.Test.IntrinsicValidity.ValueNonZeroInsufficientBalance(), ], ), From ff584dbeb05c3fa9159211a099f55d1e1ff38a18 Mon Sep 17 00:00:00 2001 From: pdobacz <5735525+pdobacz@users.noreply.github.com> Date: Tue, 20 Oct 2026 13:31:32 +0000 Subject: [PATCH 2/2] test(monad_eleven): EIP-7981 surcharged fee against the inclusion rule The fee-pricing helper moves to the reserve balance helpers so both suites share it. Co-Authored-By: Claude --- tests/monad_eight/reserve_balance/helpers.py | 26 +++- .../test_gas_fees_vs_reserve.py | 24 +--- .../eip7981_fee_solvency/__init__.py | 1 + .../test_surcharged_fee.py | 119 ++++++++++++++++++ 4 files changed, 145 insertions(+), 25 deletions(-) create mode 100644 tests/monad_eleven/eip7981_fee_solvency/__init__.py create mode 100644 tests/monad_eleven/eip7981_fee_solvency/test_surcharged_fee.py diff --git a/tests/monad_eight/reserve_balance/helpers.py b/tests/monad_eight/reserve_balance/helpers.py index 40a3a77f874..00a75c720f0 100644 --- a/tests/monad_eight/reserve_balance/helpers.py +++ b/tests/monad_eight/reserve_balance/helpers.py @@ -3,10 +3,11 @@ """ from enum import Enum, auto, unique -from typing import List +from typing import Any, List -from execution_testing import EOA, Address, AuthorizationTuple, Op +from execution_testing import EOA, Address, AuthorizationTuple, Op, Transaction from execution_testing.forks.helpers import Fork +from execution_testing.test_types.block_types import DEFAULT_BASE_FEE from tests.prague.eip7702_set_code_tx.spec import Spec as Spec7702 @@ -32,6 +33,27 @@ def generous_gas(fork: Fork) -> int: ) +def priced_transaction( + tx_type: int, gas_price: int, **fields: Any +) -> Transaction: + """ + Build a transaction of `tx_type` charged at `gas_price`. + + A type-2 transaction caps its fee at twice that price, with the tip + chosen so that the effective price stays at `gas_price`. + """ + if tx_type in (0, 1): + return Transaction(ty=tx_type, gas_price=gas_price, **fields) + if tx_type == 2: + return Transaction( + ty=2, + max_fee_per_gas=2 * gas_price, + max_priority_fee_per_gas=gas_price - DEFAULT_BASE_FEE, + **fields, + ) + raise ValueError(f"unsupported transaction type {tx_type}") + + @unique class Stage1Balance(Enum): """Initial balance states for Stage 1.""" diff --git a/tests/monad_eight/reserve_balance/test_gas_fees_vs_reserve.py b/tests/monad_eight/reserve_balance/test_gas_fees_vs_reserve.py index 545cb179ff2..da2753a6b3b 100644 --- a/tests/monad_eight/reserve_balance/test_gas_fees_vs_reserve.py +++ b/tests/monad_eight/reserve_balance/test_gas_fees_vs_reserve.py @@ -4,7 +4,6 @@ """ from enum import Enum, auto, unique -from typing import Any import pytest from execution_testing import ( @@ -15,12 +14,12 @@ BlockchainTestFiller, Header, Op, - Transaction, TransactionException, ) from execution_testing.forks.helpers import Fork from execution_testing.test_types.block_types import DEFAULT_BASE_FEE +from .helpers import priced_transaction from .spec import Spec, ref_spec_7702 REFERENCE_SPEC_GIT_PATH = ref_spec_7702.git_path @@ -42,27 +41,6 @@ ] -def priced_transaction( - tx_type: int, gas_price: int, **fields: Any -) -> Transaction: - """ - Build a transaction of `tx_type` charged at `gas_price`. - - A type-2 transaction caps its fee at twice that price, with the tip - chosen so that the effective price stays at `gas_price`. - """ - if tx_type == 0: - return Transaction(ty=0, gas_price=gas_price, **fields) - if tx_type == 2: - return Transaction( - ty=2, - max_fee_per_gas=2 * gas_price, - max_priority_fee_per_gas=gas_price - DEFAULT_BASE_FEE, - **fields, - ) - raise ValueError(f"unsupported transaction type {tx_type}") - - @unique class GasFees(Enum): """Gas fees of a max-gas transaction, relative to the reserve.""" diff --git a/tests/monad_eleven/eip7981_fee_solvency/__init__.py b/tests/monad_eleven/eip7981_fee_solvency/__init__.py new file mode 100644 index 00000000000..48f4e2820c3 --- /dev/null +++ b/tests/monad_eleven/eip7981_fee_solvency/__init__.py @@ -0,0 +1 @@ +"""Tests for the EIP-7981 surcharge against Monad's sender solvency rule.""" diff --git a/tests/monad_eleven/eip7981_fee_solvency/test_surcharged_fee.py b/tests/monad_eleven/eip7981_fee_solvency/test_surcharged_fee.py new file mode 100644 index 00000000000..7f3c00d1d7d --- /dev/null +++ b/tests/monad_eleven/eip7981_fee_solvency/test_surcharged_fee.py @@ -0,0 +1,119 @@ +""" +Tests for the EIP-7981 access list surcharge against the sender's balance. + +Monad includes a transaction once the sender covers its gas fees at the +effective gas price and bills the whole gas limit. MONAD_TEN fills the +same boundaries without the surcharge, for comparison. +""" + +import pytest +from execution_testing import ( + AccessList, + Account, + Address, + Alloc, + EIPChecklist, + Fork, + Hash, + RecipientType, + StateTestFiller, + TransactionException, + TransactionReceipt, +) + +from ...amsterdam.eip7981_increase_access_list_cost.helpers import ( + calculate_access_list_data_cost, +) +from ...amsterdam.eip7981_increase_access_list_cost.spec import ref_spec_7981 +from ...monad_eight.reserve_balance.helpers import priced_transaction + +REFERENCE_SPEC_GIT_PATH = ref_spec_7981.git_path +REFERENCE_SPEC_VERSION = ref_spec_7981.version + +GAS_PRICE = 10 +ACCESS_LIST = [AccessList(address=Address(1), storage_keys=[Hash(0)])] + +pytestmark = pytest.mark.valid_from("MONAD_TEN") + + +@pytest.mark.parametrize( + "value,balance_delta", + [ + pytest.param( + 1, + -1, + id="fee_short_by_one", + marks=[ + pytest.mark.exception_test, + EIPChecklist.TransactionType.Test.IntrinsicValidity.ValueNonZeroInsufficientBalance(), + ], + ), + pytest.param( + 1, + 0, + id="fee_exact", + marks=EIPChecklist.TransactionType.Test.IntrinsicValidity.ValueNonZeroInsufficientBalance(), + ), + pytest.param(2, 1, id="value_short_by_one"), + ], +) +@pytest.mark.parametrize( + "tx_type", + [pytest.param(1, id="type_1"), pytest.param(2, id="type_2")], +) +def test_surcharged_fee_inclusion( + state_test: StateTestFiller, + pre: Alloc, + fork: Fork, + tx_type: int, + value: int, + balance_delta: int, +) -> None: + """ + Fund the surcharged fee one wei short, exactly, or with part of the + value on top; the value stays out of the inclusion check. + """ + recipient = pre.fund_eoa(amount=0) + intrinsic_cost = fork.transaction_intrinsic_cost_calculator() + gas_limit = intrinsic_cost( + access_list=ACCESS_LIST, + sends_value=True, + recipient_type=RecipientType.EMPTY_ACCOUNT, + ) + # Pin where the fee boundary sits on either side of EIP-7981. + costs = fork.gas_costs() + surcharge = ( + calculate_access_list_data_cost(ACCESS_LIST, fork) + if fork.is_eip_enabled(7981) + else 0 + ) + assert gas_limit == ( + intrinsic_cost( + sends_value=True, recipient_type=RecipientType.EMPTY_ACCOUNT + ) + + costs.TX_ACCESS_LIST_ADDRESS + + costs.TX_ACCESS_LIST_STORAGE_KEY + + surcharge + ) + sender = pre.fund_eoa(amount=gas_limit * GAS_PRICE + balance_delta) + sender_nonce = sender.nonce + rejected = balance_delta < 0 + tx = priced_transaction( + tx_type, + GAS_PRICE, + sender=sender, + to=recipient, + value=value, + gas_limit=gas_limit, + access_list=ACCESS_LIST, + error=TransactionException.INSUFFICIENT_ACCOUNT_FUNDS + if rejected + else None, + expected_receipt=None + if rejected + else TransactionReceipt(status=0, gas_used=gas_limit), + ) + post: dict[Address, Account | None] = {recipient: Account.NONEXISTENT} + if not rejected: + post[sender] = Account(nonce=sender_nonce + 1, balance=balance_delta) + state_test(pre=pre, post=post, tx=tx)