Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ Test fixtures for use by clients are available for each release on the [Github r
- ✨ Add EIP-7928 successful and OOG single-opcode tests ([#2118](https://github.com/ethereum/execution-spec-tests/pull/2118)).
- ✨ Add EIP-7928 tests for EIP-2930 interactions ([#2167](https://github.com/ethereum/execution-spec-tests/pull/2167)).
- ✨ Add EIP-7928 tests for NOOP operations ([#2178](https://github.com/ethereum/execution-spec-tests/pull/2178)).
- ✨ Add EIP-7928 tests for net-zero balance transfers ([#2280](https://github.com/ethereum/execution-spec-tests/pull/2280)).

## [v5.0.0](https://github.com/ethereum/execution-spec-tests/releases/tag/v5.0.0) - 2025-09-05

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -947,6 +947,109 @@ def test_bal_zero_value_transfer(
blockchain_test(pre=pre, blocks=[block], post={})


@pytest.mark.parametrize(
"initial_balance,transfer_amount,transfer_mechanism",
[
pytest.param(0, 0, "call", id="zero_balance_zero_transfer_call"),
pytest.param(0, 0, "selfdestruct", id="zero_balance_zero_transfer_selfdestruct"),
pytest.param(1, 1, "call", id="nonzero_balance_net_zero"),
pytest.param(100, 50, "call", id="larger_balance_net_zero"),
],
)
def test_bal_net_zero_balance_transfer(
pre: Alloc,
blockchain_test: BlockchainTestFiller,
initial_balance: int,
transfer_amount: int,
transfer_mechanism: str,
):
"""
Test that BAL does not record balance changes when net change is zero.

A contract starts with `initial_balance`, receives `transfer_amount`
(increasing its balance), then sends `transfer_amount` to a recipient
(decreasing its balance back to `initial_balance`). The net change is zero,
so BAL should not record any balance changes for this contract.

The contract verifies this by reading its own balance with SELFBALANCE,
storing it in slot 0, then sending that amount to the recipient.
"""
alice = pre.fund_eoa()
recipient = pre.fund_eoa(amount=0)

net_zero_bal_contract_code = (
Op.SSTORE(0, Op.SELFBALANCE) + Op.SELFDESTRUCT(recipient)
if transfer_mechanism == "selfdestruct"
# store current balance in slot 0
else (
Op.SSTORE(0, Op.SELFBALANCE)
# send only the `transfer_amount` received to recipient (net zero)
+ Op.CALL(0, recipient, Op.CALLVALUE, 0, 0, 0, 0)
+ Op.STOP
)
)
net_zero_bal_contract = pre.deploy_contract(
code=net_zero_bal_contract_code, balance=initial_balance
)

tx = Transaction(
sender=alice,
to=net_zero_bal_contract,
value=transfer_amount,
gas_limit=1_000_000,
gas_price=0xA,
)

expected_balance_in_slot = initial_balance + transfer_amount

block = Block(
txs=[tx],
expected_block_access_list=BlockAccessListExpectation(
account_expectations={
alice: BalAccountExpectation(
nonce_changes=[BalNonceChange(tx_index=1, post_nonce=1)],
),
net_zero_bal_contract: BalAccountExpectation(
# receives transfer_amount and sends transfer_amount away
# (net-zero change)
balance_changes=[],
storage_reads=[0x00] if expected_balance_in_slot == 0 else [],
storage_changes=[
BalStorageSlot(
slot=0x00,
slot_changes=[
BalStorageChange(tx_index=1, post_value=expected_balance_in_slot)
],
)
]
if expected_balance_in_slot > 0
else [],
),
# recipient receives transfer_amount
recipient: BalAccountExpectation(
balance_changes=[BalBalanceChange(tx_index=1, post_balance=transfer_amount)]
if transfer_amount > 0
else [],
),
}
),
)

blockchain_test(
pre=pre,
blocks=[block],
post={
net_zero_bal_contract: Account(
balance=initial_balance,
storage={0x00: expected_balance_in_slot} if expected_balance_in_slot > 0 else {},
),
recipient: Account(balance=transfer_amount)
if transfer_amount > 0
else Account.NONEXISTENT,
},
)


def test_bal_pure_contract_call(
pre: Alloc,
blockchain_test: BlockchainTestFiller,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
| `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`. | 🟑 Planned |
| `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_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_contracts_2935_4788` | BAL includes pre-exec system writes for parent hash & beacon root | Build a block with `N` normal txs; 2935 & 4788 active | BAL MUST include `HISTORY_STORAGE_ADDRESS` (EIP-2935) and `BEACON_ROOTS_ADDRESS` (EIP-4788) with `storage_changes` to ring-buffer slots; each write uses `tx_index = N` (system op). | 🟑 Planned |
| `test_bal_system_dequeue_withdrawals_eip7002` | BAL tracks post-exec system dequeues for withdrawals | Pre-populate EIP-7002 withdrawal requests; produce a block where dequeues occur | BAL MUST include the 7002 system contract with `storage_changes` (queue head/tail slots 0–3) using `tx_index = len(txs)` and balance changes for withdrawal recipients. | 🟑 Planned |
| `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 `tx_index = len(txs)`. | 🟑 Planned |
Expand Down
Loading