Skip to content

test_selfdestruct_pre_existing_account_no_refund()

Documentation for tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_selfdestruct.py::test_selfdestruct_pre_existing_account_no_refund@a9abd46e.

Generate fixtures for these test cases for Amsterdam with:

fill -v tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_selfdestruct.py::test_selfdestruct_pre_existing_account_no_refund --fork Amsterdam

Verify SELFDESTRUCT of a pre-existing account earns no refund.

The same-tx-create guard (address in tx_state.created_accounts) is load-bearing: without it, destroying any account would leak state gas back into the reservoir. A contract deployed in pre is destroyed by the tx; accounts_to_delete contains it but created_accounts does not, so no refund is applied. The block header gas_used reflects the full regular-gas tx cost (no state-gas refund offset).

Source code in tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_selfdestruct.py
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
@pytest.mark.valid_from("EIP8037")
def test_selfdestruct_pre_existing_account_no_refund(
    blockchain_test: BlockchainTestFiller,
    pre: Alloc,
    fork: Fork,
) -> None:
    """
    Verify SELFDESTRUCT of a pre-existing account earns no refund.

    The same-tx-create guard (`address in tx_state.created_accounts`)
    is load-bearing: without it, destroying any account would leak
    state gas back into the reservoir.  A contract deployed in `pre`
    is destroyed by the tx; `accounts_to_delete` contains it but
    `created_accounts` does not, so no refund is applied.  The block
    header `gas_used` reflects the full regular-gas tx cost (no
    state-gas refund offset).
    """
    intrinsic_gas = fork.transaction_intrinsic_cost_calculator()()

    # Victim deployed in `pre` (NOT same-tx-created).  SELFDESTRUCTs
    # to self so no new-account state gas is charged to the tx.
    victim_code = Op.SELFDESTRUCT.with_metadata(address_warm=True)(Op.ADDRESS)
    victim = pre.deploy_contract(code=victim_code)

    caller_code = Op.POP(Op.CALL(gas=Op.GAS, address=victim))
    caller = pre.deploy_contract(code=caller_code)

    # No refund offset: both caller_code and victim_code are pure
    # regular gas (SELFDESTRUCT to self, no value-to-new-account).
    tx_regular = (
        intrinsic_gas + caller_code.gas_cost(fork) + victim_code.gas_cost(fork)
    )

    tx = Transaction(
        to=caller,
        state_gas_reservoir=0,
        sender=pre.fund_eoa(),
    )

    # Per EIP-6780, SELFDESTRUCT on a not-same-tx-created account
    # does not delete it — the account still exists after the tx.
    blockchain_test(
        pre=pre,
        blocks=[Block(txs=[tx], header_verify=Header(gas_used=tx_regular))],
        post={victim: Account(code=victim_code)},
    )

Parametrized Test Cases

This test generates 1 parametrized test case across 1 fork.