Skip to content

test_create_collision_refunds_state_gas()

Documentation for tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_create.py::test_create_collision_refunds_state_gas@c74f1a67.

Generate fixtures for these test cases for Amsterdam with:

fill -v tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_create.py::test_create_collision_refunds_state_gas --fork Amsterdam

Verify CREATE/CREATE2 address collision refunds account state gas.

The collision path increments the factory nonce and burns the forwarded regular gas (consumed by the never-spawned child), but still refunds GAS_NEW_ACCOUNT to the reservoir. Tight gas tuning limits the factory's post-collision gas_left so the probe SSTORE can only succeed via the refunded reservoir, not by spilling state gas from gas_left.

Source code in tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_create.py
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
@pytest.mark.pre_alloc_mutable()
@pytest.mark.with_all_create_opcodes()
@pytest.mark.valid_from("EIP8037")
def test_create_collision_refunds_state_gas(
    state_test: StateTestFiller,
    pre: Alloc,
    fork: Fork,
    create_opcode: Op,
) -> None:
    """
    Verify CREATE/CREATE2 address collision refunds account state gas.

    The collision path increments the factory nonce and burns the
    forwarded regular gas (consumed by the never-spawned child), but
    still refunds `GAS_NEW_ACCOUNT` to the reservoir. Tight gas
    tuning limits the factory's post-collision `gas_left` so the
    probe SSTORE can only succeed via the refunded reservoir, not
    by spilling state gas from `gas_left`.
    """
    gas_costs = fork.gas_costs()
    sstore_state_gas = Op.SSTORE(new_value=1).state_cost(fork)
    new_account_state_gas = gas_costs.NEW_ACCOUNT

    init_code = Op.STOP
    mstore_value, size = init_code_at_high_bytes(init_code)
    salt = 0

    storage = Storage()
    create_call = (
        create_opcode(value=0, offset=0, size=size, salt=salt)
        if create_opcode == Op.CREATE2
        else create_opcode(value=0, offset=0, size=size)
    )
    factory_code = (
        Op.MSTORE(0, mstore_value)
        + Op.POP(create_call)
        + Op.SSTORE(storage.store_next(1, "reservoir_ok"), 1)
    )
    factory = pre.deploy_contract(code=factory_code)

    collision_target = compute_create_address(
        address=factory,
        nonce=1,
        salt=salt,
        initcode=bytes(init_code),
        opcode=create_opcode,
    )
    pre.deploy_contract(code=Op.STOP, address=collision_target)

    # Tight gas tuning: factory retains
    # ~(forwarded - pre_sstore_regular) / 64 after collision burns
    # `max_message_call_gas` as regular. Target the discrimination
    # window `(probe_regular, probe_regular + sstore_state_gas)` so
    # the probe SSTORE regular fits but state gas spillover from
    # `gas_left` under the old behavior OOGs.
    pre_sstore_code = Op.MSTORE(0, mstore_value) + Op.POP(create_call)
    pre_sstore_regular = pre_sstore_code.gas_cost(fork) - new_account_state_gas
    probe_code = Op.SSTORE(0, 1)
    probe_regular = probe_code.gas_cost(fork) - sstore_state_gas
    target_gas_left = probe_regular + sstore_state_gas // 2
    forwarded_gas = target_gas_left * 64 + pre_sstore_regular
    # Reservoir sized for CREATE charge only — SSTORE must pull from
    # the refunded reservoir, not from spill.
    caller = pre.deploy_contract(
        code=Op.CALL(gas=forwarded_gas, address=factory)
    )
    tx = Transaction(
        to=caller,
        state_gas_reservoir=new_account_state_gas,
        sender=pre.fund_eoa(),
    )

    state_test(pre=pre, post={factory: Account(storage=storage)}, tx=tx)

Parametrized Test Cases

This test generates 2 parametrized test cases across 1 fork.