@pytest.mark.parametrize("access_warm", [True, False])
@pytest.mark.parametrize(
"initial_value,write_values",
[
pytest.param(
0xDEADBEEF,
[0, 0xDEADBEEF, 0, 0xDEADBEEF],
id="oscillation_4x",
),
pytest.param(
0xDEADBEEF,
[0, 0xDEADBEEF, 0, 0xDEADBEEF, 0, 0xDEADBEEF],
id="oscillation_6x",
),
pytest.param(
0xDEADBEEF,
[0xBEEFBEEF, 0xCAFECAFE, 0xDEADBEEF],
id="triple_write_restore",
),
pytest.param(
0xDEADBEEF,
[0],
id="mass_clear",
),
pytest.param(
0,
[1, 0, 1, 0],
id="oscillation_4x_from_zero",
marks=pytest.mark.skip(
reason="net-zero state gas; degenerates to an "
"execution-gas loop"
),
),
pytest.param(
0,
[1],
id="mass_set_from_zero",
),
],
)
def test_sstore_dirty_transitions(
benchmark_test: BenchmarkTestFiller,
fork: Fork,
pre: Alloc,
tx_gas_limit: int,
gas_benchmark_value: int,
access_warm: bool,
initial_value: int,
write_values: List[int],
) -> None:
"""
Benchmark SSTORE dirty state transitions.
Exercise EIP-2200/EIP-3529 refund logic by writing the same slot
multiple times per iteration. Uses EIP-7702 delegation: authority
EOA delegates to initializer then to dirty-write executor.
Variants:
- oscillation: X→0→X→0, alternates a clean change (slot access plus
`STORAGE_WRITE`) with a dirty one (slot access only)
- triple_write_restore: X→B→C→X, all SSTORE branches
- mass_clear: X→0, maximum per-slot refund generation
"""
# Initial Storage Construction
initializer_code = create_sstore_initializer(initial_value)
initializer_addr = pre.deploy_contract(code=initializer_code)
# Benchmark Executor — multi-write per slot
executor_code = create_sstore_dirty_executor(
write_values=write_values,
key_warm=access_warm,
initial_value=initial_value,
)
executor_addr = pre.deploy_contract(code=executor_code)
authority = pre.fund_eoa(amount=0)
authority_nonce = 0
delegation_sender = pre.fund_eoa()
calldata_gen = partial(executor_calldata_generator)
access_list_gen = partial(
access_list_generator,
access_warm=access_warm,
authority=authority,
)
# Number of slots processable in execution phase
num_target_slots = sum(
executor_code.tx_iterations_by_gas_limit(
fork=fork,
gas_limit=gas_benchmark_value,
calldata=calldata_gen,
access_list=access_list_gen,
start_iteration=1,
recipient_type=RecipientType.DELEGATION_7702,
)
)
# Setup phase: initialize all slots to initial_value
with TestPhaseManager.setup():
blocks = build_delegated_storage_setup(
pre=pre,
fork=fork,
tx_gas_limit=tx_gas_limit,
block_gas_budget=gas_benchmark_value,
needs_init=initial_value != 0,
num_target_slots=num_target_slots,
initializer_code=initializer_code,
initializer_addr=initializer_addr,
executor_addr=executor_addr,
authority=authority,
authority_nonce=authority_nonce,
delegation_sender=delegation_sender,
initializer_calldata_generator=(initializer_calldata_generator),
)
# Execution phase — no expected_benchmark_gas_used because
# refund cap (gas_used/5) makes actual consumption non-trivial
with TestPhaseManager.execution():
exec_txs = list(
executor_code.transactions_by_gas_limit(
fork=fork,
gas_limit=gas_benchmark_value,
sender=pre.fund_eoa(),
to=authority,
calldata=calldata_gen,
start_iteration=1,
access_list=access_list_gen,
recipient_type=RecipientType.DELEGATION_7702,
)
)
blocks.append(Block(txs=exec_txs))
benchmark_test(
pre=pre,
blocks=blocks,
skip_gas_used_validation=True,
)