3030
3131from . import vm
3232from .block_access_lists .builder import build_block_access_list
33- from .block_access_lists .rlp_types import BlockAccessIndex
3433from .block_access_lists .rlp_utils import compute_block_access_list_hash
3534from .blocks import Block , Header , Log , Receipt , Withdrawal , encode_receipt
3635from .bloom import logs_bloom
6564 state_root ,
6665)
6766from .state_tracker import (
67+ StateChanges ,
6868 capture_pre_balance ,
6969 commit_transaction_frame ,
7070 create_child_frame ,
71- get_block_access_index ,
72- handle_in_transaction_selfdestruct ,
71+ filter_net_zero_frame_changes ,
7372 increment_block_access_index ,
74- merge_on_success ,
75- normalize_balance_changes_for_transaction ,
7673 track_address ,
7774 track_balance_change ,
75+ track_nonce_change ,
76+ track_selfdestruct ,
7877)
7978from .transactions import (
8079 AccessListTransaction ,
@@ -247,6 +246,7 @@ def state_transition(chain: BlockChain, block: Block) -> None:
247246 prev_randao = block .header .prev_randao ,
248247 excess_blob_gas = block .header .excess_blob_gas ,
249248 parent_beacon_block_root = block .header .parent_beacon_block_root ,
249+ state_changes = StateChanges (),
250250 )
251251
252252 block_output = apply_body (
@@ -634,7 +634,7 @@ def process_system_transaction(
634634 """
635635 # EIP-7928: Create a child frame for system transaction
636636 # This allows proper pre-state capture for net-zero filtering
637- system_tx_state_changes = create_child_frame (block_env .block_state_changes )
637+ system_tx_state_changes = create_child_frame (block_env .state_changes )
638638
639639 tx_env = vm .TransactionEnvironment (
640640 origin = SYSTEM_ADDRESS ,
@@ -647,8 +647,12 @@ def process_system_transaction(
647647 authorizations = (),
648648 index_in_block = None ,
649649 tx_hash = None ,
650+ state_changes = system_tx_state_changes ,
650651 )
651652
653+ # Create call frame as child of tx frame
654+ call_frame = create_child_frame (tx_env .state_changes )
655+
652656 system_tx_message = Message (
653657 block_env = block_env ,
654658 tx_env = tx_env ,
@@ -667,14 +671,15 @@ def process_system_transaction(
667671 accessed_storage_keys = set (),
668672 disable_precompiles = False ,
669673 parent_evm = None ,
670- transaction_state_changes = system_tx_state_changes ,
674+ is_create = False ,
675+ state_changes = call_frame ,
671676 )
672677
673678 system_tx_output = process_message_call (system_tx_message )
674679
675- # Merge system transaction changes back to block frame
680+ # Commit system transaction changes to block frame
676681 # System transactions always succeed (or block is invalid)
677- merge_on_success ( system_tx_state_changes )
682+ commit_transaction_frame ( tx_env . state_changes )
678683
679684 return system_tx_output
680685
@@ -814,17 +819,17 @@ def apply_body(
814819 # EIP-7928: Increment block frame to post-execution index
815820 # After N transactions, block frame is at index N
816821 # Post-execution operations (withdrawals, etc.) use index N+1
817- increment_block_access_index (block_env .block_state_changes )
822+ increment_block_access_index (block_env .state_changes )
818823
819824 process_withdrawals (block_env , block_output , withdrawals )
820825
821826 process_general_purpose_requests (
822827 block_env = block_env ,
823828 block_output = block_output ,
824829 )
825- # Build block access list from block_env.block_state_changes
830+ # Build block access list from block_env.state_changes
826831 block_output .block_access_list = build_block_access_list (
827- block_env .block_state_changes
832+ block_env .state_changes
828833 )
829834
830835 return block_output
@@ -907,9 +912,10 @@ def process_transaction(
907912 """
908913 # EIP-7928: Create a transaction-level StateChanges frame
909914 # The frame will read the current block_access_index from the block frame
910- increment_block_access_index (block_env .block_state_changes )
911- tx_state_changes = create_child_frame (block_env .block_state_changes )
915+ increment_block_access_index (block_env .state_changes )
916+ tx_state_changes = create_child_frame (block_env .state_changes )
912917
918+ # Capture coinbase pre-balance for net-zero filtering
913919 coinbase_pre_balance = get_account (
914920 block_env .state , block_env .coinbase
915921 ).balance
@@ -947,16 +953,27 @@ def process_transaction(
947953 effective_gas_fee = tx .gas * effective_gas_price
948954
949955 gas = tx .gas - intrinsic_gas
950- increment_nonce (block_env .state , sender , tx_state_changes )
956+
957+ # Track sender nonce increment
958+ increment_nonce (block_env .state , sender )
959+ sender_nonce_after = get_account (block_env .state , sender ).nonce
960+ track_nonce_change (tx_state_changes , sender , U64 (sender_nonce_after ))
961+
962+ # Track sender balance deduction for gas fee
963+ sender_balance_before = get_account (block_env .state , sender ).balance
964+ track_address (tx_state_changes , sender )
965+ capture_pre_balance (tx_state_changes , sender , sender_balance_before )
951966
952967 sender_balance_after_gas_fee = (
953968 Uint (sender_account .balance ) - effective_gas_fee - blob_gas_fee
954969 )
955970 set_account_balance (
956- block_env .state ,
971+ block_env .state , sender , U256 (sender_balance_after_gas_fee )
972+ )
973+ track_balance_change (
974+ tx_state_changes ,
957975 sender ,
958976 U256 (sender_balance_after_gas_fee ),
959- tx_state_changes ,
960977 )
961978
962979 access_list_addresses = set ()
@@ -991,13 +1008,13 @@ def process_transaction(
9911008 authorizations = authorizations ,
9921009 index_in_block = index ,
9931010 tx_hash = get_transaction_hash (encode_transaction (tx )),
1011+ state_changes = tx_state_changes ,
9941012 )
9951013
9961014 message = prepare_message (
9971015 block_env ,
9981016 tx_env ,
9991017 tx ,
1000- tx_state_changes ,
10011018 )
10021019
10031020 tx_output = process_message_call (message )
@@ -1027,22 +1044,24 @@ def process_transaction(
10271044 sender_balance_after_refund = get_account (
10281045 block_env .state , sender
10291046 ).balance + U256 (gas_refund_amount )
1030- set_account_balance (
1031- block_env .state ,
1047+ set_account_balance (block_env .state , sender , sender_balance_after_refund )
1048+ track_balance_change (
1049+ tx_env .state_changes ,
10321050 sender ,
10331051 sender_balance_after_refund ,
1034- tx_state_changes ,
10351052 )
10361053
10371054 coinbase_balance_after_mining_fee = get_account (
10381055 block_env .state , block_env .coinbase
10391056 ).balance + U256 (transaction_fee )
10401057
10411058 set_account_balance (
1042- block_env .state ,
1059+ block_env .state , block_env .coinbase , coinbase_balance_after_mining_fee
1060+ )
1061+ track_balance_change (
1062+ tx_env .state_changes ,
10431063 block_env .coinbase ,
10441064 coinbase_balance_after_mining_fee ,
1045- tx_state_changes ,
10461065 )
10471066
10481067 if coinbase_balance_after_mining_fee == 0 and account_exists_and_is_empty (
@@ -1068,35 +1087,19 @@ def process_transaction(
10681087
10691088 block_output .block_logs += tx_output .logs
10701089
1071- # EIP-7928: Handle in-transaction self-destruct BEFORE normalization
1072- # Destroy accounts first so normalization sees correct post-tx state
1073- # Only accounts created in same tx are in accounts_to_delete per EIP-6780
10741090 for address in tx_output .accounts_to_delete :
10751091 destroy_account (block_env .state , address )
10761092
1077- # EIP-7928: Normalize balance changes for this transaction before merging
1078- # into block frame. Must happen AFTER destroy_account so net-zero filtering
1079- # sees the correct post-transaction balance (0 for destroyed accounts).
1080- normalize_balance_changes_for_transaction (
1081- tx_state_changes ,
1082- BlockAccessIndex (
1083- get_block_access_index (block_env .block_state_changes )
1084- ),
1085- block_env .state ,
1086- )
1093+ # EIP-7928: Filter net-zero changes before committing to block frame.
1094+ # Must happen AFTER destroy_account so filtering sees correct state.
1095+ filter_net_zero_frame_changes (tx_env .state_changes , block_env .state )
10871096
1088- commit_transaction_frame (tx_state_changes )
1097+ commit_transaction_frame (tx_env . state_changes )
10891098
1090- # EIP-7928: Handle in-transaction self-destruct normalization AFTER merge
1099+ # EIP-7928: Track in-transaction self-destruct normalization AFTER merge
10911100 # Convert storage writes to reads and remove nonce/code changes
10921101 for address in tx_output .accounts_to_delete :
1093- handle_in_transaction_selfdestruct (
1094- block_env .block_state_changes ,
1095- address ,
1096- BlockAccessIndex (
1097- get_block_access_index (block_env .block_state_changes )
1098- ),
1099- )
1102+ track_selfdestruct (block_env .state_changes , address )
11001103
11011104
11021105def process_withdrawals (
@@ -1107,13 +1110,12 @@ def process_withdrawals(
11071110 """
11081111 Increase the balance of the withdrawing account.
11091112 """
1113+ # Capture pre-state for withdrawal balance filtering
11101114 withdrawal_addresses = {wd .address for wd in withdrawals }
11111115 for address in withdrawal_addresses :
11121116 pre_balance = get_account (block_env .state , address ).balance
1113- track_address (block_env .block_state_changes , address )
1114- capture_pre_balance (
1115- block_env .block_state_changes , address , pre_balance
1116- )
1117+ track_address (block_env .state_changes , address )
1118+ capture_pre_balance (block_env .state_changes , address , pre_balance )
11171119
11181120 def increase_recipient_balance (recipient : Account ) -> None :
11191121 recipient .balance += wd .amount * U256 (10 ** 9 )
@@ -1129,22 +1131,16 @@ def increase_recipient_balance(recipient: Account) -> None:
11291131
11301132 new_balance = get_account (block_env .state , wd .address ).balance
11311133 track_balance_change (
1132- block_env .block_state_changes , wd .address , new_balance
1134+ block_env .state_changes ,
1135+ wd .address ,
1136+ new_balance ,
11331137 )
11341138
11351139 if account_exists_and_is_empty (block_env .state , wd .address ):
11361140 destroy_account (block_env .state , wd .address )
11371141
1138- # EIP-7928: Normalize balance changes after all withdrawals
1139- # Filters out net-zero changes
1140-
1141- normalize_balance_changes_for_transaction (
1142- block_env .block_state_changes ,
1143- BlockAccessIndex (
1144- get_block_access_index (block_env .block_state_changes )
1145- ),
1146- block_env .state ,
1147- )
1142+ # EIP-7928: Filter net-zero balance changes for withdrawals
1143+ filter_net_zero_frame_changes (block_env .state_changes , block_env .state )
11481144
11491145
11501146def check_gas_limit (gas_limit : Uint , parent_gas_limit : Uint ) -> bool :
0 commit comments