1pub use context_interface::cfg::gas::*;
4
5#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
18#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
19pub struct Gas {
20 tracker: GasTracker,
22 memory: MemoryGas,
24}
25
26impl Gas {
27 #[inline]
31 pub const fn new(limit: u64) -> Self {
32 Self {
33 tracker: GasTracker::new(limit, limit, 0),
34 memory: MemoryGas::new(),
35 }
36 }
37
38 #[inline]
40 pub const fn tracker(&self) -> &GasTracker {
41 &self.tracker
42 }
43
44 #[inline]
46 pub const fn tracker_mut(&mut self) -> &mut GasTracker {
47 &mut self.tracker
48 }
49
50 #[inline]
61 pub const fn new_with_regular_gas_and_reservoir(limit: u64, reservoir: u64) -> Self {
62 Self {
63 tracker: GasTracker::new(limit, limit, reservoir),
64 memory: MemoryGas::new(),
65 }
66 }
67
68 #[inline]
70 pub const fn new_spent_with_reservoir(limit: u64, reservoir: u64) -> Self {
71 Self {
72 tracker: GasTracker::new(limit, 0, reservoir),
73 memory: MemoryGas::new(),
74 }
75 }
76
77 #[inline]
79 pub const fn limit(&self) -> u64 {
80 self.tracker.limit()
81 }
82
83 #[inline]
85 pub const fn memory(&self) -> &MemoryGas {
86 &self.memory
87 }
88
89 #[inline]
91 pub const fn memory_mut(&mut self) -> &mut MemoryGas {
92 &mut self.memory
93 }
94
95 #[inline]
97 pub const fn refunded(&self) -> i64 {
98 self.tracker.refunded()
99 }
100
101 #[inline]
103 #[deprecated(
104 since = "32.0.0",
105 note = "After EIP-8037 gas is split on
106 regular and state gas, this method is no longer valid.
107 Use [`Gas::total_gas_spent`] instead"
108 )]
109 pub const fn spent(&self) -> u64 {
110 self.tracker
111 .limit()
112 .saturating_sub(self.tracker.remaining())
113 }
114
115 #[inline]
117 pub const fn total_gas_spent(&self) -> u64 {
118 self.tracker
119 .limit()
120 .saturating_sub(self.tracker.remaining())
121 }
122
123 #[inline]
125 pub const fn used(&self) -> u64 {
126 self.total_gas_spent()
127 .saturating_sub(self.refunded() as u64)
128 }
129
130 #[inline]
132 pub const fn spent_sub_refunded(&self) -> u64 {
133 self.total_gas_spent()
134 .saturating_sub(self.tracker.refunded() as u64)
135 }
136
137 #[inline]
139 pub const fn remaining(&self) -> u64 {
140 self.tracker.remaining()
141 }
142
143 #[inline]
145 pub const fn reservoir(&self) -> u64 {
146 self.tracker.reservoir()
147 }
148
149 #[inline]
151 pub const fn set_reservoir(&mut self, val: u64) {
152 self.tracker.set_reservoir(val);
153 }
154
155 #[inline]
157 pub const fn state_gas_spent(&self) -> u64 {
158 self.tracker.state_gas_spent()
159 }
160
161 #[inline]
163 pub const fn set_state_gas_spent(&mut self, val: u64) {
164 self.tracker.set_state_gas_spent(val);
165 }
166
167 #[inline]
169 pub const fn erase_cost(&mut self, returned: u64) {
170 self.tracker.erase_cost(returned);
171 }
172
173 #[inline]
180 pub const fn spend_all(&mut self) {
181 self.tracker.spend_all();
182 }
183
184 #[inline]
189 pub const fn record_refund(&mut self, refund: i64) {
190 self.tracker.record_refund(refund);
191 }
192
193 #[inline]
199 pub fn set_final_refund(&mut self, is_london: bool) {
200 let max_refund_quotient = if is_london { 5 } else { 2 };
201 let gas_used = self.total_gas_spent().saturating_sub(self.reservoir());
203 self.tracker
204 .set_refunded((self.refunded() as u64).min(gas_used / max_refund_quotient) as i64);
205 }
206
207 #[inline]
209 pub const fn set_refund(&mut self, refund: i64) {
210 self.tracker.set_refunded(refund);
211 }
212
213 #[inline]
215 pub const fn set_remaining(&mut self, remaining: u64) {
216 self.tracker.set_remaining(remaining);
217 }
218
219 #[inline]
221 pub const fn set_spent(&mut self, spent: u64) {
222 self.tracker
223 .set_remaining(self.tracker.limit().saturating_sub(spent));
224 }
225
226 #[inline]
234 #[must_use = "prefer using `gas!` instead to return an out-of-gas error on failure"]
235 #[deprecated(since = "32.0.0", note = "use record_regular_cost instead")]
236 pub const fn record_cost(&mut self, cost: u64) -> bool {
237 self.record_regular_cost(cost)
238 }
239
240 #[inline(always)]
246 #[must_use = "In case of not enough gas, the interpreter should halt with an out-of-gas error"]
247 pub const fn record_cost_unsafe(&mut self, cost: u64) -> bool {
248 let remaining = self.tracker.remaining();
249 let oog = remaining < cost;
250 self.tracker.set_remaining(remaining.wrapping_sub(cost));
251 oog
252 }
253
254 #[inline]
262 #[must_use = "In case of not enough gas, the interpreter should halt with an out-of-gas error"]
263 pub const fn record_state_cost(&mut self, cost: u64) -> bool {
264 self.tracker.record_state_cost(cost)
265 }
266
267 #[inline]
271 #[must_use = "In case of not enough gas, the interpreter should halt with an out-of-gas error"]
272 pub const fn record_regular_cost(&mut self, cost: u64) -> bool {
273 self.tracker.record_regular_cost(cost)
274 }
275}
276
277#[derive(Debug)]
279pub enum MemoryExtensionResult {
280 Extended,
282 Same,
284 OutOfGas,
286}
287
288#[derive(Clone, Copy, Default, Debug, PartialEq, Eq, Hash)]
293#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
294pub struct MemoryGas {
295 pub words_num: usize,
297 pub expansion_cost: u64,
299}
300
301impl MemoryGas {
302 #[inline]
304 pub const fn new() -> Self {
305 Self {
306 words_num: 0,
307 expansion_cost: 0,
308 }
309 }
310
311 #[inline]
315 pub const fn set_words_num(
316 &mut self,
317 words_num: usize,
318 mut expansion_cost: u64,
319 ) -> Option<u64> {
320 self.words_num = words_num;
321 core::mem::swap(&mut self.expansion_cost, &mut expansion_cost);
322 self.expansion_cost.checked_sub(expansion_cost)
323 }
324}
325
326#[cfg(test)]
327mod tests {
328 use super::*;
329
330 #[test]
331 fn test_record_state_cost() {
332 let mut gas = Gas::new_with_regular_gas_and_reservoir(1000, 500);
334 assert!(gas.record_state_cost(200));
335 assert_eq!(
336 (gas.reservoir(), gas.remaining(), gas.state_gas_spent()),
337 (300, 1000, 200)
338 );
339
340 let mut gas = Gas::new_with_regular_gas_and_reservoir(1000, 500);
342 assert!(gas.record_state_cost(500));
343 assert_eq!(
344 (gas.reservoir(), gas.remaining(), gas.state_gas_spent()),
345 (0, 1000, 500)
346 );
347
348 let mut gas = Gas::new_with_regular_gas_and_reservoir(1000, 300);
350 assert!(gas.record_state_cost(500));
351 assert_eq!(
352 (gas.reservoir(), gas.remaining(), gas.state_gas_spent()),
353 (0, 800, 500)
354 );
355
356 let mut gas = Gas::new(1000);
358 assert!(gas.record_state_cost(200));
359 assert_eq!(
360 (gas.reservoir(), gas.remaining(), gas.state_gas_spent()),
361 (0, 800, 200)
362 );
363
364 let mut gas = Gas::new_with_regular_gas_and_reservoir(100, 50);
366 assert!(gas.record_state_cost(0));
367 assert_eq!(
368 (gas.reservoir(), gas.remaining(), gas.state_gas_spent()),
369 (50, 100, 0)
370 );
371
372 let mut gas = Gas::new_with_regular_gas_and_reservoir(100, 50);
374 assert!(!gas.record_state_cost(200));
375
376 let mut gas = Gas::new_with_regular_gas_and_reservoir(2000, 1000);
378 assert!(gas.record_state_cost(100));
379 assert!(gas.record_state_cost(200));
380 assert!(gas.record_state_cost(150));
381 assert_eq!(gas.state_gas_spent(), 450);
382
383 let mut gas = Gas::new_with_regular_gas_and_reservoir(500, 300);
385 assert!(gas.record_state_cost(150)); assert_eq!((gas.reservoir(), gas.remaining()), (150, 500));
387 assert!(gas.record_state_cost(200)); assert_eq!((gas.reservoir(), gas.remaining()), (0, 450));
389 assert!(gas.record_state_cost(100)); assert_eq!(
391 (gas.reservoir(), gas.remaining(), gas.state_gas_spent()),
392 (0, 350, 450)
393 );
394 }
395
396 #[test]
399 fn test_record_state_cost_oog_inflates_state_gas_spent() {
400 let mut gas = Gas::new(30);
402 assert!(!gas.record_state_cost(100));
403 assert_eq!(gas.state_gas_spent(), 0);
405
406 let mut gas = Gas::new_with_regular_gas_and_reservoir(30, 20);
409 assert!(!gas.record_state_cost(100));
410 assert_eq!(gas.state_gas_spent(), 0);
412 assert_eq!(gas.reservoir(), 20);
413 }
414
415 #[test]
417 fn test_record_state_cost_zero_remaining_with_reservoir() {
418 let mut gas = Gas::new_with_regular_gas_and_reservoir(0, 500);
420 assert!(gas.record_state_cost(200));
421 assert_eq!(
422 (gas.reservoir(), gas.remaining(), gas.state_gas_spent()),
423 (300, 0, 200)
424 );
425
426 assert!(gas.record_state_cost(300));
428 assert_eq!(
429 (gas.reservoir(), gas.remaining(), gas.state_gas_spent()),
430 (0, 0, 500)
431 );
432
433 assert!(!gas.record_state_cost(1));
435 }
436}