Markdown Content
Below is the verbatim markdown content from tests/amsterdam/eip7928_block_level_access_lists/test_cases.md@20373115.
EIP-7928 Block Access Lists (BAL) Test Cases¶
| Function Name | Goal | Setup | Expectation | Status |
|---|---|---|---|---|
test_bal_nonce_changes |
Ensure BAL captures changes to nonce | Alice sends 100 wei to Bob | BAL MUST include changes to Alice's nonce. | ✅ Completed |
test_bal_balance_changes |
Ensure BAL captures changes to balance | Alice sends 100 wei to Bob | BAL MUST include balance change for Alice, Bob, and Coinbase | ✅ Completed |
test_bal_code_changes |
Ensure BAL captures changes to account code | Alice deploys factory contract that creates new contract | BAL MUST include code changes for newly deployed contract | ✅ Completed |
test_selfdestruct_to_account (TangerineWhistle) |
Ensure BAL captures SELFDESTRUCT success boundary for account beneficiaries | Victim executes SELFDESTRUCT(beneficiary) at exact gas boundary. Tests final gas boundary where operation completes. Parametrized: is_success (exact_gas/exact_gas_minus_1), beneficiary (EOA/contract), warm (cold/warm where warm=Berlin+), same_tx (pre_deploy/same_tx), originator_balance (0/1), beneficiary_balance (0/1). File: tests/tangerine_whistle/eip150_operation_gas_costs/test_eip150_selfdestruct.py. |
exact_gas: Beneficiary in BAL with balance_changes, victim destroyed (pre-Cancun/same_tx) or preserved (>=Cancun). exact_gas_minus_1: OOG, beneficiary in BAL only if G_NEW_ACCOUNT was part of gas calculation. |
✅ Completed |
test_selfdestruct_state_access_boundary (TangerineWhistle) |
Ensure BAL correctly tracks beneficiary access at state access boundary (consensus check) | Victim executes SELFDESTRUCT(beneficiary) at state access boundary (base + cold). Verifies beneficiary is accessed before G_NEW_ACCOUNT check. Parametrized: is_success (exact_gas/exact_gas_minus_1), beneficiary (EOA/contract), warm (cold/warm), same_tx, originator_balance (0/1), beneficiary_balance (0/1). File: tests/tangerine_whistle/eip150_operation_gas_costs/test_eip150_selfdestruct.py. |
exact_gas: Beneficiary IN BAL (state accessed). exact_gas_minus_1: Beneficiary NOT in BAL (OOG before state access). Operation may succeed at exact_gas if no G_NEW_ACCOUNT needed. | ✅ Completed |
test_selfdestruct_to_self (TangerineWhistle) |
Ensure BAL captures SELFDESTRUCT where beneficiary is self at gas boundary | Victim executes SELFDESTRUCT(ADDRESS) - selfdestructs to itself. Always warm, always alive (no G_NEW_ACCOUNT, no cold access). Gas = G_BASE + G_SELF_DESTRUCT. Parametrized: is_success (exact_gas/exact_gas_minus_1), originator_balance (0/1), same_tx (pre_deploy/same_tx). File: tests/tangerine_whistle/eip150_operation_gas_costs/test_eip150_selfdestruct.py. |
exact_gas_minus_1: Victim in BAL with unchanged state. exact_gas: Pre-Cancun/same_tx: destroyed, balance=0. >=Cancun pre-existing: preserved with original balance. | ✅ Completed |
test_selfdestruct_to_precompile (TangerineWhistle) |
Ensure BAL captures SELFDESTRUCT success boundary for precompile beneficiaries | Victim executes SELFDESTRUCT(precompile) at exact gas boundary. Precompiles are always warm (no cold access charge). Parametrized: is_success (exact_gas/exact_gas_minus_1), all precompiles via @pytest.mark.with_all_precompiles, same_tx (pre_deploy/same_tx), originator_balance (0/1), beneficiary_balance (0/1). File: tests/tangerine_whistle/eip150_operation_gas_costs/test_eip150_selfdestruct.py. |
exact_gas: Precompile in BAL with balance_changes, victim destroyed (pre-Cancun/same_tx) or preserved (>=Cancun). exact_gas_minus_1: OOG, precompile in BAL only if G_NEW_ACCOUNT was part of gas calculation. |
✅ Completed |
test_selfdestruct_to_precompile_state_access_boundary (TangerineWhistle) |
Ensure BAL correctly tracks precompile access at state access boundary (consensus check) | Victim executes SELFDESTRUCT(precompile) at state access boundary (base only, precompiles always warm). Verifies precompile is accessed before G_NEW_ACCOUNT check. Parametrized: is_success (exact_gas/exact_gas_minus_1), all precompiles, same_tx, originator_balance (0/1), beneficiary_balance (0/1). File: tests/tangerine_whistle/eip150_operation_gas_costs/test_eip150_selfdestruct.py. |
exact_gas: Precompile IN BAL (state accessed). exact_gas_minus_1: Precompile NOT in BAL (OOG before state access). Operation may succeed at exact_gas if no G_NEW_ACCOUNT needed. | ✅ Completed |
test_selfdestruct_to_system_contract (Cancun) |
Ensure BAL captures SELFDESTRUCT success boundary for system contract beneficiaries | Victim executes SELFDESTRUCT(system_contract) at exact gas boundary. System contracts are always warm (no cold access charge) and always have code (no G_NEW_ACCOUNT charge). Gas = G_VERY_LOW + G_SELF_DESTRUCT. Parametrized: is_success (exact_gas/exact_gas_minus_1), all system contracts via @pytest.mark.with_all_system_contracts, same_tx (pre_deploy/same_tx), originator_balance (0/1). File: tests/tangerine_whistle/eip150_operation_gas_costs/test_eip150_selfdestruct.py. |
exact_gas: System contract in BAL with balance_changes if originator had balance, victim destroyed (same_tx) or balance=0 (pre-existing). exact_gas_minus_1: OOG, system contract not in BAL (no state access). |
✅ Completed |
test_initcode_selfdestruct_to_self (TangerineWhistle) |
Ensure BAL captures SELFDESTRUCT during initcode where beneficiary is self | Initcode executes SELFDESTRUCT(ADDRESS) during CREATE, before any code is deployed. Contract has nonce=1 (post-EIP-161), making it non-empty. Always warm (executing contract), no G_NEW_ACCOUNT (nonce > 0). Gas boundary testing not possible (CREATE uses all available gas). Parametrized: originator_balance (0/1). File: tests/tangerine_whistle/eip150_operation_gas_costs/test_eip150_selfdestruct.py. |
Contract created and destroyed in same tx - victim has empty BAL changes. Caller has nonce_changes (incremented by CREATE) and balance_changes if originator had balance. Victim is NONEXISTENT in post state. |
✅ Completed |
test_bal_account_access_target |
Ensure BAL captures target addresses of account access opcodes | Alice calls Oracle contract which uses account access opcodes (BALANCE, EXTCODESIZE, EXTCODECOPY, EXTCODEHASH, CALL, CALLCODE, DELEGATECALL, STATICCALL) on TargetContract. |
BAL MUST include Alice, Oracle, and TargetContract with empty changes for TargetContract and nonce changes for Alice. |
✅ Completed |
test_bal_call_no_delegation_and_oog_before_target_access |
Ensure BAL handles OOG before target access and success for non-delegated CALL | Parametrized: target warm/cold, target empty/existing, value 0/1, memory expansion, OOG boundary (before_target_access/success). | OOG: target in BAL ONLY if pre-warmed. Success: target always in BAL with balance changes when value > 0. | ✅ Completed |
test_bal_call_no_delegation_oog_after_target_access |
Ensure BAL includes target but excludes value transfer when OOG after target access | Hardcoded: empty target, value=1 (required for create_cost gap). Parametrized: warm/cold, memory expansion. | Target always in BAL. No balance changes (value transfer fails after G_NEW_ACCOUNT check). | ✅ Completed |
test_bal_call_7702_delegation_and_oog |
Ensure BAL handles OOG at all 4 boundaries for CALL to 7702 delegated accounts | Parametrized: target warm/cold, delegation warm/cold, value 0/1, memory expansion, OOG boundary (before_target_access/after_target_access/success_minus_1/success). | OOG before: neither in BAL. OOG after & success_minus_1: target in BAL, delegation NOT in BAL (static check optimization). Success: all in BAL. | ✅ Completed |
test_bal_callcode_nested_value_transfer |
Ensure BAL captures balance changes from nested value transfers when CALLCODE executes target code that itself makes CALL with value | Alice calls Oracle contract (200 wei balance) which uses CALLCODE to execute TargetContract's code; that code makes a nested CALL transferring 100 wei to Bob. |
BAL MUST include Alice (nonce changes), Oracle (balance change to 100 wei), Bob (balance change to 100 wei), and TargetContract (empty changes). |
✅ Completed |
test_bal_delegated_storage_writes |
Ensure BAL captures delegated storage writes via DELEGATECALL and CALLCODE |
Alice calls Oracle contract which uses DELEGATECALL/CALLCODE to TargetContract that writes 0x42 to slot 0x01. |
BAL MUST include Alice (nonce changes), Oracle (storage changes for slot 0x01 = 0x42), and TargetContract (empty changes). |
✅ Completed |
test_bal_delegated_storage_reads |
Ensure BAL captures delegated storage reads via DELEGATECALL and CALLCODE |
Alice calls Oracle contract (with slot 0x01 = 0x42) which uses DELEGATECALL/CALLCODE to TargetContract that reads from slot 0x01. |
BAL MUST include Alice (nonce changes), Oracle (storage reads for slot 0x01), and TargetContract (empty changes). |
✅ Completed |
test_bal_block_rewards |
BAL tracks fee recipient balance changes from block rewards | Alice sends 100 wei to Bob with Charlie as fee recipient | BAL MUST include fee recipient Charlie with balance_changes reflecting transaction fees collected from the block. |
✅ Completed |
test_bal_2930_account_listed_but_untouched |
Ensure BAL excludes listed but untouched account | Alice sends a simple eth transfer tx to Bob with EIP-2930 access list including Oracle |
BAL MUST NOT include any entry for Oracle because it wasn't accessed. |
✅ Completed |
test_bal_2930_slot_listed_but_untouched |
Ensure BAL excludes listed but untouched storage slots | Alice sends tx with EIP-2930 access list including (PureCalculator, slot=0x01); PureCalculator executes pure arithmetic (adding two numbers) without touching slot 0x01 |
BAL MUST NOT include any entry for PureCalculator's slot 0x01 because it doesn't access state |
✅ Completed |
test_bal_2930_slot_listed_and_unlisted_writes |
Ensure BAL includes storage writes regardless of access list presence | Alice sends tx with EIP-2930 access list including (StorageWriter, slot=0x01); StorageWriter executes SSTORE to slots 0x01 and 0x02 |
BAL MUST include storage_changes for StorageWriter's slots 0x01 and 0x02 |
✅ Completed |
test_bal_2930_slot_listed_and_unlisted_reads |
Ensure BAL includes storage reads regardless of access list presence | Alice sends tx with EIP-2930 access list including (StorageReader, slot=0x01); StorageReader executes SLOAD from slots 0x01 and 0x02 |
BAL MUST include storage_reads for StorageReader's slots 0x01 and 0x02 |
✅ Completed |
test_bal_self_transfer |
BAL handles self-transfers correctly | Alice sends 100 wei to Alice |
BAL MUST include one entry for Alice with balance_changes reflecting gas cost only (value cancels out) and nonce change. |
✅ Completed |
test_bal_zero_value_transfer |
BAL handles zero-value transfers correctly | Alice sends 0 wei to Bob |
BAL MUST include Alice with balance_changes (gas cost only) and nonce change, and Bob in account_changes with empty balance_changes. |
✅ Completed |
test_bal_noop_storage_write |
Ensure BAL includes storage read but not write for no-op writes where pre-state equals post-state | Contract with pre-existing storage value 0x42 in slot 0x01; transaction executes SSTORE(0x01, 0x42) (writing same value) |
BAL MUST include the contract address with storage_reads for slot 0x01 since it was accessed, but MUST NOT include it in storage_changes (no actual state change). |
✅ Completed |
test_bal_fully_unmutated_account |
Ensure BAL captures account that has zero net mutations | Alice sends 0 wei to Oracle which writes same pre-existing value to storage |
BAL MUST include Alice with nonce_changes and balance changes (gas), Oracle with storage_reads for accessed slot but empty storage_changes. |
✅ Completed |
test_bal_net_zero_balance_transfer |
BAL includes accounts with net-zero balance change but excludes them from balance changes | Contract receives and sends same amount to recipient using CALL or SELFDESTRUCT | BAL MUST include contract in account_changes without balance_changes (net zero). BAL MUST record non-zero balance_changes for recipient. |
✅ Completed |
test_bal_system_dequeue_consolidations_eip7251 |
BAL tracks post-exec system dequeues for consolidations | Pre-populate EIP-7251 consolidation requests; produce a block where dequeues occur | BAL MUST include the 7251 system contract with storage_changes (queue slots 0–3) using block_access_index = len(txs). |
✅ Completed |
test_bal_withdrawal_contract_cross_index |
Ensure withdrawal system contract shows storage changes at both tx and post-execution indices | Alice sends withdrawal request to EIP-7002 system contract. Slots 0x01 (count) and 0x03 (tail) are incremented during tx (index 1) and reset during post-execution dequeue (index 2). | BAL MUST include withdrawal request contract with storage_changes for slots 0x01 and 0x03, each with two slot_changes: one at block_access_index=1 (tx) and one at block_access_index=2 (post-exec). |
✅ Completed |
test_bal_consolidation_contract_cross_index |
Ensure consolidation system contract shows storage changes at both tx and post-execution indices | Alice sends consolidation request to EIP-7251 system contract. Slots 0x01 (count) and 0x03 (tail) are incremented during tx (index 1) and reset during post-execution dequeue (index 2). | BAL MUST include consolidation request contract with storage_changes for slots 0x01 and 0x03, each with two slot_changes: one at block_access_index=1 (tx) and one at block_access_index=2 (post-exec). |
✅ Completed |
test_bal_noop_write_filtering |
Ensure BAL filters NOOP storage writes (writing same value or 0 to empty slot) | Contract writes 0 to uninitialized slot 1 (noop), 42 to slot 2 (real change), 100 to slot 3 (same as pre-state, noop), 200 to slot 4 (different from pre-state 150, real change). | BAL MUST include storage_changes only for slots 2 and 4 (actual changes). Slots 1 and 3 MUST NOT appear in storage_changes (no-op writes filtered). |
✅ Completed |
test_bal_system_contract_noop_filtering |
Ensure system contract post-execution calls filter net-zero storage writes | Simple transfer that doesn't interact with system contracts. Post-execution system calls read withdrawal/consolidation contract slots 0-3 but don't modify them. | Withdrawal and consolidation system contracts MUST have storage_reads for slots 0x00-0x03 but MUST NOT have storage_changes (no actual modifications occurred). |
✅ Completed |
test_bal_aborted_storage_access |
Ensure BAL captures storage access in aborted transactions correctly | Alice calls contract that reads storage slot 0x01, writes to slot 0x02, then aborts with REVERT/INVALID |
BAL MUST include storage_reads for slots 0x01 and 0x02 (aborted writes become reads), empty storage_changes. Only nonce changes for Alice. |
✅ Completed |
test_bal_aborted_account_access |
Ensure BAL captures account access in aborted transactions for all account accessing opcodes | Alice calls AbortContract that performs account access operations (BALANCE, EXTCODESIZE, EXTCODECOPY, EXTCODEHASH, CALL, CALLCODE, DELEGATECALL, STATICCALL) on TargetContract and aborts via REVERT/INVALID |
BAL MUST include Alice, TargetContract, and AbortContract in account_changes and nonce changes for Alice. |
✅ Completed |
test_bal_pure_contract_call |
Ensure BAL captures contract access for pure computation calls | Alice calls PureContract that performs pure arithmetic (ADD operation) without storage or balance changes |
BAL MUST include Alice and PureContract in account_changes, and nonce_changes for Alice. |
✅ Completed |
test_bal_create_storage_op_then_selfdestruct_same_tx |
BAL correctly demotes ephemeral storage operations to storage_reads when a contract is created and destroyed in the same tx (combined coverage for read and write storage_op cases — replaces test_bal_create2_to_A_read_then_selfdestruct and test_bal_create2_to_A_write_then_selfdestruct). Parametrized: @pytest.mark.with_all_create_opcodes + storage_op ∈ ["read", "write"]. |
Address A is pre-funded via pre.fund_address. Alice sends a single tx calling a factory that deploys a contract at A via the parametrized create opcode; init code is either SLOAD(B)+SELFDESTRUCT or SSTORE(B, v)+SELFDESTRUCT (beneficiary = separate EOA). |
BAL MUST include A with balance_changes [(1, 0)] (outflow to beneficiary on destruction). Slot B MUST appear under storage_reads, NOT storage_changes (write demoted to read because the contract is destroyed same-tx). A MUST NOT have nonce_changes or code_changes. Beneficiary has balance_changes reflecting receipt of fund at index 1. |
✅ Completed |
test_bal_precompile_funded |
BAL records precompile value transfer with or without balance change | Alice sends value to precompile (all precompiles) via direct transaction. Parameterized: (1) with value (1 ETH), (2) without value (0 ETH). | For with_value: BAL MUST include precompile with balance_changes. For no_value: BAL MUST include precompile with empty balance_changes. No storage_changes or code_changes in either case. |
✅ Completed |
test_bal_precompile_call_opcode |
BAL records the precompile address regardless of call opcode. | Parametrized: @pytest.mark.with_all_precompiles × @pytest.mark.with_all_call_opcodes. Alice calls Oracle which invokes the precompile via the parametrized call opcode. For DELEGATECALL/CALLCODE the precompile provides the code but is not the call target, so its access has to be recorded explicitly rather than incidentally. |
BAL MUST include the precompile address with empty changes for all four call opcodes. Oracle has empty changes (called but no state mutation), Alice has nonce_changes. |
✅ Completed |
test_bal_7702_delegated_create |
BAL tracks EIP-7702 delegation indicator write and contract creation | Alice sends a type-4 (7702) tx authorizing herself to delegate to Deployer code which executes CREATE |
BAL MUST include for Alice: code_changes (delegation indicator), nonce_changes (increment from 7702 processing), and balance_changes (post-gas). For Child: code_changes (runtime bytecode) and nonce_changes = 1. |
✅ Completed |
test_bal_7702_delegation_create |
Ensure BAL captures creation of EOA delegation | Alice authorizes delegation to contract Oracle. Transaction sends 10 wei to Bob. Two variants: (1) Self-funded: Alice sends 7702 tx herself. (2) Sponsored: Relayer sends 7702 tx on Alice's behalf. |
BAL MUST include Alice: code_changes (delegation designation 0xef0100\|\|address(Oracle)),nonce_changes (increment). Bob: balance_changes (receives 10 wei). For sponsored variant, BAL MUST also include Relayer:nonce_changes.Oracle MUST NOT be present in BAL - the account is never accessed. |
✅ Completed |
test_bal_7702_delegation_update |
Ensure BAL captures update of existing EOA delegation | Alice first delegates to Oracle1, then in second tx updates delegation to Oracle2. Each transaction sends 10 wei to Bob. Two variants: (1) Self-funded: Alice sends both 7702 txs herself. (2) Sponsored: Relayer sends both 7702 txs on Alice's behalf. |
BAL MUST include Alice: first tx has code_changes (delegation designation 0xef0100\|\|address(Oracle1)),nonce_changes. Second tx hascode_changes (delegation designation 0xef0100\|\|address(Oracle2)),nonce_changes. Bob:balance_changes (receives 10 wei on each tx). For sponsored variant, BAL MUST also include Relayer:nonce_changes for both transactions. Oracle1 and Oracle2 MUST NOT be present in BAL - accounts are never accessed. |
✅ Completed |
test_bal_7702_delegation_clear |
Ensure BAL captures clearing of EOA delegation | Alice first delegates to Oracle, then in second tx clears delegation by authorizing to 0x0 address. Each transaction sends 10 wei to Bob. Two variants: (1) Self-funded: Alice sends both 7702 txs herself. (2) Sponsored: Relayer sends both 7702 txs on Alice's behalf. |
BAL MUST include Alice: first tx has code_changes (delegation designation 0xef0100\|\|address(Oracle)), nonce_changes. Second tx has code_changes (empty code - delegation cleared), nonce_changes. Bob: balance_changes (receives 10 wei on each tx). For sponsored variant, BAL MUST also include Relayer: nonce_changes for both transactions. Oracle and 0x0 address MUST NOT be present in BAL - accounts are never accessed. |
✅ Completed |
test_bal_7702_delegated_storage_access |
Ensure BAL captures storage operations when calling a delegated EIP-7702 account | Alice has delegated her account to Oracle. Oracle contract contains code that reads from storage slot 0x01 and writes to storage slot 0x02. Bob sends 10 wei to Alice (the delegated account), which executes Oracle's code. |
BAL MUST include Alice: balance_changes (receives 10 wei), storage_changes for slot 0x02 (write operation performed in Alice's storage), storage_reads for slot 0x01 (read operation from Alice's storage). Bob: nonce_changes (sender), balance_changes (loses 10 wei plus gas costs). Oracle (account access). |
✅ Completed |
test_bal_7702_invalid_nonce_authorization |
Ensure BAL handles failed authorization due to wrong nonce | Relayer sends sponsored transaction to Bob (10 wei transfer succeeds) but Alice's authorization to delegate to Oracle uses incorrect nonce, causing silent authorization failure |
BAL MUST include Alice with empty changes (account access), Bob with balance_changes (receives 10 wei), Relayer with nonce_changes. MUST NOT include Oracle (authorization failed, no delegation) |
✅ Completed |
test_bal_7702_invalid_chain_id_authorization |
Ensure BAL handles failed authorization due to wrong chain id | Relayer sends sponsored transaction to Bob (10 wei transfer succeeds) but Alice's authorization to delegate to Oracle uses incorrect chain id, causing authorization failure before account access |
BAL MUST include Bob with balance_changes (receives 10 wei), Relayer with nonce_changes. MUST NOT include Alice (authorization fails before loading account) or Oracle (authorization failed, no delegation) |
✅ Completed |
test_call_into_self_delegating_set_code |
Self-delegation degenerate one-hop case (companion to test_call_into_chain_delegating_set_code). File: tests/prague/eip7702_set_code_tx/test_set_code_txs.py. Parametrized over @pytest.mark.with_all_call_opcodes. |
auth_signer auths itself as its own delegation target. entry_address issues call_opcode(auth_signer). EVM resolves once: auth_signer's code is the designator pointing back to auth_signer; the second hop is not followed, so the 0xef0100... bytecode runs as legacy code → INVALID → returns 0. |
auth_signer MUST appear with nonce_changes and code_changes (delegation designator to itself). entry_address MUST have storage_reads=[0] (no-op SSTORE demoted). No additional delegation-target entry is created because the target coincides with the authority. |
✅ Completed |
test_bal_7702_invalid_authority_has_code_authorization |
Ensure BAL handles failed authorization where the authority already has non-empty, non-delegation code (EIP-7702 step-5 rejection, post-load). | Alice is pre-allocated with code=Op.STOP and nonce=1. Relayer sends sponsored tx to Bob (10 wei transfer). Authorization tuple delegates Alice to Oracle with matching nonce. EIP-7702 step 5 rejects because Alice's code is non-empty and not a delegation designator. |
BAL MUST include Bob with balance_changes (10 wei), Relayer with nonce_changes, and Alice with empty changes (loaded at step 4 then rejected at step 5). MUST NOT include Oracle (delegation target never loaded). |
✅ Completed |
test_signature_s_out_of_range |
Ensure BAL excludes the authority when the EIP-7702 auth signature has an out-of-range s value (EIP-2 low-s rule, ecrecover fails before authority is loaded). File: tests/prague/eip7702_set_code_tx/test_set_code_txs.py. |
auth_signer signs a normal auth tuple delegating to set_code_to_address; the tuple is then mutated so s = SECP256K1N - s and v is flipped, producing a high-s signature. Sender (a separate EOA) sends the tx to entry_address which executes SSTORE(1, 1). |
BAL MUST include sender with nonce_changes at block_access_index=1 and entry_address with storage_changes for slot 1 (post=1). BAL MUST NOT include auth_signer (auth fails at ecrecover, before the authority is added to accessed_addresses) or set_code_to_address (target is never loaded as an execution target). |
✅ Completed |
test_nonce_validity |
Ensure BAL correctly records EIP-7702 auth-tuple nonce-field validity across pre-load (nonce >= 2**64-1), success, and post-load (account.nonce != auth.nonce) branches. File: tests/prague/eip7702_set_code_tx/test_set_code_txs.py. |
Parametrized over (account_nonce, authorization_nonce): (1) (MAX, MAX) pre-load rejection at step 2, (2) (MAX-1, MAX-1) successful auth, (3) (0, 1) post-load nonce mismatch, (4) (1, 0) post-load nonce mismatch. entry_address always does SSTORE(success_slot, 1) + CALL(auth_signer) + SSTORE(return_slot, RETURNDATASIZE). |
Valid auth (case 2): auth_signer MUST have nonce_changes (post=account_nonce+1) and code_changes (delegation designation); set_code_to_address MUST appear with empty changes (loaded as execution target via delegation dispatch); entry_address has storage_changes for both slot 1 and slot 2 (post=1). Invalid auth (cases 1, 3, 4): auth_signer MUST appear with empty changes (the subsequent CALL touches it, even though the auth processing did or did not add it); set_code_to_address MUST NOT be in BAL; entry_address has storage_changes for slot 1 and storage_reads for slot 2 (the SSTORE(0, 0) no-op is demoted per EIP-7928). Sender always has nonce_changes. |
✅ Completed |
test_call_into_chain_delegating_set_code |
Ensure BAL respects EIP-7702 one-hop delegation resolution when A delegates to B and B delegates back to A. File: tests/prague/eip7702_set_code_tx/test_set_code_txs.py. Parameterized over @pytest.mark.with_all_call_opcodes. |
Two auths: auth_signer_1 -> auth_signer_2, then auth_signer_2 -> auth_signer_1. entry_address issues call_opcode(auth_signer_1) and stores the return code in slot 0. The EVM resolves A -> B once, then runs B's 0xef0100... bytecode as legacy code; 0xef is INVALID, so the call frame aborts and returns 0. |
BAL MUST include only auth_signer_1 and auth_signer_2 from the delegation chain (each with nonce_changes and code_changes for its delegation designator). No third address is ever loaded because the second hop is never followed. entry_address MUST have storage_reads for slot 0 (the SSTORE(slot, 0) is a no-op write and is demoted per EIP-7928) and no storage_changes. Sender has nonce_changes. |
✅ Completed |
test_bal_7702_multi_hop_delegation_chain |
Multi-hop EIP-7702 delegation: chain resolves A→B→C and loop resolves A→B→A. In both cases the EVM follows the delegation once and runs B's 0xef0100<dest> bytecode, which aborts on the INVALID 0xef opcode. Parametrized over destination_is_loop ∈ [False, True]. |
auth_a delegates to auth_b; auth_b delegates to either target_c (chain) or back to auth_a (loop). Entry contract has storage[0] = 0xDEAD (witness) and issues CALL(auth_a) then stores the return code in slot 0. |
auth_a and auth_b MUST appear with nonce_changes and code_changes (each successful auth). In chain, target_c MUST NOT appear in BAL. entry_address has storage_changes for slot 0 with post_value=0 (0xDEAD overwritten by the failed CALL's return code 0). Sender has nonce_changes. |
✅ Completed |
test_set_code_to_precompile |
Covers the BAL recording of a precompile address when reached via EIP-7702 delegation dispatch — across all 4 call opcodes and all precompiles. Companion to test_bal_precompile_call_opcode for the direct-call path. File: tests/prague/eip7702_set_code_tx/test_set_code_txs.py. |
@pytest.mark.with_all_call_opcodes × @pytest.mark.with_all_precompiles. EOA auth_signer delegates to a precompile address. A caller contract invokes call_opcode(auth_signer); EIP-7702 resolves the delegation to the precompile (whose code is treated as empty per spec). |
When EIP-7928 is enabled, BAL MUST include auth_signer with nonce_changes and code_changes (delegation designator), and MUST include the precompile address with empty changes (loaded as execution target via delegation dispatch). |
✅ Completed |
test_bal_7702_delegated_via_call_opcode |
Ensure BAL captures delegation target when a contract uses *CALL opcodes to call a delegated account | Pre-deployed contract Alice delegated to Oracle. Caller contract uses CALL/CALLCODE/DELEGATECALL/STATICCALL to call Alice. Bob sends transaction to Caller. |
BAL MUST include Bob: nonce_changes. Caller: empty changes (account access). Alice: empty changes (account access - delegated account being called). Oracle: empty changes (delegation target access). |
✅ Completed |
test_bal_7702_null_address_delegation_no_code_change |
Ensure BAL does not record spurious code changes for net-zero code operations | Alice sends transaction with authorization delegating to NULL_ADDRESS (0x0), which sets code to b"" on an account that already has b"" code. Transaction sends 10 wei to Bob. |
BAL MUST include Alice with nonce_changes (tx nonce + auth nonce increment) but MUST NOT include code_changes (setting b"" -> b"" is net-zero and filtered out). Bob: balance_changes (receives 10 wei). This ensures net-zero code change is not recorded. |
✅ Completed |
test_bal_7702_double_auth_reset |
Ensure BAL tracks multiple 7702 nonce increments but filters net-zero code change | Single transaction contains two EIP-7702 authorizations for Alice: (1) first auth sets delegation 0xef0100\|\|Oracle, (2) second auth clears delegation back to empty. Transaction sends 10 wei to Bob. Two variants: (a) Self-funded: Alice is tx sender (one tx nonce bump + two auth bumps → nonce 0→3). (b) Sponsored: Relayer is tx sender (Alice only in auths → nonce 0→2 for Alice, plus one nonce bump for Relayer). |
Variant (a): BAL MUST include Alice with nonce_changes 0→3. Variant (b): BAL MUST include Alice with nonce_changes 0→2 and Relayer with its own nonce_changes. For both variants, BAL MUST NOT include code_changes for Alice (net code is empty), MUST include Bob with balance_changes (receives 10 wei), and Oracle MUST NOT appear in BAL. |
✅ Completed |
test_bal_7702_double_auth_swap |
Ensure BAL captures final code when double auth swaps delegation targets | Relayer sends transaction with two authorizations for Alice: (1) First auth sets delegation to CONTRACT_A at nonce=0, (2) Second auth changes delegation to CONTRACT_B at nonce=1. Transaction sends 10 wei to Bob. Per EIP-7702, only the last authorization takes effect. |
BAL MUST include Alice with nonce_changes (both auths increment nonce to 2) and code_changes (final code is delegation designation for CONTRACT_B, not CONTRACT_A). Bob: balance_changes (receives 10 wei). Relayer: nonce_changes. Neither CONTRACT_A nor CONTRACT_B appear in BAL during delegation setup (never accessed). This ensures BAL shows final state, not intermediate changes. |
✅ Completed |
test_bal_sstore_and_oog |
Ensure BAL handles OOG during SSTORE execution at various gas boundaries (EIP-2200 stipend and implicit SLOAD) | Alice calls contract that attempts SSTORE to cold slot 0x01. Parameterized: (1) OOG at EIP-2200 stipend check (2300 gas after PUSH opcodes) - fails before implicit SLOAD, (2) OOG at stipend + 1 (2301 gas) - passes stipend check but fails after implicit SLOAD, (3) OOG at exact gas - 1, (4) Successful SSTORE with exact gas. |
For case (1): BAL MUST NOT include slot 0x01 in storage_reads or storage_changes (fails before implicit SLOAD). For cases (2) and (3): BAL MUST include slot 0x01 in storage_reads (implicit SLOAD occurred) but MUST NOT include in storage_changes (write didn't complete). For case (4): BAL MUST include slot 0x01 in storage_changes only (successful write; read is filtered by builder). |
✅ Completed |
test_bal_sstore_static_context |
SSTORE in static context must not leak storage reads into BAL | Contract A STATICCALLs Contract B which attempts SSTORE. Parametrized: original_value (0, nonzero) to catch clients that perform the implicit SLOAD before the static check. |
Contract B IS in BAL (accessed via STATICCALL) but MUST NOT have storage_reads. |
✅ Completed |
test_bal_sload_and_oog |
Ensure BAL handles OOG during SLOAD execution correctly | Alice calls contract that attempts SLOAD from cold slot 0x01. Parameterized: (1) OOG at SLOAD opcode (insufficient gas), (2) Successful SLOAD execution. |
For OOG case: BAL MUST NOT contain slot 0x01 in storage_reads since storage wasn't accessed. For success case: BAL MUST contain slot 0x01 in storage_reads. |
✅ Completed |
test_bal_balance_and_oog |
Ensure BAL handles OOG during BALANCE opcode execution correctly | Alice calls contract that attempts BALANCE opcode on cold target account. Parameterized: (1) OOG at BALANCE opcode (insufficient gas), (2) Successful BALANCE execution. |
For OOG case: BAL MUST NOT include target account (wasn't accessed). For success case: BAL MUST include target account in account_changes. |
✅ Completed |
test_bal_account_touch_system_address |
Ensure BAL includes SYSTEM_ADDRESS when a regular transaction touches it via any account-accessing opcode |
Alice calls a contract that executes one of BALANCE, EXTCODESIZE, EXTCODEHASH, EXTCODECOPY, CALL, or STATICCALL against SYSTEM_ADDRESS. Parametrized over the six opcodes. |
BAL MUST include SYSTEM_ADDRESS as an account-only entry for every opcode because the address experienced a real EVM state access. This is distinct from excluding the synthetic system-operation caller. |
✅ Completed |
test_bal_extcodesize_and_oog |
Ensure BAL handles OOG during EXTCODESIZE opcode execution correctly | Alice calls contract that attempts EXTCODESIZE opcode on cold target contract. Parameterized: (1) OOG at EXTCODESIZE opcode (insufficient gas), (2) Successful EXTCODESIZE execution. |
For OOG case: BAL MUST NOT include target contract (wasn't accessed). For success case: BAL MUST include target contract in account_changes. |
✅ Completed |
test_bal_delegatecall_no_delegation_and_oog_before_target_access |
Ensure BAL handles OOG before target access and success for non-delegated DELEGATECALL | Parametrized: target warm/cold, memory expansion, OOG boundary (before_target_access/success). | OOG: target in BAL ONLY if pre-warmed. Success: target always in BAL. | ✅ Completed |
test_bal_delegatecall_7702_delegation_and_oog |
Ensure BAL handles OOG at all 4 boundaries for DELEGATECALL to 7702 delegated accounts | Parametrized: target warm/cold, delegation warm/cold, memory expansion, OOG boundary (before_target_access/after_target_access/success_minus_1/success). | OOG before: neither in BAL. OOG after & success_minus_1: target in BAL, delegation NOT in BAL (static check optimization). Success: all in BAL. | ✅ Completed |
test_bal_callcode_no_delegation_and_oog_before_target_access |
Ensure BAL handles OOG before target access and success for non-delegated CALLCODE | Parametrized: target warm/cold, value 0/1, memory expansion, OOG boundary (before_target_access/success). | OOG: target in BAL ONLY if pre-warmed. Success: target always in BAL. | ✅ Completed |
test_bal_callcode_7702_delegation_and_oog |
Ensure BAL handles OOG at all 4 boundaries for CALLCODE to 7702 delegated accounts | Parametrized: target warm/cold, delegation warm/cold, value 0/1, memory expansion, OOG boundary (before_target_access/after_target_access/success_minus_1/success). | OOG before: neither in BAL. OOG after & success_minus_1: target in BAL, delegation NOT in BAL (static check optimization). Success: all in BAL. | ✅ Completed |
test_bal_staticcall_no_delegation_and_oog_before_target_access |
Ensure BAL handles OOG before target access and success for non-delegated STATICCALL | Parametrized: target warm/cold, memory expansion, OOG boundary (before_target_access/success). | OOG: target in BAL ONLY if pre-warmed. Success: target always in BAL. | ✅ Completed |
test_bal_staticcall_7702_delegation_and_oog |
Ensure BAL handles OOG at all 4 boundaries for STATICCALL to 7702 delegated accounts | Parametrized: target warm/cold, delegation warm/cold, memory expansion, OOG boundary (before_target_access/after_target_access/success_minus_1/success). | OOG before: neither in BAL. OOG after & success_minus_1: target in BAL, delegation NOT in BAL (static check optimization). Success: all in BAL. | ✅ Completed |
test_bal_extcodecopy_and_oog |
Ensure BAL handles OOG during EXTCODECOPY at various failure points | Alice calls contract that attempts EXTCODECOPY from cold target contract. Parameterized: (1) Successful EXTCODECOPY, (2) OOG at cold access (insufficient gas for account access), (3) OOG at memory expansion with large offset (64KB offset, gas covers cold access + copy but NOT memory expansion), (4) OOG at memory expansion boundary (256 byte offset, gas is exactly 1 less than needed). |
For success case: BAL MUST include target contract. For all OOG cases: BAL MUST NOT include target contract. Gas for ALL components (cold access + copy + memory expansion) must be checked BEFORE recording account access. | ✅ Completed |
test_bal_multiple_balance_changes_same_account |
Ensure BAL tracks multiple balance changes to same account across transactions | Alice funds Bob (starts at 0) in tx0 with exact amount needed. Bob spends everything in tx1 to Charlie. Bob's balance: 0 → funding_amount → 0 | BAL MUST include Bob with two balance_changes: one at txIndex=1 (receives funds) and one at txIndex=2 (balance returns to 0). This tests balance tracking across two transactions. |
✅ Completed |
test_bal_multiple_storage_writes_same_slot |
Ensure BAL tracks multiple writes to same storage slot across transactions | Alice calls contract 3 times in same block. Contract increments slot 1 on each call: 0 → 1 → 2 → 3 | BAL MUST include contract with slot 1 having three slot_changes: txIndex=1 (value 1), txIndex=2 (value 2), txIndex=3 (value 3). Each transaction's write must be recorded separately. |
✅ Completed |
test_bal_nested_delegatecall_storage_writes_net_zero |
Ensure BAL correctly filters net-zero storage changes across nested DELEGATECALL frames | Parametrized by nesting depth (1-3). Root contract has slot 0 = 1. Each frame writes a different intermediate value via DELEGATECALL chain, deepest frame writes back to original value (1). Example depth=2: 1 → 2 → 3 → 1 | BAL MUST include root contract with storage_reads for slot 0 but MUST NOT include storage_changes (net-zero). All delegate contracts MUST have empty changes. Tests that frame merging correctly removes parent's intermediate writes when child reverts to pre-tx value. |
✅ Completed |
test_bal_cross_tx_storage_revert_to_zero |
Ensure BAL captures storage changes when tx2 reverts slot back to original value (blobhash regression test) | Alice sends tx1 writing slot 0=0xABCD (from 0x0), then tx2 writing slot 0=0x0 (back to original) | BAL MUST include contract with slot 0 having two slot_changes: txIndex=1 (0xABCD) and txIndex=2 (0x0). Cross-transaction net-zero MUST NOT be filtered. |
✅ Completed |
test_bal_intra_tx_multiple_sstores_same_slot |
Ensure BAL coalesces consecutive SSTOREs to the same slot within one tx into a single storage change with the final post-value | Contract executes SSTORE(0x01, 0xAA) + SSTORE(0x01, 0xBB) + SSTORE(0x01, 0xCC) in one tx. Parametrized by pre_value: slot_starts_empty (0x00), slot_starts_nonzero (0x11), intermediate_equals_pre (0xBB, where the second write transiently matches the pre-state). |
BAL MUST include contract with slot 0x01 having exactly one slot_changes entry: txIndex=1, post_value=0xCC. Intermediate values 0xAA and 0xBB MUST NOT appear as separate entries (enforced via absent_values). |
✅ Completed |
test_bal_intra_tx_sstores_same_slot_net_zero |
Ensure BAL filters net-zero result when multiple SSTOREs to the same slot occur within one tx | Parametrized: nonzero_pre_returns_to_pre (pre=0xCC, writes 0xAA→0xBB→0xCC) and empty_pre_ephemeral_writes (pre=0x00, writes 0xAA→0xBB→0x00). Final value equals pre-state in both cases. |
BAL MUST include contract with slot 0x01 in storage_reads (slot was accessed) and MUST NOT include slot 0x01 in storage_changes (net-zero). |
✅ Completed |
test_bal_create_contract_init_revert |
Ensure BAL correctly handles CREATE when parent call reverts | Caller calls factory, factory executes CREATE (succeeds), then factory REVERTs rolling back the CREATE | BAL MUST include Alice with nonce_changes. Caller and factory with no changes (reverted). Created contract address appears in BAL but MUST NOT have nonce_changes or code_changes (CREATE was rolled back). Contract address MUST NOT exist in post-state. |
✅ Completed |
test_bal_create_oog_code_deposit |
Ensure BAL correctly handles CREATE OOG during code deposit | Alice calls factory contract that executes CREATE with init code returning 10,000 bytes. Transaction has insufficient gas for code deposit. Factory nonce increments, CREATE returns 0 and stores in slot 1. | BAL MUST include Alice with nonce_changes. Factory with nonce_changes (incremented by CREATE) and storage_changes (slot 1 = 0). Contract address with empty changes (read during collision check). MUST NOT include nonce or code changes for contract address (rolled back on OOG). Contract address MUST NOT exist in post-state. |
✅ Completed |
test_bal_create_early_failure |
Ensure BAL correctly handles CREATE that fails before accessing contract address | Factory (balance=50) attempts CREATE(value=100). CREATE fails due to insufficient endowment (100 > 50). Factory stores CREATE result (0) in slot 0. | BAL MUST include Alice with nonce_changes. Factory with storage_changes (slot 0 = 0) but MUST NOT have nonce_changes (CREATE failed before nonce increment). Contract address MUST NOT appear in BAL (never accessed - CREATE failed before track_address). This is distinct from collision/OOG failures where contract address IS in BAL. |
✅ Completed |
test_bal_invalid_missing_nonce |
Verify clients reject blocks with BAL missing required nonce changes | Alice sends transaction to Bob; BAL modifier removes Alice's nonce change entry | Block MUST be rejected with INVALID_BLOCK_ACCESS_LIST exception. Clients MUST validate that all sender accounts have nonce changes recorded. |
✅ Completed |
test_bal_invalid_nonce_value |
Verify clients reject blocks with incorrect nonce values in BAL | Alice sends transaction to Bob; BAL modifier changes Alice's nonce to incorrect value | Block MUST be rejected with INVALID_BLOCK_ACCESS_LIST exception. Clients MUST validate nonce values match actual state transitions. |
✅ Completed |
test_bal_invalid_storage_value |
Verify clients reject blocks with incorrect storage values in BAL | Alice calls contract that writes to storage; BAL modifier changes storage value to incorrect value | Block MUST be rejected with INVALID_BLOCK_ACCESS_LIST exception. Clients MUST validate storage change values match actual state transitions. |
✅ Completed |
test_bal_invalid_tx_order |
Verify clients reject blocks with incorrect transaction indices in BAL | Alice sends transaction; BAL modifier swaps transaction indices incorrectly | Block MUST be rejected with INVALID_BLOCK_ACCESS_LIST exception. Clients MUST validate transaction ordering matches actual block execution order. |
✅ Completed |
test_bal_invalid_account |
Verify clients reject blocks with incorrect account addresses in BAL | Alice sends transaction; BAL modifier includes wrong account address | Block MUST be rejected with INVALID_BLOCK_ACCESS_LIST exception. Clients MUST validate all account addresses in BAL were actually accessed. |
✅ Completed |
test_bal_invalid_duplicate_account |
Verify clients reject blocks with duplicate account entries in BAL | Alice sends transaction; BAL modifier duplicates Alice's account entry | Block MUST be rejected with INVALID_BLOCK_ACCESS_LIST exception. Clients MUST ensure each account appears at most once in BAL. |
✅ Completed |
test_bal_invalid_account_order |
Verify clients reject blocks with incorrect account ordering in BAL | Alice sends transaction to Bob; BAL modifier reverses account order (BAL requires sorted order) | Block MUST be rejected with INVALID_BLOCK_ACCESS_LIST exception. Clients MUST validate accounts are in canonical sorted order. |
✅ Completed |
test_bal_invalid_complex_corruption |
Verify clients reject blocks with multiple BAL corruptions | Alice calls contract with storage writes; BAL has multiple issues: wrong account, missing nonce, wrong storage value | Block MUST be rejected with INVALID_BLOCK_ACCESS_LIST exception. Clients MUST detect any corruption regardless of other issues. |
✅ Completed |
test_bal_invalid_missing_account |
Verify clients reject blocks whose BAL omits an account that was touched during block execution | Parameterized test: balance_change — Alice sends a value transfer to Bob; BAL modifier removes Bob's entry (recipient had a balance change). access_only — Alice calls a contract that executes BALANCE against a target account without changing it; BAL modifier removes the target account's entry (accessed-but-unchanged). |
Block MUST be rejected with INVALID_BLOCK_ACCESS_LIST exception. Clients MUST validate all accessed accounts are present, including accessed-but-unchanged accounts with empty change lists. |
✅ Completed |
test_bal_invalid_balance_value |
Verify clients reject blocks with incorrect balance values in BAL | Alice sends value to Bob; BAL modifier changes balance to incorrect value | Block MUST be rejected with INVALID_BLOCK_ACCESS_LIST exception. Clients MUST validate balance change values match actual state transitions. |
✅ Completed |
test_bal_empty_block_no_coinbase |
Ensure BAL correctly handles empty blocks without including coinbase | Block with 0 transactions, no withdrawals. System contracts may perform operations (EIP-2935 parent hash, EIP-4788 beacon root if active). | BAL MUST NOT include the coinbase/fee recipient (receives no fees). BAL MAY include system contract addresses (EIP-2935 HISTORY_STORAGE_ADDRESS, EIP-4788 BEACON_ROOTS_ADDRESS) with storage_changes at block_access_index=0 (pre-execution system operations). |
✅ Completed |
test_bal_coinbase_zero_tip |
Ensure BAL includes coinbase even when priority fee is zero | Block with 1 transaction: Alice sends 5 wei to Bob with priority fee = 0 (base fee burned post-EIP-1559) | BAL MUST include Alice with balance_changes (gas cost) and nonce_changes. BAL MUST include Bob with balance_changes. BAL MUST include coinbase with empty changes. |
✅ Completed |
test_bal_withdrawal_empty_block |
Ensure BAL captures withdrawal balance changes in empty block | Charlie starts with 1 gwei. Block with 0 transactions and 1 withdrawal of 10 gwei to Charlie | BAL MUST include Charlie with balance_changes at block_access_index = 1. Charlie's balance_changes MUST show final balance of 11 gwei. All other fields (storage_reads, storage_changes, nonce_changes, code_changes) MUST be empty. |
✅ Completed |
test_bal_withdrawal_and_transaction |
Ensure BAL captures both transaction and withdrawal balance changes | Block with 1 transaction: Alice sends 5 wei to Bob. 1 withdrawal of 10 gwei to Charlie | BAL MUST include Alice with nonce_changes and balance_changes at block_access_index = 1. BAL MUST include Bob with balance_changes at block_access_index = 1. BAL MUST include Charlie with balance_changes at block_access_index = 2 showing final balance after receiving 10 gwei. All other fields for Charlie MUST be empty. |
✅ Completed |
test_bal_withdrawal_to_nonexistent_account |
Ensure BAL captures withdrawal to non-existent account | Block with 1 withdrawal of 10 gwei to non-existent account Charlie | BAL MUST include Charlie with balance_changes at block_access_index = 1 showing final balance of 10 gwei. All other fields (storage_reads, storage_changes, nonce_changes, code_changes) MUST be empty. |
✅ Completed |
test_bal_withdrawal_no_evm_execution |
Ensure BAL captures withdrawal without triggering EVM execution | Contract Oracle with storage slot 0x01 = 0x42. Oracle code writes to slot 0x01 when called. Block with 1 withdrawal of 10 gwei to Oracle |
BAL MUST include Oracle with balance_changes at block_access_index = 1 showing final balance after receiving 10 gwei. Storage slot 0x01 MUST remain 0x42 and all other fields (storage_reads, storage_changes, nonce_changes, code_changes) MUST be empty. |
✅ Completed |
test_bal_withdrawal_and_state_access_same_account |
Ensure BAL captures both state access and withdrawal to same address | Contract Oracle with storage slot 0x01 = 0x42. Block with 1 transaction: Alice calls Oracle (reads from slot 0x01, writes to slot 0x02). 1 withdrawal of 10 gwei to Oracle |
BAL MUST include Oracle with storage_reads for slot 0x01 and storage_changes for slot 0x02 at block_access_index = 1. Oracle MUST also have balance_changes at block_access_index = 2 showing final balance after receiving 10 gwei. Both state access and withdrawal MUST be captured. |
✅ Completed |
test_bal_withdrawal_and_value_transfer_same_address |
Ensure BAL captures both transaction value transfer and withdrawal to same address | Block with 1 transaction: Alice sends 5 gwei to Bob. 1 withdrawal of 10 gwei to Bob | BAL MUST include Alice with nonce_changes and balance_changes at block_access_index = 1. BAL MUST include Bob with balance_changes at block_access_index = 1 showing balance after receiving 5 gwei. Bob MUST also have balance_changes at block_access_index = 2 showing balance after receiving 10 gwei withdrawal. Bob's final post-state balance MUST be 15 gwei (cumulative). |
✅ Completed |
test_bal_multiple_withdrawals_same_address |
Ensure BAL accumulates multiple withdrawals to same address | Block with 3 withdrawals to Charlie: 5 gwei, 10 gwei, 15 gwei | BAL MUST include Charlie with balance_changes at block_access_index = 1 showing final balance of 30 gwei. All other fields (storage_reads, storage_changes, nonce_changes, code_changes) MUST be empty. |
✅ Completed |
test_bal_withdrawal_and_selfdestruct |
Ensure BAL captures withdrawal to self-destructed contract address | Contract Oracle with 100 gwei balance. Block with 1 transaction: Oracle self-destructs sending balance to Bob. 1 withdrawal of 50 gwei to Oracle's address |
BAL MUST include Oracle with balance_changes showing 0 balance at block_access_index = 1 (after self-destruct). BAL MUST include Bob with balance_changes showing 100 gwei received from self-destruct at block_access_index = 1. Oracle MUST also have balance_changes at block_access_index = 2 showing 50 gwei after withdrawal. Both self-destruct and withdrawal MUST be captured. |
✅ Completed |
test_bal_withdrawal_and_new_contract |
Ensure BAL captures withdrawal to newly created contract | Block with 1 transaction: Alice deploys contract Oracle with 5 gwei initial balance. 1 withdrawal of 10 gwei to Oracle |
BAL MUST include Oracle with code_changes and balance_changes showing 5 gwei at block_access_index = 1. Oracle MUST also have balance_changes at block_access_index = 2 showing balance after receiving 10 gwei withdrawal. Oracle's final post-state balance MUST be 15 gwei (cumulative). |
✅ Completed |
test_bal_zero_withdrawal |
Ensure BAL handles zero-amount withdrawal correctly | Block with 0 transactions and 1 zero-amount withdrawal (0 gwei) to Charlie. Two variations: Charlie has existing balance (5 gwei) or Charlie is non-existent. | BAL MUST include Charlie at block_access_index = 1 with empty changes. Balance remains unchanged. |
✅ Completed |
test_bal_withdrawal_to_precompiles |
Ensure BAL captures withdrawal to precompile addresses | Block with 1 withdrawal of 10 gwei to precompile address (all precompiles) | BAL MUST include precompile address with balance_changes at block_access_index = 1 showing final balance of 10 gwei. All other fields (storage_reads, storage_changes, nonce_changes, code_changes) MUST be empty. |
✅ Completed |
test_bal_withdrawal_largest_amount |
Ensure BAL captures withdrawal with largest amount | Block with 1 withdrawal of maximum uint64 value (2^64-1 gwei) to Charlie | BAL MUST include Charlie with balance_changes at block_access_index = 1 showing final balance of (2^64-1) * 10^9 wei. All other fields (storage_reads, storage_changes, nonce_changes, code_changes) MUST be empty. |
✅ Completed |
test_bal_withdrawal_to_coinbase |
Ensure BAL captures withdrawal to coinbase address | Block with 1 transaction: Alice sends 5 wei to Bob. 1 withdrawal of 10 gwei to coinbase/fee recipient | BAL MUST include coinbase with balance_changes at block_access_index = 1 showing balance after transaction fees. Coinbase MUST also have balance_changes at block_access_index = 2 showing balance after receiving 10 gwei withdrawal. Coinbase's final post-state balance MUST include both transaction fees and withdrawal. |
✅ Completed |
test_bal_withdrawal_to_coinbase_empty_block |
Ensure BAL captures withdrawal to coinbase even when there are no transactions (no fees) | Block with 0 transactions and 1 withdrawal of 10 gwei to coinbase/fee recipient | BAL MUST include coinbase with balance_changes at block_access_index = 1 showing final balance of 10 gwei. All other fields (storage_reads, storage_changes, nonce_changes, code_changes) MUST be empty. |
✅ Completed |
test_bal_nonexistent_value_transfer |
Ensure BAL captures non-existent account on value transfer | Alice sends value (0 wei or 1 ETH) to non-existent account Bob (address never funded or accessed before) via direct transfer | For zero value: BAL MUST include Alice with nonce_changes and Bob (non-existent) with empty changes. For positive value: BAL MUST include Bob with balance_changes showing received amount. |
✅ Completed |
test_bal_nonexistent_account_access_read_only |
Ensure BAL captures non-existent account accessed via read-only account-reading opcodes | Alice calls Oracle contract which uses read-only account access opcodes (BALANCE, EXTCODESIZE, EXTCODECOPY, EXTCODEHASH, STATICCALL, DELEGATECALL) on non-existent account Bob. |
BAL MUST include Alice with nonce_changes, Oracle with empty changes, and Bob with empty changes (account accessed but no state modifications). |
✅ Completed |
test_bal_nonexistent_account_access_value_transfer |
Ensure BAL captures non-existent account accessed via CALL/CALLCODE with value transfers | Alice calls Oracle contract which uses CALL or CALLCODE on non-existent account Bob. Tests both zero and positive value transfers. |
BAL MUST include Alice with nonce_changes. For CALL with positive value: Oracle with balance_changes (loses value), Bob with balance_changes (receives value). For CALLCODE with value or zero value transfers: Oracle and Bob with empty changes (CALLCODE self-transfer = net zero). |
✅ Completed |
test_bal_storage_write_read_same_frame |
Ensure BAL captures write precedence over read in same call frame (writes shadow reads) | Alice calls Oracle which writes (SSTORE) value 0x42 to slot 0x01, then reads (SLOAD) from slot 0x01 in the same call frame |
BAL MUST include Oracle with slot 0x01 in storage_changes showing final value 0x42. Slot 0x01 MUST NOT appear in storage_reads (write shadows the subsequent read in same frame). |
✅ Completed |
test_bal_storage_write_read_cross_frame |
Ensure BAL captures write precedence over read across call frames (writes shadow reads cross-frame) | Alice calls Oracle. First call reads slot 0x01 (sees initial value), writes 0x42 to slot 0x01, then calls itself (via CALL, DELEGATECALL, or CALLCODE). Second call reads slot 0x01 (sees 0x42) and exits. |
BAL MUST include Oracle with slot 0x01 in storage_changes showing final value 0x42. Slot 0x01 MUST NOT appear in storage_reads (write shadows both the read before it in same frame and the read in the recursive call). |
✅ Completed |
test_bal_create_transaction_empty_code |
Ensure BAL does not record spurious code changes for CREATE transaction deploying empty code | Alice sends CREATE transaction with empty initcode (deploys code b""). Contract address gets nonce = 1 and code = b"". |
BAL MUST include Alice with nonce_changes and created contract with nonce_changes but MUST NOT include code_changes for contract (setting b"" -> b"" is net-zero). |
✅ Completed |
test_bal_cross_block_ripemd160_state_leak |
Ensure internal EVM state for precompile handling does not leak between blocks | Block 1: Alice calls RIPEMD-160 (0x03) with zero value (RIPEMD-160 must be pre-funded). Block 2: Bob's transaction triggers an exception (stack underflow). | BAL for Block 1 MUST include RIPEMD-160. BAL for Block 2 MUST NOT include RIPEMD-160 (never accessed in Block 2). Internal state from Parity Touch Bug (EIP-161) handling must be reset between blocks. | ✅ Completed |
test_bal_all_transaction_types |
Ensure BAL correctly captures state changes from all transaction types in a single block | Single block with 5 transactions: Type 0 (Legacy), Type 1 (EIP-2930 Access List), Type 2 (EIP-1559), Type 3 (EIP-4844 Blob), Type 4 (EIP-7702 Set Code). Each tx writes to contract storage. Note: Access list addresses are pre-warmed but NOT recorded in BAL (no state access). | BAL MUST include: (1) All 5 senders with nonce_changes. (2) Contracts 0-3 with storage_changes. (3) Alice (7702 target) with nonce_changes, code_changes (delegation), storage_changes. (4) Oracle (delegation source) with empty changes. |
✅ Completed |
test_bal_create2_collision |
Ensure BAL handles CREATE2 address collision correctly | Factory contract (nonce=1, storage slot 0=0xDEAD) executes CREATE2(salt=0, initcode) targeting address that already has code=STOP, nonce=1. Pre-deploy contract at calculated CREATE2 target address before factory deployment. |
BAL MUST include: (1) Factory with nonce_changes (1→2, incremented even on failed CREATE2), storage_changes for slot 0 (0xDEAD→0, stores failure). (2) Collision address with empty changes (accessed during collision check, no state changes). CREATE2 returns 0. Collision address MUST NOT have nonce_changes or code_changes. |
✅ Completed |
test_bal_create_selfdestruct_to_self_with_call |
Ensure BAL handles init code that calls external contract then selfdestructs to itself | Factory executes CREATE2 with endowment=100. Init code (embedded in factory via CODECOPY): (1) CALL(Oracle, 0) - Oracle writes to its storage slot 0x01. (2) SSTORE(0x01, 0x42) - write to own storage. (3) SELFDESTRUCT(SELF) - selfdestruct to own address. Contract created and destroyed in same tx. |
BAL MUST include: (1) Factory with nonce_changes, balance_changes (loses 100). (2) Oracle with storage_changes for slot 0x01 (external call succeeded). (3) Created address with storage_reads for slot 0x01 (aborted write becomes read) - MUST NOT have nonce_changes, code_changes, storage_changes, or balance_changes (ephemeral contract, balance burned via SELFDESTRUCT to self). |
✅ Completed |
test_bal_selfdestruct_to_7702_delegation |
Ensure BAL correctly handles SELFDESTRUCT to a 7702 delegated account (no code execution on recipient) | Tx1: Alice authorizes delegation to Oracle (sets code to 0xef0100\|\|Oracle). Tx2: Victim contract (balance=100) executes SELFDESTRUCT(Alice). Two separate transactions in same block. Note: Alice starts with initial balance which accumulates with selfdestruct. |
BAL MUST include: (1) Alice at block_access_index=1 with code_changes (delegation), nonce_changes. (2) Alice at block_access_index=2 with balance_changes (receives selfdestruct). (3) Victim at block_access_index=2 with balance_changes (100→0). Oracle MUST NOT appear in tx2 - per EVM spec, SELFDESTRUCT transfers balance without executing recipient code, so delegation target is never accessed. |
✅ Completed |
test_bal_call_revert_insufficient_funds |
Ensure BAL handles value-transferring call failure due to insufficient balance (not OOG), with and without 7702 delegation | Caller contract (balance=100, storage slot 0x02=0xDEAD) executes: SLOAD(0x01), call_opcode(target, value=1000), SSTORE(0x02, result). The call fails because 1000 > 100. Parametrized: (1) call_opcode over CALL and CALLCODE via with_all_call_opcodes(selector=...), (2) delegated (target is plain EOA vs. 7702-delegated EOA pointing to delegation_target=STOP), (3) target_is_warm (cold/warm via EIP-2930 access list), (4) delegation_is_warm (only when delegated). |
BAL MUST include: (1) Caller with storage_reads for slot 0x01, storage_changes for slot 0x02 (value=0, call returned failure). (2) Target with empty changes - accessed before balance check fails. (3) When delegated: delegation_target with empty changes - delegation is loaded before balance check fails (unlike the OOG cases in test_bal_call_7702_delegation_and_oog where the static-check optimization avoids the delegation load). Access-list warming does NOT add to BAL on its own, so the BAL is identical across warm/cold variants. |
✅ Completed |
test_bal_lexicographic_address_ordering |
Ensure BAL enforces strict lexicographic byte-wise ordering | Pre-fund three addresses with specific byte patterns: addr_low = 0x0000...0001, addr_mid = 0x0000...0100, addr_high = 0x0100...0000. Contract touches them in reverse order: BALANCE(addr_high), BALANCE(addr_low), BALANCE(addr_mid). Additionally, include two endian-trap addresses that are byte-reversals of each other: addr_endian_low = 0x0100000000000000000000000000000000000002, addr_endian_high = 0x0200000000000000000000000000000000000001. Note: reverse(addr_endian_low) = addr_endian_high. Correct lexicographic order: addr_endian_low < addr_endian_high (0x01 < 0x02 at byte 0). If implementation incorrectly reverses bytes before comparing, it would get addr_endian_low > addr_endian_high (wrong). |
BAL account list MUST be sorted lexicographically by address bytes: addr_low < addr_mid < addr_high < addr_endian_low < addr_endian_high, regardless of access order. The endian-trap addresses specifically catch byte-reversal bugs where addresses are compared with wrong byte order. Complements test_bal_invalid_account_order which tests rejection; this tests correct generation. |
✅ Completed |
test_bal_gas_limit_boundary |
Ensure BAL max items gas limit boundary is enforced for empty blocks | Empty block with gas limit set to the exact boundary where bal_items <= block_gas_limit // GAS_BLOCK_ACCESS_LIST_ITEM. Parameterized: (1) at_boundary (gas limit = exact minimum, passes), (2) below_boundary (gas limit = exact minimum - 1, fails). |
At boundary: block MUST be accepted. Below boundary: block MUST be rejected with BLOCK_ACCESS_LIST_GAS_LIMIT_EXCEEDED exception. |
✅ Completed |
test_bal_transient_storage_not_tracked |
Ensure BAL excludes EIP-1153 transient storage operations | Contract executes: TSTORE(0x01, 0x42) (transient write), TLOAD(0x01) (transient read), SSTORE(0x02, result) (persistent write using transient value). |
BAL MUST include slot 0x02 in storage_changes (persistent storage was modified). BAL MUST NOT include slot 0x01 in storage_reads or storage_changes (transient storage is not persisted, not needed for stateless execution). This verifies TSTORE/TLOAD don't pollute BAL. |
✅ Completed |
test_bal_withdrawal_to_7702_delegation |
Ensure BAL correctly handles withdrawal to a 7702 delegated account (no code execution on recipient) | Tx1: Alice authorizes delegation to Oracle (sets code to 0xef0100\|\|Oracle). Withdrawal: 10 gwei sent to Alice. Single block with tx + withdrawal. |
BAL MUST include: (1) Alice at block_access_index=1 with code_changes (delegation), nonce_changes. (2) Alice at block_access_index=2 with balance_changes (receives withdrawal). Oracle MUST NOT appear - withdrawals credit balance without executing recipient code, so delegation target is never accessed. This complements test_bal_selfdestruct_to_7702_delegation (selfdestruct) and test_bal_withdrawal_no_evm_execution (withdrawal to contract). |
✅ Completed |
test_init_collision_create_tx |
Ensure BAL tracks CREATE collisions correctly (pre-Amsterdam test with BAL) | CREATE transaction targeting address with existing storage aborts | BAL MUST show empty expectations for collision address (no changes occur due to abort) | ✅ Completed |
test_call_to_pre_authorized_oog |
Ensure BAL handles OOG during EIP-7702 delegation access (pre-Amsterdam test with BAL) | Call to delegated account that OOGs before accessing delegation contract | BAL MUST include auth_signer (code read for delegation check) but MUST NOT include delegation contract (OOG before access) | ✅ Completed |
test_selfdestruct_created_in_same_tx_with_revert |
Ensure BAL tracks selfdestruct with revert correctly (pre-Amsterdam test with BAL) | Contract created and selfdestructed in same tx with nested revert | BAL MUST track storage reads and balance changes for selfdestruct even with reverts | ✅ Completed |
test_value_transfer_gas_calculation |
Ensure BAL correctly tracks OOG scenarios for CALL/CALLCODE/DELEGATECALL/STATICCALL (pre-Amsterdam test with BAL) | Nested calls with precise gas limits to test OOG behavior. For CALL with OOG: target account is read. For CALLCODE/DELEGATECALL/STATICCALL with OOG: target account NOT read (OOG before state access) | For CALL: target in BAL even with OOG. For CALLCODE/DELEGATECALL/STATICCALL: target NOT in BAL when OOG (state access deferred until after gas check) | ✅ Completed |
test_bal_call_with_value_in_static_context |
Ensure BAL does NOT include target when CALL with value fails in static context | static_caller uses STATICCALL to call caller. caller attempts CALL(target, value=1) which must fail due to static context. Target is an empty account. |
BAL MUST NOT include target because static context check (is_static && value > 0) must happen BEFORE any account access or BAL tracking. BAL MUST include static_caller with storage_changes (STATICCALL succeeded), caller with empty changes. |
✅ Completed |
test_staticcall_reentrant_call_to_precompile |
Ensure BAL captures STATICCALL reentry with CALL to precompile | Contract STATICCALLs itself. On reentry (CALLVALUE=0), attempts CALL to precompile with parametrized value. File: tests/byzantium/eip214_staticcall/test_staticcall.py. |
call_value=0: target with storage_changes (slot 0=1), precompile with empty changes. call_value>0: target with storage_reads (slot 0), precompile NOT in BAL (reverted before accessed). |
✅ Completed |
test_staticcall_call_to_precompile |
Ensure BAL captures STATICCALL → CALL to precompile chain | Contract A STATICCALLs contract B. B attempts CALL to precompile. File: tests/byzantium/eip214_staticcall/test_staticcall.py. |
call_value=0: contract_a with markers, contract_b empty (STATICCALLed), precompile empty. call_value>0: contract_a with storage_reads for slot 1, precompile NOT in BAL. |
✅ Completed |
test_staticcall_nested_call_to_precompile |
Ensure BAL captures nested CALL → STATICCALL → CALL to precompile | Contract B CALLs A. A STATICCALLs C. C attempts CALL to precompile. File: tests/byzantium/eip214_staticcall/test_staticcall.py. |
call_value=0: all contracts with markers/empty, precompile empty. call_value>0: contract_a with storage_reads for slot 1, precompile NOT in BAL. |
✅ Completed |
test_staticcall_call_to_precompile_from_contract_init |
Ensure BAL captures STATICCALL to precompile during CREATE init | Contract A CREATEs contract. Init code STATICCALLs B which CALLs precompile. File: tests/byzantium/eip214_staticcall/test_staticcall.py. |
call_value=0: contract_a with markers/nonce, created_contract with markers/nonce, contract_b empty, precompile empty. call_value>0: created_contract with storage_reads for slot 1, precompile NOT in BAL. |
✅ Completed |
test_bal_4788_simple |
Ensure BAL captures beacon root storage writes during pre-execution system call | Block with 2 normal user transactions: Alice sends 10 wei to Charlie, Bob sends 10 wei to Charlie. At block start (pre-execution), SYSTEM_ADDRESS calls BEACON_ROOTS_ADDRESS to store parent beacon root |
BAL MUST include at block_access_index=0: BEACON_ROOTS_ADDRESS with two storage_changes (timestamp slot and beacon root slot); SYSTEM_ADDRESS MUST NOT be included in BAL. At block_access_index=1: Alice with nonce_changes, Charlie with balance_changes (10 wei). At block_access_index=2: Bob with nonce_changes, Charlie with balance_changes (20 wei total). |
✅ Completed |
test_bal_4788_empty_block |
Ensure BAL captures beacon root storage writes in empty block | Block with no transactions. At block start (pre-execution), SYSTEM_ADDRESS calls BEACON_ROOTS_ADDRESS to store parent beacon root |
BAL MUST include at block_access_index=0: BEACON_ROOTS_ADDRESS with two storage_changes (timestamp slot and beacon root slot); SYSTEM_ADDRESS MUST NOT be included in BAL. No transaction-related BAL entries. |
✅ Completed |
test_bal_4788_query |
Ensure BAL captures storage reads when querying beacon root (valid and invalid queries) with optional value transfer | Parameterized test: Block 1 stores beacon root at timestamp 12. Block 2 queries with three timestamp scenarios (valid=12, invalid non-zero=42, invalid zero=0) and value (0 or 100 wei). Valid query (timestamp=12): reads both timestamp and root slots, writes returned value. If value > 0, beacon root contract receives balance. Invalid query with non-zero timestamp (timestamp=42): reads only timestamp slot before reverting, query contract has implicit SLOAD recorded (SSTORE reverts), no value transferred. Invalid query with zero timestamp (timestamp=0): reverts immediately without any storage access, query contract has implicit SLOAD recorded, no value transferred. | Block 1 BAL: System call writes. Block 2 BAL MUST include at block_access_index=0: System call writes for block 2. Valid case (timestamp=12) at block_access_index=1: BEACON_ROOTS_ADDRESS with storage_reads [timestamp_slot, root_slot] and balance_changes if value > 0, query contract with storage_changes. Invalid non-zero case (timestamp=42) at block_access_index=1: BEACON_ROOTS_ADDRESS with storage_reads [timestamp_slot only] and NO balance_changes (reverted), query contract with storage_reads [0] and NO storage_changes. Invalid zero case (timestamp=0) at block_access_index=1: BEACON_ROOTS_ADDRESS with NO storage_reads (reverts before access) and NO balance_changes, query contract with storage_reads [0] and NO storage_changes. |
✅ Completed |
test_bal_4788_invalid_calldata_size |
Ensure BAL correctly handles EIP-4788 queries with invalid calldata size | Parameterized test: Query contract calls BEACON_ROOTS_ADDRESS with variable-size calldata (0, 31, or 33 bytes) and value (0 or 100 wei). EIP-4788 requires exactly 32 bytes of calldata; any other size reverts before any storage access. |
BAL MUST include at block_access_index=0: BEACON_ROOTS_ADDRESS with storage_changes from system call. At block_access_index=1: BEACON_ROOTS_ADDRESS with NO storage_reads (reverts before access) and NO balance_changes (value not transferred), query contract with storage_reads [0] (implicit from no-op SSTORE) and NO storage_changes. If value > 0, query contract retains balance. Alice with nonce_changes. |
✅ Completed |
test_bal_4788_selfdestruct_to_beacon_root |
Ensure BAL captures SELFDESTRUCT to beacon root address alongside system call storage writes |
Single block: Pre-execution system call writes beacon root to storage. Transaction: Alice calls contract (pre-funded with 100 wei) that selfdestructs with BEACON_ROOTS_ADDRESS as beneficiary. |
BAL MUST include at block_access_index=0: BEACON_ROOTS_ADDRESS with storage_changes (timestamp and root slots from system call). At block_access_index=1: Alice with nonce_changes, contract with balance_changes (100→0), BEACON_ROOTS_ADDRESS with balance_changes (receives 100 wei). |
✅ Completed |
test_selfdestruct_send_to_sender |
Ensure BAL tracks SELFDESTRUCT sending all funds back to the tx sender (no burn). Multi-fork: valid_from TangerineWhistle, BAL expectations applied from Amsterdam via is_eip_enabled(7928). File: tests/tangerine_whistle/eip150_operation_gas_costs/test_eip150_selfdestruct.py. |
Parametrized over originator_balance: [0, 100]. Pre-deployed contract victim with code=SELFDESTRUCT(CALLER) and the given balance. Alice (tx.sender) sends tx directly to victim; no intermediate caller. Pre-Cancun: victim destroyed. >=Cancun (EIP-6780, non-same-tx): victim preserved with balance 0. |
When EIP-7928 is enabled, BAL MUST include Alice with nonce_changes coalesced with balance_changes (Alice is both sender and beneficiary). Victim has balance_changes 100→0 when funded, otherwise empty changes — and MUST NOT have code_changes or nonce_changes. |
✅ Completed |
test_bal_7002_clean_sweep |
Ensure BAL correctly tracks "clean sweep" where all withdrawal requests are dequeued in same block (requests ≤ MAX). Parameterized: (1) pubkey first 32 bytes zero / non-zero, (2) amount zero / non-zero | Alice sends transaction to WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS with 1 withdrawal request. Validator pubkey has either first 32 bytes zero or non-zero. Amount is either zero or non-zero. Since 1 ≤ MAX_WITHDRAWAL_REQUESTS_PER_BLOCK, post-execution system call dequeues all requests ("clean sweep"), resetting head and tail to 0. |
BAL MUST include Alice with nonce_changes at block_access_index=1. WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS MUST have: balance_changes at block_access_index=1 (receives fee), storage_reads for excess, head, and slot 5 (first 32 bytes of pubkey) if zero. At block_access_index=1 (tx enqueue): storage_changes for count (0→1), tail (0→1), slot 4 (source address), slot 5 (first 32 bytes, ONLY if non-zero), slot 6. At block_access_index=2 (post-exec dequeue): storage_changes for count (1→0), tail (1→0). Clean sweep invariant: when all requests dequeued, both head and tail reset to 0. |
✅ Completed |
test_bal_7002_partial_sweep |
Ensure BAL correctly tracks queue overflow when requests exceed MAX, demonstrating partial sweep in block 1 and cleanup in block 2 | Block 1: 20 different EOAs each send withdrawal request to WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS. Since 20 > MAX_WITHDRAWAL_REQUESTS_PER_BLOCK, only first MAX requests dequeued ("partial sweep"), leaving 4 in queue. Block 2: Empty block (no transactions), remaining 4 requests dequeued ("clean sweep"), queue becomes empty. |
Block 1 BAL MUST include all 20 senders with nonce_changes at respective block_access_index (1-20). WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS at each tx: storage_changes for count (increments to 20), tail (increments to 20). At block_access_index=21 (post-exec partial dequeue): storage_changes for count (20→0), head (0→MAX). Partial sweep: head advances by MAX, tail stays 20, queue has 4 remaining (tail - head = 4). Block 2 BAL MUST include WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS at block_access_index=1 (post-exec clean sweep): storage_changes for head (MAX→0), tail (20→0). Clean sweep: both head and tail reset to 0, queue empty. |
✅ Completed |
test_bal_7002_no_withdrawal_requests |
Ensure BAL captures EIP-7002 system contract dequeue operation even when block has no withdrawal requests | Block with 1 transaction: Alice sends 10 wei to Bob. No withdrawal requests submitted. | BAL MUST include Alice with nonce_changes at block_access_index=1. BAL MUST include Bob with balance_changes at block_access_index=1. BAL MUST include EIP-7002 system contract (WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS) with storage_reads for slots: excess (slot 0), count (slot 1), head (slot 2), tail (slot 3). System contract MUST NOT have storage_changes (no writes occur when queue is empty). This test demonstrates that the post-execution dequeue operation always runs and reads queue state, even when no requests are present. |
✅ Completed |
test_bal_7002_request_from_contract |
Ensure BAL captures withdrawal request from contract with correct source address | Alice calls RelayContract which internally calls EIP-7002 system contract with withdrawal request. Withdrawal request should have source_address = RelayContract (not Alice). |
BAL MUST include Alice with nonce_changes at block_access_index=1. BAL MUST include RelayContract with balance_changes (fee paid to system contract) at block_access_index=1. BAL MUST include system contract with balance_changes, storage_reads, and storage_changes (queue modified). Source address in withdrawal request MUST be RelayContract. Clean sweep: count and tail reset to 0 at block_access_index=2. |
✅ Completed |
test_bal_7002_request_invalid |
Ensure BAL correctly handles invalid withdrawal request scenarios | Parameterized test with 8 invalid scenarios: (1) insufficient_fee (fee=0), (2) calldata_too_short (55 bytes), (3) calldata_too_long (57 bytes), (4) oog (insufficient gas), (5-7) invalid_call_type (DELEGATECALL/STATICCALL/CALLCODE), (8) contract_reverts. Tests both EOA and contract-based withdrawal requests. | BAL MUST include sender with nonce_changes at block_access_index=1. BAL MUST include system contract with storage_reads for slots: excess (slot 0), count (slot 1), head (slot 2), tail (slot 3). System contract MUST NOT have storage_changes (transaction failed, no queue modification). |
✅ Completed |
test_bal_invalid_extraneous_entries |
Verify clients reject blocks with any type of extraneous BAL entries | Alice sends 100 wei to Oracle contract (which reads storage slot 0). Charlie is uninvolved in this transaction. A valid BAL is created containing nonce change for Alice, balance change and storage read for Oracle. The BAL is corrupted by adding various extraneous entries: (1) extra_nonce, (2) extra_balance, (3) extra_code, (4) extra_storage_write_touched (slot 0 - already read), (5) extra_storage_write_untouched (slot 1 - not accessed), (6) extra_storage_write_uninvolved_account (Charlie - uninvolved account), (7) extra_account_access (Charlie), (8) extra_storage_read (slot 999). Each tested at block_access_index 1 (same tx), 2 (system tx), 3 (out of bounds). | Block MUST be rejected with INVALID_BLOCK_ACCESS_LIST exception. Clients MUST detect any extraneous entries in BAL. |
✅ Completed |
test_bal_invalid_duplicate_entries |
Verify clients reject blocks where BAL violates uniqueness constraints | Oracle writes storage, reads storage, and CREATEs a contract. BAL is corrupted with duplicate entries: (1) duplicate_nonce_change, (2) duplicate_balance_change, (3) duplicate_code_change, (4) duplicate_storage_slot, (5) duplicate_storage_read, (6) duplicate_slot_change, (7) storage_key_in_both_changes_and_reads. | Block MUST be rejected with INVALID_BLOCK_ACCESS_LIST exception. Each block_access_index must appear at most once per change list, each storage key at most once in storage_changes and storage_reads, and no key in both. |
✅ Completed |
test_bal_invalid_missing_withdrawal_account |
Verify clients reject blocks where BAL is missing an account modified only by a withdrawal | Alice sends 5 wei to Bob (1 transaction). Charlie receives 10 gwei withdrawal. BAL modifier removes Charlie's entry entirely. | Block MUST be rejected with INVALID_BLOCK_ACCESS_LIST exception. Clients MUST detect that Charlie's balance was modified by the withdrawal but has no corresponding BAL entry. |
✅ Completed |
test_bal_invalid_missing_withdrawal_account_empty_block |
Verify clients reject blocks where BAL is missing a withdrawal-modified account in an empty block | Charlie receives 10 gwei withdrawal in block with no transactions. BAL modifier removes Charlie's entry entirely. | Block MUST be rejected with INVALID_BLOCK_ACCESS_LIST exception. Clients MUST detect withdrawal-modified accounts even when no transactions are present. |
✅ Completed |
test_bal_invalid_hash_mismatch |
Verify clients reject blocks where the BAL hash in the header does not match the actual BAL content | Alice sends value to Bob. BAL content is valid but header hash is overridden to a wrong value via rlp_modifier. Unlike other invalid BAL tests (which corrupt BAL content with matching hash), this keeps the BAL valid but injects a wrong header hash. |
Block MUST be rejected with INVALID_BAL_HASH or INVALID_BLOCK_HASH exception. Clients MUST re-derive the BAL from block execution and compare its hash to the header, not just verify the BAL content is self-consistent. |
✅ Completed |
test_bal_invalid_field_entries |
Verify clients reject blocks with missing or incorrect field-level BAL entries | Oracle writes storage slot 1, reads storage slot 2, and CREATEs a small contract. A valid BAL is created, then corrupted by parameterized modifier: (1) missing_storage_change: Oracle's storage writes removed, (2) missing_storage_read: Oracle's storage reads removed, (3) missing_code_change: created contract's code change removed, (4) wrong_code_value: created contract's deployed bytecode changed to wrong value. | Block MUST be rejected with INVALID_BLOCK_ACCESS_LIST exception. Clients MUST validate all field-level BAL entries: storage writes, storage reads, and code changes. |
✅ Completed |
test_bal_invalid_withdrawal_balance_value |
Verify clients reject blocks where BAL has an incorrect balance value for a withdrawal-modified account | Charlie receives 10 gwei withdrawal in an empty block. BAL modifier changes Charlie's post-balance from the correct 10_000_000_000 wei to 999. | Block MUST be rejected with INVALID_BLOCK_ACCESS_LIST exception. Clients MUST validate withdrawal balance values match actual withdrawal amounts (including gwei-to-wei conversion). |
✅ Completed |
test_bal_invalid_missing_coinbase |
Verify clients reject blocks where BAL is missing the coinbase/fee recipient | Alice sends 100 wei to Bob with gas_price > base_fee so coinbase (charlie) receives a non-zero tip. BAL modifier removes charlie's entry entirely. | Block MUST be rejected with INVALID_BLOCK_ACCESS_LIST exception. Clients MUST include fee recipients that received tips in the BAL. |
✅ Completed |
test_bal_invalid_coinbase_balance_value |
Verify clients reject blocks where BAL has an incorrect balance for the coinbase/fee recipient | Same setup as test_bal_invalid_missing_coinbase. BAL modifier changes charlie's post-balance from the actual tip to 999. | Block MUST be rejected with INVALID_BLOCK_ACCESS_LIST exception. Clients MUST validate coinbase balance values match actual fee accounting (priority fee x gas used). |
✅ Completed |
test_bal_invalid_extraneous_coinbase |
Verify clients reject blocks with a spurious coinbase entry when coinbase received no fees | Parameterized: (1) empty_block: no txs, no withdrawals — only system contracts in valid BAL, (2) withdrawal_only: no txs, one withdrawal to a different address — withdrawals don't pay fees so coinbase is still untouched. BAL modifier appends spurious coinbase entry with empty changes. | Block MUST be rejected with INVALID_BLOCK_ACCESS_LIST exception. Coinbase MUST NOT appear in BAL when it receives no transaction tips, even if the block has other state-modifying activity (withdrawals). |
✅ Completed |
test_bal_invalid_surplus_system_address_from_system_call |
Verify clients reject a BAL containing SYSTEM_ADDRESS solely due to a system operation caller |
Empty block with an EIP-4788 pre-execution system call to BEACON_ROOTS_ADDRESS. Helper beacon_root_system_call_expectations builds the valid baseline BAL: BEACON_ROOTS_ADDRESS has timestamp/root storage changes at block_access_index=0, while SYSTEM_ADDRESS is marked absent (None). The fixture BAL is then corrupted by appending an empty SYSTEM_ADDRESS entry. |
Block MUST be rejected with INVALID_BLOCK_ACCESS_LIST. The synthetic system caller address MUST NOT be accepted unless it experienced an actual state access. |
✅ Completed |
test_bal_2935_simple |
Ensure BAL captures EIP-2935 history storage writes during pre-execution system call alongside normal transactions | Block with 2 normal user transactions: Alice sends 10 wei to Charlie, Bob sends 10 wei to Charlie. At block start (pre-execution), SYSTEM_ADDRESS calls HISTORY_STORAGE_ADDRESS to store parent block hash. |
BAL MUST include HISTORY_STORAGE_ADDRESS with storage_changes (ring buffer slot 0, empty slot_changes since parent hash is framework-computed); SYSTEM_ADDRESS MUST NOT be included in BAL. At block_access_index=1: Alice with nonce_changes, Charlie with balance_changes (10 wei). At block_access_index=2: Bob with nonce_changes, Charlie with balance_changes (20 wei total). |
✅ Completed |
test_bal_2935_empty_block |
Ensure BAL captures EIP-2935 history storage writes in empty block | Block with no transactions. At block start (pre-execution), SYSTEM_ADDRESS calls HISTORY_STORAGE_ADDRESS to store parent block hash. |
BAL MUST include HISTORY_STORAGE_ADDRESS with storage_changes (ring buffer slot 0, empty slot_changes); SYSTEM_ADDRESS MUST NOT be included in BAL. No transaction-related BAL entries. |
✅ Completed |
test_bal_2935_query |
Ensure BAL captures storage reads when querying EIP-2935 historical block hashes (valid and invalid queries) with optional value transfer | Parameterized test: Block 1 (empty, stores genesis hash via system call). Block 2: Oracle contract queries HISTORY_STORAGE_ADDRESS with block number. Two block number scenarios (valid=0 genesis hash, invalid=1042 out of range) and value (0 or 100 wei). Valid query (block_number=0): reads genesis hash slot, oracle writes returned value. If value > 0, history storage contract receives balance. Invalid query (block_number=1042, out of range): reverts before storage access, oracle has implicit SLOAD recorded, value stays in oracle (not transferred to history storage). |
Block 2 BAL MUST include: Valid case at block_access_index=1: HISTORY_STORAGE_ADDRESS with storage_reads [slot 0] and balance_changes if value > 0, oracle with storage_changes (empty slot_changes). Invalid case at block_access_index=1: HISTORY_STORAGE_ADDRESS with NO storage_reads (reverts before access) and NO balance_changes, oracle with storage_reads [0], NO storage_changes, and balance_changes if value > 0 (value stays in oracle). Alice with nonce_changes at block_access_index=1. |
✅ Completed |
test_bal_2935_selfdestruct_to_history_storage |
Ensure BAL captures SELFDESTRUCT to EIP-2935 history storage address |
Single block: Transaction where Alice calls contract (pre-funded with 100 wei) that selfdestructs with HISTORY_STORAGE_ADDRESS as beneficiary. |
BAL MUST include at block_access_index=1: Alice with nonce_changes, contract with balance_changes (100→0), HISTORY_STORAGE_ADDRESS with balance_changes (receives 100 wei). |
✅ Completed |
test_bal_2935_invalid_calldata_size |
Ensure BAL correctly handles EIP-2935 queries with invalid calldata size (reverts before any storage access) | Parameterized test: Block 1 stores genesis hash via system call. Block 2: Oracle contract calls HISTORY_STORAGE_ADDRESS with invalid calldata sizes (0, 31, 33 bytes). EIP-2935 requires exactly 32 bytes calldata; any other size causes immediate revert before storage access. Optional value transfer (0 or 100 wei). |
Block 2 BAL MUST include: HISTORY_STORAGE_ADDRESS with NO storage_reads (calldata size check fails before any SLOAD) and NO balance_changes (call reverts). Oracle with storage_reads [0] (implicit SLOAD from no-op SSTORE), NO storage_changes, and balance_changes if value > 0 (value stays in oracle on revert). Alice with nonce_changes. |
✅ Completed |
test_bal_create2_selfdestruct_then_recreate_same_block |
Ensure BAL handles (tx1) CREATE2+SELFDESTRUCT then (tx2) CREATE2 "resurrection" of the same address in the same block. Parametrized over pre_balance: [0, 100]. |
Two identical txs invoke the same factory with the same initcode (same hash → same CREATE2 address A). The factory branches on its own storage[1]: on the first tx, slot 1 is 0 so the factory CREATE2's then CALLs A (runtime SELFDESTRUCTs to beneficiary) and records the CALL's return code in slot 1; on the second tx, slot 1 is non-zero so only CREATE2 runs and A persists. When pre_balance > 0, A is pre-funded so Tx1's SELFDESTRUCT transfers a real balance. |
At block_access_index=1 (destructed A): MUST NOT include nonce_changes or code_changes for A (EIP-7928 SELFDESTRUCT-in-tx semantics). balance_changes for A appears only when pre-funded. Beneficiary appears with balance_changes if pre-funded, otherwise empty() (SELFDESTRUCT touches the beneficiary even with 0 value). At block_access_index=2 (resurrection): A has nonce_changes (post=1) and code_changes (post=runtime). Factory's storage[1] = 1 confirms the Tx1 CALL went through. |
✅ Completed |
test_bal_call_with_value_in_static_context |
CALL with nonzero value in static context: target NOT in BAL | Parametrized: target_is_warm (cold/warm via access list), target_has_code (EOA/contract). Static check must fire before account access. |
target MUST NOT appear in BAL. Balances unchanged. |
✅ Completed |
test_bal_create_in_static_context |
CREATE/CREATE2 in static context: created address NOT in BAL | Parametrized: @pytest.mark.with_all_create_opcodes, value (0/1). Static check must fire before balance check, address computation, or nonce increment. |
Created address MUST NOT appear in BAL. Factory nonce unchanged. | ✅ Completed |
test_bal_selfdestruct_in_static_context |
SELFDESTRUCT in static context: beneficiary NOT in BAL | Parametrized: beneficiary_is_warm (cold/warm via access list), caller_balance (0/100). Static check must fire before beneficiary access or balance transfer. |
beneficiary MUST NOT appear in BAL. Balances unchanged. |
✅ Completed |
test_bal_call_opcode_succeeds_in_static_context |
All call opcodes (no value) succeed in static context | Parametrized: @pytest.mark.with_all_call_opcodes. Caller invokes call_opcode(target) inside STATICCALL. |
target MUST appear in BAL. Ensures clients don't over-restrict beyond EIP-214. |
✅ Completed |
test_bal_callcode_with_value_in_static_context |
CALLCODE with nonzero value succeeds in static context | EIP-214 explicitly excludes CALLCODE from write-protection. Caller invokes CALLCODE(value=1, target) inside STATICCALL. |
target MUST appear in BAL. Ensures clients don't apply CALL-with-value restriction to CALLCODE. |
✅ Completed |
test_bal_create_and_oog |
CREATE/CREATE2 OOG boundary test at three gas levels | Parametrized: @pytest.mark.with_all_create_opcodes, OutOfGasBoundary (OOG_BEFORE_TARGET_ACCESS, OOG_AFTER_TARGET_ACCESS, SUCCESS). BEFORE and AFTER differ by 1 gas, proving the static cost boundary. |
OOG_BEFORE: created address MUST NOT appear in BAL. OOG_AFTER: created address IS in BAL as empty() (accessed, state reverted). SUCCESS: created address in BAL with nonce_changes/code_changes. |
✅ Completed |