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 ,
@@ -249,6 +248,7 @@ def state_transition(chain: BlockChain, block: Block) -> None:
249248 prev_randao = block .header .prev_randao ,
250249 excess_blob_gas = block .header .excess_blob_gas ,
251250 parent_beacon_block_root = block .header .parent_beacon_block_root ,
251+ state_changes = StateChanges (),
252252 )
253253
254254 block_output = apply_body (
@@ -636,7 +636,7 @@ def process_system_transaction(
636636 """
637637 # EIP-7928: Create a child frame for system transaction
638638 # This allows proper pre-state capture for net-zero filtering
639- system_tx_state_changes = create_child_frame (block_env .block_state_changes )
639+ system_tx_state_changes = create_child_frame (block_env .state_changes )
640640
641641 tx_env = vm .TransactionEnvironment (
642642 origin = SYSTEM_ADDRESS ,
@@ -649,8 +649,12 @@ def process_system_transaction(
649649 authorizations = (),
650650 index_in_block = None ,
651651 tx_hash = None ,
652+ state_changes = system_tx_state_changes ,
652653 )
653654
655+ # Create call frame as child of tx frame
656+ call_frame = create_child_frame (tx_env .state_changes )
657+
654658 system_tx_message = Message (
655659 block_env = block_env ,
656660 tx_env = tx_env ,
@@ -669,14 +673,15 @@ def process_system_transaction(
669673 accessed_storage_keys = set (),
670674 disable_precompiles = False ,
671675 parent_evm = None ,
672- transaction_state_changes = system_tx_state_changes ,
676+ is_create = False ,
677+ state_changes = call_frame ,
673678 )
674679
675680 system_tx_output = process_message_call (system_tx_message )
676681
677- # Merge system transaction changes back to block frame
682+ # Commit system transaction changes to block frame
678683 # System transactions always succeed (or block is invalid)
679- merge_on_success ( system_tx_state_changes )
684+ commit_transaction_frame ( tx_env . state_changes )
680685
681686 return system_tx_output
682687
@@ -816,17 +821,17 @@ def apply_body(
816821 # EIP-7928: Increment block frame to post-execution index
817822 # After N transactions, block frame is at index N
818823 # Post-execution operations (withdrawals, etc.) use index N+1
819- increment_block_access_index (block_env .block_state_changes )
824+ increment_block_access_index (block_env .state_changes )
820825
821826 process_withdrawals (block_env , block_output , withdrawals )
822827
823828 process_general_purpose_requests (
824829 block_env = block_env ,
825830 block_output = block_output ,
826831 )
827- # Build block access list from block_env.block_state_changes
832+ # Build block access list from block_env.state_changes
828833 block_output .block_access_list = build_block_access_list (
829- block_env .block_state_changes
834+ block_env .state_changes
830835 )
831836
832837 return block_output
@@ -909,9 +914,10 @@ def process_transaction(
909914 """
910915 # EIP-7928: Create a transaction-level StateChanges frame
911916 # The frame will read the current block_access_index from the block frame
912- increment_block_access_index (block_env .block_state_changes )
913- tx_state_changes = create_child_frame (block_env .block_state_changes )
917+ increment_block_access_index (block_env .state_changes )
918+ tx_state_changes = create_child_frame (block_env .state_changes )
914919
920+ # Capture coinbase pre-balance for net-zero filtering
915921 coinbase_pre_balance = get_account (
916922 block_env .state , block_env .coinbase
917923 ).balance
@@ -949,16 +955,27 @@ def process_transaction(
949955 effective_gas_fee = tx .gas * effective_gas_price
950956
951957 gas = tx .gas - intrinsic_gas
952- increment_nonce (block_env .state , sender , tx_state_changes )
958+
959+ # Track sender nonce increment
960+ increment_nonce (block_env .state , sender )
961+ sender_nonce_after = get_account (block_env .state , sender ).nonce
962+ track_nonce_change (tx_state_changes , sender , U64 (sender_nonce_after ))
963+
964+ # Track sender balance deduction for gas fee
965+ sender_balance_before = get_account (block_env .state , sender ).balance
966+ track_address (tx_state_changes , sender )
967+ capture_pre_balance (tx_state_changes , sender , sender_balance_before )
953968
954969 sender_balance_after_gas_fee = (
955970 Uint (sender_account .balance ) - effective_gas_fee - blob_gas_fee
956971 )
957972 set_account_balance (
958- block_env .state ,
973+ block_env .state , sender , U256 (sender_balance_after_gas_fee )
974+ )
975+ track_balance_change (
976+ tx_state_changes ,
959977 sender ,
960978 U256 (sender_balance_after_gas_fee ),
961- tx_state_changes ,
962979 )
963980
964981 access_list_addresses = set ()
@@ -993,13 +1010,13 @@ def process_transaction(
9931010 authorizations = authorizations ,
9941011 index_in_block = index ,
9951012 tx_hash = get_transaction_hash (encode_transaction (tx )),
1013+ state_changes = tx_state_changes ,
9961014 )
9971015
9981016 message = prepare_message (
9991017 block_env ,
10001018 tx_env ,
10011019 tx ,
1002- tx_state_changes ,
10031020 )
10041021
10051022 tx_output = process_message_call (message )
@@ -1029,22 +1046,24 @@ def process_transaction(
10291046 sender_balance_after_refund = get_account (
10301047 block_env .state , sender
10311048 ).balance + U256 (gas_refund_amount )
1032- set_account_balance (
1033- block_env .state ,
1049+ set_account_balance (block_env .state , sender , sender_balance_after_refund )
1050+ track_balance_change (
1051+ tx_env .state_changes ,
10341052 sender ,
10351053 sender_balance_after_refund ,
1036- tx_state_changes ,
10371054 )
10381055
10391056 coinbase_balance_after_mining_fee = get_account (
10401057 block_env .state , block_env .coinbase
10411058 ).balance + U256 (transaction_fee )
10421059
10431060 set_account_balance (
1044- block_env .state ,
1061+ block_env .state , block_env .coinbase , coinbase_balance_after_mining_fee
1062+ )
1063+ track_balance_change (
1064+ tx_env .state_changes ,
10451065 block_env .coinbase ,
10461066 coinbase_balance_after_mining_fee ,
1047- tx_state_changes ,
10481067 )
10491068
10501069 if coinbase_balance_after_mining_fee == 0 and account_exists_and_is_empty (
@@ -1070,35 +1089,19 @@ def process_transaction(
10701089
10711090 block_output .block_logs += tx_output .logs
10721091
1073- # EIP-7928: Handle in-transaction self-destruct BEFORE normalization
1074- # Destroy accounts first so normalization sees correct post-tx state
1075- # Only accounts created in same tx are in accounts_to_delete per EIP-6780
10761092 for address in tx_output .accounts_to_delete :
10771093 destroy_account (block_env .state , address )
10781094
1079- # EIP-7928: Normalize balance changes for this transaction before merging
1080- # into block frame. Must happen AFTER destroy_account so net-zero filtering
1081- # sees the correct post-transaction balance (0 for destroyed accounts).
1082- normalize_balance_changes_for_transaction (
1083- tx_state_changes ,
1084- BlockAccessIndex (
1085- get_block_access_index (block_env .block_state_changes )
1086- ),
1087- block_env .state ,
1088- )
1095+ # EIP-7928: Filter net-zero changes before committing to block frame.
1096+ # Must happen AFTER destroy_account so filtering sees correct state.
1097+ filter_net_zero_frame_changes (tx_env .state_changes , block_env .state )
10891098
1090- commit_transaction_frame (tx_state_changes )
1099+ commit_transaction_frame (tx_env . state_changes )
10911100
1092- # EIP-7928: Handle in-transaction self-destruct normalization AFTER merge
1101+ # EIP-7928: Track in-transaction self-destruct normalization AFTER merge
10931102 # Convert storage writes to reads and remove nonce/code changes
10941103 for address in tx_output .accounts_to_delete :
1095- handle_in_transaction_selfdestruct (
1096- block_env .block_state_changes ,
1097- address ,
1098- BlockAccessIndex (
1099- get_block_access_index (block_env .block_state_changes )
1100- ),
1101- )
1104+ track_selfdestruct (block_env .state_changes , address )
11021105
11031106
11041107def process_withdrawals (
@@ -1109,13 +1112,12 @@ def process_withdrawals(
11091112 """
11101113 Increase the balance of the withdrawing account.
11111114 """
1115+ # Capture pre-state for withdrawal balance filtering
11121116 withdrawal_addresses = {wd .address for wd in withdrawals }
11131117 for address in withdrawal_addresses :
11141118 pre_balance = get_account (block_env .state , address ).balance
1115- track_address (block_env .block_state_changes , address )
1116- capture_pre_balance (
1117- block_env .block_state_changes , address , pre_balance
1118- )
1119+ track_address (block_env .state_changes , address )
1120+ capture_pre_balance (block_env .state_changes , address , pre_balance )
11191121
11201122 def increase_recipient_balance (recipient : Account ) -> None :
11211123 recipient .balance += wd .amount * U256 (10 ** 9 )
@@ -1131,22 +1133,16 @@ def increase_recipient_balance(recipient: Account) -> None:
11311133
11321134 new_balance = get_account (block_env .state , wd .address ).balance
11331135 track_balance_change (
1134- block_env .block_state_changes , wd .address , new_balance
1136+ block_env .state_changes ,
1137+ wd .address ,
1138+ new_balance ,
11351139 )
11361140
11371141 if account_exists_and_is_empty (block_env .state , wd .address ):
11381142 destroy_account (block_env .state , wd .address )
11391143
1140- # EIP-7928: Normalize balance changes after all withdrawals
1141- # Filters out net-zero changes
1142-
1143- normalize_balance_changes_for_transaction (
1144- block_env .block_state_changes ,
1145- BlockAccessIndex (
1146- get_block_access_index (block_env .block_state_changes )
1147- ),
1148- block_env .state ,
1149- )
1144+ # EIP-7928: Filter net-zero balance changes for withdrawals
1145+ filter_net_zero_frame_changes (block_env .state_changes , block_env .state )
11501146
11511147
11521148def check_gas_limit (gas_limit : Uint , parent_gas_limit : Uint ) -> bool :
0 commit comments