Skip to content

test_call_value_new_account_state_gas_returned_on_caller_revert()

Documentation for tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_call.py::test_call_value_new_account_state_gas_returned_on_caller_revert@622620d8.

Generate fixtures for these test cases for Amsterdam with:

fill -v tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_call.py::test_call_value_new_account_state_gas_returned_on_caller_revert --fork Amsterdam

Return a spilled NEW_ACCOUNT charge when the caller cleanly reverts.

Same value CALL to the absent account target as the halt case, but the caller ends with REVERT. A revert refills the frame state gas in LIFO order: the spilled portion returns to gas_left and the reservoir-funded portion restores the reservoir, both refunded to the sender. The sender pays only the execution gas, the same value in-cap and over-cap, and the value transfer is rolled back.

Source code in tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_call.py
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
@pytest.mark.parametrize("reservoir", ["in_cap", "over_cap", "full"])
@pytest.mark.valid_from("EIP8037")
def test_call_value_new_account_state_gas_returned_on_caller_revert(
    state_test: StateTestFiller,
    pre: Alloc,
    fork: Fork,
    reservoir: str,
) -> None:
    """
    Return a spilled NEW_ACCOUNT charge when the caller cleanly reverts.

    Same value CALL to the absent account `target` as the halt case, but the
    caller ends with `REVERT`. A revert refills the frame state gas in LIFO
    order: the spilled portion returns to `gas_left` and the reservoir-funded
    portion restores the reservoir, both refunded to the sender. The sender
    pays only the execution gas, the same value in-cap and over-cap,
    and the value transfer is rolled back.
    """
    value = 1
    target = pre.nonexistent_account()
    caller_code = Op.CALL(
        gas=0,
        address=target,
        value=value,
        value_transfer=True,
        account_new=True,
    ) + Op.REVERT(0, 0)
    caller = pre.deploy_contract(code=caller_code, balance=value)
    sender = pre.fund_eoa()

    # Only execution gas is billed: the spilled and reservoir-funded
    # parts of the NEW_ACCOUNT charge are both refunded, so the cost
    # matches in-cap and over-cap. `execution_cost` covers the pushes, cold
    # access and the value transfer (NEW_ACCOUNT lands in the state
    # dimension); the empty child returns its stipend unused.
    expected_gas_used = (
        fork.transaction_intrinsic_cost_calculator()()
        + caller_code.execution_cost(fork)
        - fork.call_value_stipend()
    )
    receipt = TransactionReceipt(cumulative_gas_used=expected_gas_used)

    gas_limit_cap = fork.transaction_gas_limit_cap()
    assert gas_limit_cap is not None
    if reservoir == "full":
        gas_limit = gas_limit_cap + caller_code.state_cost(fork)
    elif reservoir == "over_cap":
        gas_limit = gas_limit_cap + caller_code.state_cost(fork) // 2
    else:
        gas_limit = 1_000_000

    tx = Transaction(
        to=caller,
        sender=sender,
        gas_limit=gas_limit,
        expected_receipt=receipt,
    )

    post = {
        caller: Account(balance=value),
        target: Account.NONEXISTENT,
    }
    state_test(pre=pre, post=post, tx=tx)

Parametrized Test Cases

This test generates 3 parametrized test cases across 1 fork.