revm_primitives/eip8038.rs
1//! EIP-8038: State-Access Gas Cost Update
2//!
3//! Increases the gas cost of state-access operations to reflect Ethereum's
4//! larger state. The values below match the glamsterdam devnet-8 numbers in
5//! execution-specs `forks/amsterdam` (`vm/gas.py::GasCosts`).
6//!
7//! Active alongside EIP-7904 and EIP-8037 starting at the Amsterdam hardfork.
8
9/// Cold touch of an account (was 2,600 pre-EIP-8038).
10pub const COLD_ACCOUNT_ACCESS: u64 = 3_000;
11
12/// Surcharge for writing to an account that changes one account leaf value for
13/// the first time (was 6,700 pre-EIP-8038).
14pub const ACCOUNT_WRITE: u64 = 9_000;
15
16/// Cold touch of a storage slot. Unchanged from the pre-EIP-8038 cost, but
17/// under EIP-8038 it is the *total* cold charge (the warm base is folded in).
18pub const COLD_STORAGE_ACCESS: u64 = 2_100;
19
20/// Surcharge for writing to a storage slot that changes its value for the
21/// first time (was 2,800 pre-EIP-8038).
22pub const STORAGE_WRITE: u64 = 10_000;
23
24/// Touch of an already-warm account or storage slot. Unchanged by EIP-8038.
25pub const WARM_ACCESS: u64 = 100;
26
27/// Refund for clearing a storage slot (was 4,800 pre-EIP-8038).
28///
29/// Derived per the spec as `(STORAGE_WRITE + COLD_STORAGE_ACCESS) * 4800 / 5000`.
30pub const STORAGE_CLEAR_REFUND: u64 = (STORAGE_WRITE + COLD_STORAGE_ACCESS) * 4_800 / 5_000;
31
32/// State access cost for contract deployment (was 7,000 pre-EIP-8038).
33///
34/// Per the spec, `CREATE_ACCESS = ACCOUNT_WRITE + COLD_ACCOUNT_ACCESS`. This does
35/// not match the legacy decomposition (`GAS_CREATE - GAS_NEW_ACCOUNT = 7,000`); the
36/// EIP keeps that discrepancy rather than reconciling it.
37pub const CREATE_ACCESS: u64 = ACCOUNT_WRITE + COLD_ACCOUNT_ACCESS;
38
39/// Gas charged per storage key included in a transaction's access list
40/// (was 1,900 pre-EIP-8038). Derived per the spec as
41/// `COLD_STORAGE_ACCESS - WARM_ACCESS`: the access list pre-pays only the cold
42/// premium, the warm base is still charged at first use.
43pub const ACCESS_LIST_STORAGE_KEY_COST: u64 = COLD_STORAGE_ACCESS - WARM_ACCESS;
44
45/// Gas charged per address included in a transaction's access list
46/// (was 2,400 pre-EIP-8038). Derived per the spec as
47/// `COLD_ACCOUNT_ACCESS - WARM_ACCESS`.
48pub const ACCESS_LIST_ADDRESS_COST: u64 = COLD_ACCOUNT_ACCESS - WARM_ACCESS;
49
50/// Cold premium on top of `WARM_ACCESS` for account access.
51pub const COLD_ACCOUNT_ACCESS_ADDITIONAL: u64 = COLD_ACCOUNT_ACCESS - WARM_ACCESS;
52
53/// Cold premium on top of `WARM_ACCESS` for storage access.
54pub const COLD_STORAGE_ACCESS_ADDITIONAL: u64 = COLD_STORAGE_ACCESS - WARM_ACCESS;
55
56/// CALL value transfer cost: `ACCOUNT_WRITE + CALL_STIPEND` per the EIP.
57pub const CALL_VALUE: u64 = ACCOUNT_WRITE + 2_300;
58
59/// Calldata bytes charged for one EIP-7702 authorization tuple (execution-specs
60/// `AUTH_TUPLE_BYTES`): chain id, authority address, nonce, signature parity, and
61/// the two signature scalars. Charged at the calldata floor rate.
62pub const EIP7702_AUTH_TUPLE_BYTES: u64 = 101;
63
64/// ecRecover precompile base cost, charged once per EIP-7702 authorization to
65/// recover the authority.
66pub const EIP7702_ECRECOVER_COST: u64 = 3_000;
67
68/// Calldata floor rate per token under EIP-7976 (Amsterdam).
69pub const TX_DATA_TOKEN_FLOOR: u64 = 16;
70
71/// `REGULAR_PER_AUTH_BASE_COST`: the state-independent regular-gas base charged
72/// per EIP-7702 authorization at the intrinsic phase under EIP-2780
73/// (ethereum/EIPs#11844).
74///
75/// `AUTH_TUPLE_BYTES * TX_DATA_TOKEN_FLOOR + PRECOMPILE_ECRECOVER + COLD_ACCOUNT_ACCESS + 2 * WARM_ACCESS`
76/// = `101*16 + 3,000 + 3,000 + 200 = 7,816`. Covers the authorization's calldata,
77/// the ECDSA recovery, the authority's cold access, and the warm writes every
78/// authorization performs. The state-dependent remainder (`ACCOUNT_WRITE` and the
79/// new-account / delegation-bytes state gas) is charged at runtime, only for the
80/// authorities that incur it.
81pub const EIP7702_PER_AUTH_BASE_REGULAR: u64 = EIP7702_AUTH_TUPLE_BYTES * TX_DATA_TOKEN_FLOOR
82 + EIP7702_ECRECOVER_COST
83 + COLD_ACCOUNT_ACCESS
84 + 2 * WARM_ACCESS;
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89
90 /// Values must match execution-specs `forks/amsterdam` (glamsterdam devnet-8).
91 #[test]
92 fn constants_match_spec() {
93 assert_eq!(WARM_ACCESS, 100); // unchanged by EIP-8038
94 assert_eq!(COLD_ACCOUNT_ACCESS, 3_000);
95 assert_eq!(ACCOUNT_WRITE, 9_000);
96 assert_eq!(COLD_STORAGE_ACCESS, 2_100);
97 assert_eq!(STORAGE_WRITE, 10_000);
98 assert_eq!(STORAGE_CLEAR_REFUND, 11_616);
99 assert_eq!(CREATE_ACCESS, 12_000);
100 assert_eq!(ACCESS_LIST_ADDRESS_COST, 2_900);
101 assert_eq!(ACCESS_LIST_STORAGE_KEY_COST, 2_000);
102 assert_eq!(CALL_VALUE, 11_300);
103 assert_eq!(EIP7702_PER_AUTH_BASE_REGULAR, 7_816);
104 }
105
106 /// Spec-defined relationships between the parameters (kept as derivations so a
107 /// renumber of one base value propagates correctly).
108 #[test]
109 fn derived_relations() {
110 assert_eq!(CREATE_ACCESS, ACCOUNT_WRITE + COLD_ACCOUNT_ACCESS);
111 assert_eq!(CALL_VALUE, ACCOUNT_WRITE + 2_300);
112 assert_eq!(ACCESS_LIST_ADDRESS_COST, COLD_ACCOUNT_ACCESS - WARM_ACCESS);
113 assert_eq!(
114 ACCESS_LIST_STORAGE_KEY_COST,
115 COLD_STORAGE_ACCESS - WARM_ACCESS
116 );
117 assert_eq!(
118 STORAGE_CLEAR_REFUND,
119 (STORAGE_WRITE + COLD_STORAGE_ACCESS) * 4_800 / 5_000
120 );
121 assert_eq!(
122 COLD_ACCOUNT_ACCESS_ADDITIONAL,
123 COLD_ACCOUNT_ACCESS - WARM_ACCESS
124 );
125 assert_eq!(
126 COLD_STORAGE_ACCESS_ADDITIONAL,
127 COLD_STORAGE_ACCESS - WARM_ACCESS
128 );
129 }
130}