Skip to content

test_gas_cost()

Documentation for tests/prague/eip7702_set_code_tx/test_gas.py::test_gas_cost@ffe48572.

Generate fixtures for these test cases for Bogota with:

fill -v tests/prague/eip7702_set_code_tx/test_gas.py::test_gas_cost --fork Bogota

Test gas at the execution start of a set-code transaction in multiple scenarios.

Pre-EIP-8037: intrinsic over-charges empty-account auth cost and refunds existing authorities via refund_counter (EIP-3529 ⅕ cap).

EIP-8037 / EIP-2780: intrinsic is execution-only; state-dependent auth charges land at the top frame with no existing-authority refund. With gas_limit under the EIP-7825 cap the reservoir is empty, so state charges spill into gas_left and GAS at code entry reports the execution gas left after both top-frame charges.

Source code in tests/prague/eip7702_set_code_tx/test_gas.py
@pytest.mark.parametrize(
    **gas_test_parameter_args(
        include_pre_authorized=False, execution_gas_allowance=True
    )
)
@pytest.mark.slow()
def test_gas_cost(
    state_test: StateTestFiller,
    pre: Alloc,
    fork: Fork,
    authorization_list_with_properties: List[AuthorizationWithProperties],
    authorization_list: List[AuthorizationTuple],
    data: bytes,
    access_list: List[AccessList],
    sender: EOA,
    self_sponsored: bool,
) -> None:
    """
    Test gas at the execution start of a set-code transaction in multiple
    scenarios.

    Pre-EIP-8037: intrinsic over-charges empty-account auth cost and refunds
    existing authorities via ``refund_counter`` (EIP-3529 1/5 cap).

    EIP-8037 / EIP-2780: intrinsic is execution-only; state-dependent auth
    charges land at the top frame with no existing-authority refund. With
    ``gas_limit`` under the EIP-7825 cap the reservoir is empty, so state
    charges spill into ``gas_left`` and ``GAS`` at code entry reports the
    execution gas left after both top-frame charges.
    """
    gas_opcode_cost = Op.GAS.gas_cost(fork)
    sstore_opcode_count = 10
    push_opcode_count = (2 * sstore_opcode_count) - 1
    measurement_bytecode = (
        Op.GAS
        + Op.PUSH1(0) * push_opcode_count
        + Op.SSTORE(key_warm=False) * sstore_opcode_count
    )

    header_gas_used: int | None = None

    if fork.is_eip_enabled(8037):
        annotated_auths = _annotate_authorizations_for_top_frame(
            authorization_list_with_properties,
            self_sponsored=self_sponsored,
            sender=sender,
        )
        # Match ``allocate_evm_gas``: only the execution intrinsic is
        # removed before the top frame; calldata floor is settled later.
        intrinsic_execution = fork.transaction_intrinsic_cost_calculator()(
            calldata=data,
            access_list=access_list,
            authorization_list_or_count=annotated_auths,
            recipient_type=RecipientType.CONTRACT,
            return_cost_deducted_prior_execution=True,
        )
        top_frame_execution = fork.transaction_top_frame_execution_gas(
            recipient_type=RecipientType.CONTRACT,
            authorizations=annotated_auths,
        )
        top_frame_state = fork.transaction_top_frame_state_gas(
            recipient_type=RecipientType.CONTRACT,
            authorizations=annotated_auths,
        )
        # Combined execution+state bytecode cost: with an empty reservoir
        # the SSTORE state gas also spills into gas_left.
        code_gas = measurement_bytecode.gas_cost(fork)
        expected_gas_measure = code_gas - gas_opcode_cost

        tx_gas_limit = (
            intrinsic_execution
            + top_frame_execution
            + top_frame_state
            + code_gas
        )
        gas_limit_cap = fork.transaction_gas_limit_cap()
        assert gas_limit_cap is None or tx_gas_limit <= gas_limit_cap

        # The calldata floor never binds here: the per-authorization
        # intrinsic charge alone exceeds the floor of the tiny calldata.
        # If it did, the top frame would enter with the floor excess as
        # extra gas_left and the GAS measure below would be wrong.
        intrinsic_with_floor = fork.transaction_intrinsic_cost_calculator()(
            calldata=data,
            access_list=access_list,
            authorization_list_or_count=annotated_auths,
            recipient_type=RecipientType.CONTRACT,
        )
        assert tx_gas_limit >= intrinsic_with_floor

        # No existing-authority refund.
        gas_used = (
            intrinsic_execution
            + top_frame_execution
            + top_frame_state
            + code_gas
        )
        header_gas_used = max(
            intrinsic_execution
            + top_frame_execution
            + measurement_bytecode.execution_cost(fork),
            top_frame_state + measurement_bytecode.state_cost(fork),
        )
        authorization_list = annotated_auths
    else:
        # Prague / Osaka: charge full empty-account auth cost in intrinsic,
        # refund existing authorities via refund_counter (EIP-3529 cap).
        intrinsic_gas = fork.transaction_intrinsic_cost_calculator()(
            calldata=data,
            access_list=access_list,
            authorization_list_or_count=authorization_list,
        )

        discounted_authorizations = 0
        seen_authority: set[Address] = set()
        for (
            authorization_with_properties
        ) in authorization_list_with_properties:
            if authorization_with_properties.invalidity_type is None:
                authority = authorization_with_properties.tuple.signer
                assert authority is not None
                if not authorization_with_properties.empty:
                    seen_authority.add(authority)
                if authority in seen_authority:
                    discounted_authorizations += 1
                else:
                    seen_authority.add(authority)

        discount_gas = (
            Spec.AUTH_PER_EMPTY_ACCOUNT - Spec.REFUND_AUTH_PER_EXISTING_ACCOUNT
        ) * discounted_authorizations

        code_gas = measurement_bytecode.gas_cost(fork)
        expected_gas_measure = code_gas - gas_opcode_cost
        tx_gas_limit = intrinsic_gas + code_gas

        # EIP-3529
        max_discount = tx_gas_limit // 5
        if discount_gas > max_discount:
            # Only one test hits this condition, but it's ok to also test this
            # case.
            discount_gas = max_discount

        gas_used = tx_gas_limit - discount_gas

    test_code_storage = Storage()
    test_code = (
        Op.SSTORE(test_code_storage.store_next(expected_gas_measure), Op.GAS)
        + sum(
            Op.SSTORE(test_code_storage.store_next(1), 1)
            for _ in range(sstore_opcode_count - 1)
        )
        + Op.STOP
    )
    test_code_address = pre.deploy_contract(test_code)

    sender_account = pre[sender]
    assert sender_account is not None

    tx = Transaction(
        gas_limit=tx_gas_limit,
        to=test_code_address,
        value=0,
        data=data,
        authorization_list=authorization_list,
        access_list=access_list,
        sender=sender,
        expected_receipt=TransactionReceipt(cumulative_gas_used=gas_used),
    )

    state_test_kwargs: dict = {
        "pre": pre,
        "tx": tx,
        "post": {
            test_code_address: Account(storage=test_code_storage),
        },
    }
    if header_gas_used is not None:
        state_test_kwargs["blockchain_test_header_verify"] = Header(
            gas_used=header_gas_used
        )
    state_test(**state_test_kwargs)

Parametrized Test Cases

This test generates 33 parametrized test cases across 4 forks.