Skip to content

test_access_list_intrinsic_across_amsterdam_transition()

Documentation for tests/amsterdam/eip7981_increase_access_list_cost/test_fork_transition.py::test_access_list_intrinsic_across_amsterdam_transition@26332146.

Generate fixtures for these test cases for Amsterdam with:

fill -v tests/amsterdam/eip7981_increase_access_list_cost/test_fork_transition.py::test_access_list_intrinsic_across_amsterdam_transition --fork Amsterdam

Pin the access list intrinsic change across the Amsterdam boundary.

The same access-list transaction shape is sent in a pre-fork block (flat base plus the EIP-2930 per-entry charges, no data cost) and a post-fork block (decomposed base, repriced entries, plus the EIP-7981 byte surcharge). Each block uses a distinct sender so its post-tx balance pins the fork-appropriate intrinsic; the recipient is an existing EOA, so no EVM bytecode runs and gas_used equals the intrinsic exactly.

The per-fork intrinsic returned by the calculator is also checked against a hand-derived per-EIP decomposition, so a calculator regression fails here with a clear message rather than only as a downstream balance mismatch.

Source code in tests/amsterdam/eip7981_increase_access_list_cost/test_fork_transition.py
 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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
@EIPChecklist.GasCostChanges.Test.ForkTransition.Before()
@EIPChecklist.GasCostChanges.Test.ForkTransition.After()
@pytest.mark.parametrize(
    "addresses,keys_per_address",
    [
        pytest.param(1, 0, id="single_address_no_keys"),
        pytest.param(1, 2, id="single_address_two_keys"),
        pytest.param(2, 3, id="two_addresses_three_keys_each"),
    ],
)
def test_access_list_intrinsic_across_amsterdam_transition(
    blockchain_test: BlockchainTestFiller,
    pre: Alloc,
    fork: TransitionFork,
    addresses: int,
    keys_per_address: int,
) -> None:
    """
    Pin the access list intrinsic change across the Amsterdam boundary.

    The same access-list transaction shape is sent in a pre-fork block
    (flat base plus the EIP-2930 per-entry charges, no data cost) and a
    post-fork block (decomposed base, repriced entries, plus the
    EIP-7981 byte surcharge). Each block uses a distinct sender so its
    post-tx balance pins the fork-appropriate intrinsic; the recipient
    is an existing EOA, so no EVM bytecode runs and `gas_used` equals
    the intrinsic exactly.

    The per-fork intrinsic returned by the calculator is also checked
    against a hand-derived per-EIP decomposition, so a calculator
    regression fails here with a clear message rather than only as a
    downstream balance mismatch.
    """
    gas_price = 1_000_000_000
    access_list = access_list_shape(addresses, keys_per_address)
    total_keys = addresses * keys_per_address

    pre_fork = fork.fork_at(timestamp=PRE_FORK_TIMESTAMP)
    post_fork = fork.fork_at(timestamp=POST_FORK_TIMESTAMP)
    pre_costs = pre_fork.gas_costs()
    post_costs = post_fork.gas_costs()

    # Pre-fork: flat base plus the EIP-2930 per-entry charges; access
    # list bytes carry no data cost.
    expected_pre = (
        pre_costs.TX_BASE
        + addresses * pre_costs.TX_ACCESS_LIST_ADDRESS
        + total_keys * pre_costs.TX_ACCESS_LIST_STORAGE_KEY
    )
    # Post-fork: EIP-2780 decomposed base and recipient charge, EIP-8038
    # repriced entry charges, and the EIP-7981 byte surcharge.
    surcharge = (
        calculate_access_list_floor_tokens(access_list)
        * post_costs.TX_DATA_TOKEN_FLOOR
    )
    expected_post = (
        post_costs.TX_BASE
        + post_costs.COLD_ACCOUNT_ACCESS
        + addresses * post_costs.TX_ACCESS_LIST_ADDRESS
        + total_keys * post_costs.TX_ACCESS_LIST_STORAGE_KEY
        + surcharge
    )

    timestamps = [PRE_FORK_TIMESTAMP, POST_FORK_TIMESTAMP]
    expected_intrinsics = [expected_pre, expected_post]
    blocks = []
    post: dict[Address, Account] = {}

    for timestamp, expected_intrinsic in zip(
        timestamps, expected_intrinsics, strict=True
    ):
        sub_fork = fork.fork_at(timestamp=timestamp)
        intrinsic_gas = sub_fork.transaction_intrinsic_cost_calculator()(
            access_list=access_list,
            return_cost_deducted_prior_execution=True,
        )
        assert intrinsic_gas == expected_intrinsic, (
            f"intrinsic at timestamp {timestamp} ({sub_fork}) is "
            f"{intrinsic_gas}, expected {expected_intrinsic}"
        )
        # The intrinsic side must bind so gas_used equals the intrinsic.
        floor_gas = sub_fork.transaction_data_floor_cost_calculator()(
            data=b"", access_list=access_list
        )
        assert floor_gas <= intrinsic_gas

        sender_initial_balance = 10**18
        sender = pre.fund_eoa(sender_initial_balance)
        target = pre.fund_eoa(amount=0)

        tx = Transaction(
            sender=sender,
            to=target,
            gas_limit=intrinsic_gas,
            gas_price=gas_price,
            access_list=access_list,
        )
        blocks.append(Block(timestamp=timestamp, txs=[tx]))

        post[sender] = Account(
            nonce=1,
            balance=sender_initial_balance - intrinsic_gas * gas_price,
        )

    blockchain_test(pre=pre, blocks=blocks, post=post)

Parametrized Test Cases

This test generates 3 parametrized test cases across 1 fork.