Skip to content

Commit df8f509

Browse files
committed
feat(specs,amsterdam): fold transfer log cost into value transfer cost
Align the EIP-2780 implementation with the changes proposed in ethereum/EIPs#11997
1 parent 2282c75 commit df8f509

8 files changed

Lines changed: 26 additions & 57 deletions

File tree

packages/testing/src/execution_testing/forks/forks/eips/amsterdam/eip_2780.py

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,7 @@ def gas_costs(cls) -> GasCosts:
4040
return replace(
4141
parent,
4242
TX_BASE=12_000,
43-
TRANSFER_LOG_COST=1_756,
44-
TX_VALUE_COST=4_244,
43+
TX_VALUE_COST=6_000,
4544
)
4645

4746
@classmethod
@@ -75,14 +74,10 @@ def fn(
7574
# CREATE_ACCESS regular gas; TX_CREATE folds in the
7675
# NEW_ACCOUNT state gas, which the floor excludes.
7776
floor += gas_costs.TX_CREATE - gas_costs.NEW_ACCOUNT
78-
if sends_value:
79-
floor += gas_costs.TRANSFER_LOG_COST
8077
elif not is_self_transfer:
8178
floor += gas_costs.COLD_ACCOUNT_ACCESS
8279
if sends_value:
83-
floor += (
84-
gas_costs.TRANSFER_LOG_COST + gas_costs.TX_VALUE_COST
85-
)
80+
floor += gas_costs.TX_VALUE_COST
8681
return floor
8782

8883
return fn
@@ -97,9 +92,8 @@ def transaction_intrinsic_cost_calculator(
9792
9893
Non-create, non-self targets pay ``COLD_ACCOUNT_ACCESS``
9994
unconditionally; access lists do not warm transaction-level
100-
accounts. Value-bearing transactions pay
101-
``TRANSFER_LOG_COST`` plus ``TX_VALUE_COST``; self-transfers
102-
suppress the value-transfer charge entirely.
95+
accounts. Value-bearing transactions pay ``TX_VALUE_COST``;
96+
self-transfers suppress the value-transfer charge entirely.
10397
"""
10498
super_fn = super(EIP2780, cls).transaction_intrinsic_cost_calculator()
10599
gas_costs = cls.gas_costs()
@@ -147,14 +141,10 @@ def fn(
147141
# remove it here, mirroring value transfer to an empty
148142
# account whose NEW_ACCOUNT is likewise top-frame.
149143
intrinsic_cost -= gas_costs.NEW_ACCOUNT
150-
if sends_value:
151-
intrinsic_cost += gas_costs.TRANSFER_LOG_COST
152144
elif not is_self_transfer:
153145
intrinsic_cost += gas_costs.COLD_ACCOUNT_ACCESS
154146
if sends_value:
155-
intrinsic_cost += (
156-
gas_costs.TRANSFER_LOG_COST + gas_costs.TX_VALUE_COST
157-
)
147+
intrinsic_cost += gas_costs.TX_VALUE_COST
158148

159149
if return_cost_deducted_prior_execution:
160150
return intrinsic_cost

packages/testing/src/execution_testing/forks/gas_costs.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ class GasCosts:
3838
NEW_ACCOUNT: int
3939
ACCOUNT_WRITE: int = 0
4040
CREATE_ACCESS: int = 0
41-
TRANSFER_LOG_COST: int = 0
4241
TX_VALUE_COST: int = 0
4342

4443
# Contract Creation

src/ethereum/forks/amsterdam/transactions.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -639,8 +639,8 @@ def calculate_intrinsic_cost(
639639
call, or `CREATE_ACCESS` for a contract creation). The created
640640
account's `NEW_ACCOUNT` state gas is state-dependent and is
641641
charged at the top frame, not here.
642-
3. Value cost (`TRANSFER_LOG_COST`, plus `TX_VALUE_COST` for a
643-
non-self-transfer call) when ``tx.value > 0``.
642+
3. Value cost (`TX_VALUE_COST` for a non-self-transfer call) when
643+
``tx.value > 0``.
644644
4. Calldata cost (zero and non-zero bytes).
645645
5. Access list entries (if applicable).
646646
6. Authorizations (if applicable): only the state-independent base
@@ -671,14 +671,10 @@ def calculate_intrinsic_cost(
671671
if is_create:
672672
recipient_regular_gas = GasCosts.CREATE_ACCESS
673673
init_code_gas = init_code_cost(ulen(tx.data))
674-
if tx.value > U256(0):
675-
recipient_regular_gas += GasCosts.TRANSFER_LOG_COST
676674
elif not is_self_transfer:
677675
recipient_regular_gas = GasCosts.COLD_ACCOUNT_ACCESS
678676
if tx.value > U256(0):
679-
recipient_regular_gas += (
680-
GasCosts.TRANSFER_LOG_COST + GasCosts.TX_VALUE_COST
681-
)
677+
recipient_regular_gas += GasCosts.TX_VALUE_COST
682678

683679
access_list_cost = Uint(0)
684680
tokens_in_access_list = Uint(0)

src/ethereum/forks/amsterdam/vm/gas.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,7 @@ class GasCosts:
137137
# Transactions
138138
TX_BASE: Final[Uint] = Uint(12000)
139139
TX_CREATE: Final[Uint] = Uint(32000)
140-
TX_VALUE_COST: Final[Uint] = Uint(4244)
141-
TRANSFER_LOG_COST: Final[Uint] = Uint(1756)
140+
TX_VALUE_COST: Final[Uint] = Uint(6000)
142141
TX_DATA_TOKEN_STANDARD: Final[Uint] = Uint(4)
143142
TX_DATA_TOKEN_FLOOR: Final[Uint] = Uint(16)
144143
TX_ACCESS_LIST_ADDRESS: Final[Uint] = COLD_ACCOUNT_ACCESS

tests/amsterdam/eip2780_reduce_intrinsic_tx_gas/spec.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@ class ReferenceSpec:
1313

1414
ref_spec_2780 = ReferenceSpec(
1515
git_path="EIPS/eip-2780.md",
16-
version="e6d8f589d355e891c37ff479d3ce668352e5b1be",
16+
version="04dd54c2e7ec1f408cf4a150d5c1aa43573bd025",
1717
)

tests/amsterdam/eip2780_reduce_intrinsic_tx_gas/test_calldata_floor.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -259,13 +259,11 @@ def test_calldata_floor_contract_creation(
259259
empty code, and prices every byte as one floor token.
260260
261261
- ``floor_binds``: ``gas_used`` pins to the floor, which anchors
262-
on the creation regular base (``TX_BASE + CREATE_ACCESS``, plus
263-
``TRANSFER_LOG_COST`` when value moves) but excludes the created
264-
account's ``NEW_ACCOUNT`` *state* charge and the init-code word
265-
cost -- both masked by the binding floor -- while the deploy
266-
(and any moved wei) still lands. The receipt pins the floor
267-
exactly, so the value-bearing case sits precisely
268-
``TRANSFER_LOG_COST`` above the zero-value one.
262+
on the creation regular base (``TX_BASE + CREATE_ACCESS``)
263+
but excludes the created account's ``NEW_ACCOUNT`` *state* charge
264+
and the init-code word cost -- both masked by the binding floor --
265+
while the deploy (and any moved wei) still lands.
266+
The receipt pins the floor exactly.
269267
- ``below_floor``: a gas limit one short of the floor still covers
270268
the creation intrinsic, so the rejection is pinned to the floor,
271269
with ``INTRINSIC_GAS_BELOW_FLOOR_GAS_COST``.

tests/amsterdam/eip2780_reduce_intrinsic_tx_gas/test_fork_transition.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,7 @@ def test_intrinsic_reduction_across_amsterdam_transition(
102102
if not self_transfer:
103103
expected_post += post_gas_costs.COLD_ACCOUNT_ACCESS
104104
if value:
105-
expected_post += (
106-
post_gas_costs.TRANSFER_LOG_COST + post_gas_costs.TX_VALUE_COST
107-
)
105+
expected_post += post_gas_costs.TX_VALUE_COST
108106

109107
timestamps = [PRE_FORK_TIMESTAMP, POST_FORK_TIMESTAMP]
110108
expected_intrinsics = [expected_pre, expected_post]
@@ -181,9 +179,8 @@ def test_creation_tx_intrinsic_across_amsterdam_transition(
181179
block, each from a fresh sender with the gas limit pinned exactly.
182180
Pre-fork the whole cost is regular intrinsic: ``TX_BASE`` plus the
183181
flat ``TX_CREATE``. Post-fork the intrinsic keeps only the
184-
``CREATE_ACCESS`` regular portion of ``TX_CREATE`` (plus the
185-
transfer-log charge when value moves), while the created account's
186-
``NEW_ACCOUNT`` is charged as *state* gas at the top frame — the
182+
``CREATE_ACCESS`` regular portion of ``TX_CREATE``, while the created
183+
account's ``NEW_ACCOUNT`` is charged as *state* gas at the top frame — the
187184
sender-facing total is the sum of both.
188185
189186
The per-fork costs are hand-derived from each fork's gas constants
@@ -217,8 +214,6 @@ def test_creation_tx_intrinsic_across_amsterdam_transition(
217214
+ (post_costs.TX_CREATE - post_costs.NEW_ACCOUNT)
218215
+ init_code_terms
219216
)
220-
if value:
221-
expected_post += post_costs.TRANSFER_LOG_COST
222217
expected_post_state = post_costs.NEW_ACCOUNT
223218

224219
timestamps = [PRE_FORK_TIMESTAMP, POST_FORK_TIMESTAMP]

tests/amsterdam/eip2780_reduce_intrinsic_tx_gas/test_value_moving_transactions.py

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,10 @@ def test_value_moving_transactions(
6767
``NEW_ACCOUNT`` state charge when value is transferred.
6868
6969
The EIP-7708 transfer log is asserted to fire exactly when
70-
``TRANSFER_LOG_COST`` is charged: for a non-self value transfer,
70+
``TX_VALUE_COST`` is charged: for a non-self value transfer,
7171
and never for a self-transfer (carve-out) or a zero-value tx.
7272
"""
73-
sender_initial_balance = 10**18
74-
sender = pre.fund_eoa(sender_initial_balance)
73+
sender = pre.fund_eoa()
7574
target = setup_target(pre, recipient_type, sender)
7675

7776
target_initial_balance = (
@@ -95,13 +94,12 @@ def test_value_moving_transactions(
9594
# spills entirely into regular gas.
9695
total_gas_cost = intrinsic_gas + top_frame_gas + top_frame_state_gas
9796

98-
tx_gas_limit = total_gas_cost + 1000 # add a small buffer
99-
gas_price = 1_000_000_000
97+
tx_gas_limit = total_gas_cost
10098

10199
is_self_transfer = recipient_type == RecipientType.SELF
102100

103101
# A transfer log is emitted iff value moves to a distinct account,
104-
# which is exactly when the intrinsic includes ``TRANSFER_LOG_COST``.
102+
# which is exactly when the intrinsic includes ``TX_VALUE_COST``.
105103
# ``logs=[]`` asserts no log fires for the carved-out cases.
106104
if value > 0 and not is_self_transfer:
107105
expected_logs = [transfer_log(sender, target, value)]
@@ -113,19 +111,13 @@ def test_value_moving_transactions(
113111
to=target,
114112
value=value,
115113
gas_limit=tx_gas_limit,
116-
gas_price=gas_price,
117-
expected_receipt=TransactionReceipt(logs=expected_logs),
118-
)
119-
120-
sender_value_delta = 0 if is_self_transfer else value
121-
sender_final_balance = (
122-
sender_initial_balance
123-
- sender_value_delta
124-
- total_gas_cost * gas_price
114+
expected_receipt=TransactionReceipt(
115+
cumulative_gas_used=tx_gas_limit, logs=expected_logs
116+
),
125117
)
126118

127119
post: dict[Address, Account | None] = {
128-
sender: Account(nonce=1, balance=sender_final_balance),
120+
sender: Account(nonce=1),
129121
}
130122
if not is_self_transfer:
131123
if recipient_type == RecipientType.EMPTY_ACCOUNT and value == 0:

0 commit comments

Comments
 (0)