Skip to content

test_value_moving_transactions()

Documentation for tests/amsterdam/eip2780_reduce_intrinsic_tx_gas/test_value_moving_transactions.py::test_value_moving_transactions@87aba1a3.

Generate fixtures for these test cases for Amsterdam with:

fill -v tests/amsterdam/eip2780_reduce_intrinsic_tx_gas/test_value_moving_transactions.py::test_value_moving_transactions --fork Amsterdam

Ensure value-moving transactions charge gas correctly across every non-create recipient type.

Self-transfers are carved out: the sender pays only the recipient -access-free intrinsic and the value is moved to itself, so the sender's post-tx balance reflects only gas. Pre-existing 7702 delegations on the recipient surface as an extra top-frame COLD_ACCOUNT_ACCESS; empty recipients trigger the top-frame NEW_ACCOUNT state charge when value is transferred.

The EIP-7708 transfer log is asserted to fire exactly when TRANSFER_LOG_COST is charged: for a non-self value transfer, and never for a self-transfer (carve-out) or a zero-value tx.

Source code in tests/amsterdam/eip2780_reduce_intrinsic_tx_gas/test_value_moving_transactions.py
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
@pytest.mark.parametrize("recipient_type", RECIPIENT_TYPES_NON_CREATE)
@pytest.mark.parametrize(
    "value",
    [
        pytest.param(0, id="zero_value"),
        pytest.param(1, id="non-zero_value"),
    ],
)
def test_value_moving_transactions(
    fork: Fork,
    pre: Alloc,
    state_test: StateTestFiller,
    recipient_type: RecipientType,
    value: int,
) -> None:
    """
    Ensure value-moving transactions charge gas correctly across every
    non-create recipient type.

    Self-transfers are carved out: the sender pays only the recipient
    -access-free intrinsic and the value is moved to itself, so the
    sender's post-tx balance reflects only gas. Pre-existing 7702
    delegations on the recipient surface as an extra top-frame
    ``COLD_ACCOUNT_ACCESS``; empty recipients trigger the top-frame
    ``NEW_ACCOUNT`` state charge when value is transferred.

    The EIP-7708 transfer log is asserted to fire exactly when
    ``TRANSFER_LOG_COST`` is charged: for a non-self value transfer,
    and never for a self-transfer (carve-out) or a zero-value tx.
    """
    sender_initial_balance = 10**18
    sender = pre.fund_eoa(sender_initial_balance)
    target = setup_target(pre, recipient_type, sender)

    target_initial_balance = (
        EOA_INITIAL_BALANCE if recipient_type == RecipientType.EOA else 0
    )

    intrinsic_gas = fork.transaction_intrinsic_cost_calculator()(
        sends_value=bool(value),
        recipient_type=recipient_type,
        return_cost_deducted_prior_execution=True,
    )
    top_frame_gas = fork.transaction_top_frame_gas_calculator()(
        sends_value=bool(value),
        recipient_type=recipient_type,
    )
    top_frame_state_gas = fork.transaction_top_frame_state_gas(
        sends_value=bool(value),
        recipient_type=recipient_type,
    )
    # Under the default zero state-gas reservoir, top-frame state gas
    # spills entirely into regular gas.
    total_gas_cost = intrinsic_gas + top_frame_gas + top_frame_state_gas

    tx_gas_limit = total_gas_cost + 1000  # add a small buffer
    gas_price = 1_000_000_000

    is_self_transfer = recipient_type == RecipientType.SELF

    # A transfer log is emitted iff value moves to a distinct account,
    # which is exactly when the intrinsic includes ``TRANSFER_LOG_COST``.
    # ``logs=[]`` asserts no log fires for the carved-out cases.
    if value > 0 and not is_self_transfer:
        expected_logs = [transfer_log(sender, target, value)]
    else:
        expected_logs = []

    tx = Transaction(
        sender=sender,
        to=target,
        value=value,
        gas_limit=tx_gas_limit,
        gas_price=gas_price,
        expected_receipt=TransactionReceipt(logs=expected_logs),
    )

    sender_value_delta = 0 if is_self_transfer else value
    sender_final_balance = (
        sender_initial_balance
        - sender_value_delta
        - total_gas_cost * gas_price
    )

    post: dict[Address, Account | None] = {
        sender: Account(nonce=1, balance=sender_final_balance),
    }
    if not is_self_transfer:
        if recipient_type == RecipientType.EMPTY_ACCOUNT and value == 0:
            post[target] = None
        else:
            post[target] = Account(balance=target_initial_balance + value)

    state_test(pre=pre, tx=tx, post=post)

Parametrized Test Cases

This test generates 10 parametrized test cases across 1 fork.