referrerpolicy=no-referrer-when-downgrade

pallet_revive/metering/
mod.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18mod gas;
19mod math;
20mod storage;
21mod weight;
22
23#[cfg(test)]
24mod tests;
25
26use crate::{
27	BalanceOf, Config, Error, ExecConfig, ExecOrigin as Origin, LOG_TARGET, StorageDeposit,
28	evm::fees::InfoT, exec::CallResources, storage::ContractInfo, vm::evm::Halt,
29};
30
31pub use gas::SignedGas;
32pub use storage::Diff;
33pub use weight::{ChargedAmount, Token};
34
35use frame_support::{DebugNoBound, DefaultNoBound};
36use num_traits::Zero;
37
38use core::{fmt::Debug, marker::PhantomData, ops::ControlFlow};
39use sp_runtime::{FixedPointNumber, Weight};
40use storage::{DepositOf, GenericMeter as GenericStorageMeter, Meter as RootStorageMeter};
41use weight::WeightMeter;
42
43use sp_runtime::{DispatchError, DispatchResult, FixedU128, SaturatedConversion};
44
45/// A type-state pattern ensuring that meters can only be used in valid states (root vs nested).
46///
47/// It is sealed and cannot be implemented outside of this module.
48pub trait State: private::Sealed + Default + Debug {}
49
50/// Root state for transaction-level resource metering.
51///
52/// Represents the top-level accounting of a transaction's resource usage.
53#[derive(Default, Debug)]
54pub struct Root;
55
56/// Nested state for frame-level resource metering.
57///
58/// Represents resource accounting for a single call frame.
59#[derive(Default, Debug)]
60pub struct Nested;
61
62impl State for Root {}
63impl State for Nested {}
64
65mod private {
66	pub trait Sealed {}
67	impl Sealed for super::Root {}
68	impl Sealed for super::Nested {}
69}
70
71/// The type of resource meter used at the root level for transactions as a whole.
72pub type TransactionMeter<T> = ResourceMeter<T, Root>;
73/// The type of resource meter used for an execution frame.
74pub type FrameMeter<T> = ResourceMeter<T, Nested>;
75
76/// Snapshot of a [`ResourceMeter`]'s consumption at a point in time.
77///
78/// Produced by [`ResourceMeter::snapshot`] and consumed by [`ResourceMeter::delta_since`].
79pub struct MeterSnapshot<T: Config> {
80	weight: Weight,
81	gas: SignedGas<T>,
82}
83
84/// Resource meter tracking weight and storage deposit consumption.
85#[derive(DefaultNoBound)]
86pub struct ResourceMeter<T: Config, S: State> {
87	/// The weight meter. Tracks consumed weight and weight limits.
88	weight: WeightMeter<T>,
89
90	/// The deposit meter. Tracks consumed storage deposit and storage deposit limits.
91	deposit: GenericStorageMeter<T, S>,
92
93	/// This is the maximum total consumable gas.
94	///
95	/// It is the sum of a) the total consumed gas (i.e., including all previous frames) at the
96	/// time the frame started and b) the gas limit of the frame. We don't store the gas limit of
97	/// the frame separately, it can be derived from `max_total_gas` by subtracting the total gas
98	/// at the beginning of the frame.
99	///
100	/// `max_total_gas` is only required for Ethereum execution, it is always zero for Substrate
101	/// executions.
102	max_total_gas: SignedGas<T>,
103
104	/// The total consumed weight at the time the frame started.
105	total_consumed_weight_before: Weight,
106
107	/// The total consumed storage deposit at the time the frame started.
108	total_consumed_deposit_before: DepositOf<T>,
109
110	/// The limits defined for the transaction. This determines whether this transaction uses the
111	/// Ethereum or Substrate execution mode.
112	transaction_limits: TransactionLimits<T>,
113
114	_phantom: PhantomData<S>,
115}
116
117/// Parameters required to construct a root [`TransactionMeter`].
118///
119/// Despite the name, the `EthereumGas` variant carries more than just limits: it also bundles
120/// the gas-conversion context (`eth_tx_info`) and any deposit already consumed before contract
121/// execution (`authorization_deposit`). It is the full set of inputs needed to build the root
122/// meter for an ethereum-style transaction, not a pure cap descriptor.
123///
124/// Represents the two supported resource accounting modes:
125/// - EthereumGas: Single gas limit
126/// - WeightAndDeposit: Explicit limits for both computational weight and storage deposit
127#[derive(DebugNoBound, Clone)]
128pub enum TransactionLimits<T: Config> {
129	/// Ethereum execution mode: the transaction only specifies a gas limit.
130	EthereumGas {
131		/// The Ethereum gas limit
132		eth_gas_limit: BalanceOf<T>,
133		/// The weight limit for this transaction. This ensures that execution will not exhaust
134		/// weight limit. This is required for eth_transact extrinsic execution to ensure that the
135		/// max extrinsic weights is not overstepped.
136		weight_limit: Weight,
137		/// Some extra information about the transaction that is required to calculate gas usage.
138		eth_tx_info: EthTxInfo<T>,
139		/// Net deposit movement caused by EIP-7702 authorization processing before contract
140		/// execution begins. Not a cap: applied to the meter at creation so the available deposit
141		/// budget reflects either a pre-charge (auths net to a charge) or a pre-credit (auths net
142		/// to a refund — e.g. pure-revoke). Only relevant at root meter construction; nested
143		/// frames do not see it.
144		authorization_deposit: StorageDeposit<BalanceOf<T>>,
145	},
146	/// Substrate execution mode: the transaction specifies a weight limit and a storage deposit
147	/// limit
148	WeightAndDeposit { weight_limit: Weight, deposit_limit: BalanceOf<T> },
149}
150
151impl<T: Config> Default for TransactionLimits<T> {
152	fn default() -> Self {
153		Self::WeightAndDeposit {
154			weight_limit: Default::default(),
155			deposit_limit: Default::default(),
156		}
157	}
158}
159
160impl<T: Config, S: State> ResourceMeter<T, S> {
161	/// Create a new nested meter with derived resource limits.
162	pub fn new_nested(&self, limit: &CallResources<T>) -> Result<FrameMeter<T>, DispatchError> {
163		log::trace!(
164			target: LOG_TARGET,
165			"Creating nested meter from parent: \
166				limit={limit:?}, \
167				weight_left={:?}, \
168				deposit_left={:?}, \
169				weight_consumed={:?}, \
170				deposit_consumed={:?}",
171			self.weight_left(),
172			self.deposit_left(),
173			self.weight_consumed(),
174			self.deposit_consumed(),
175		);
176
177		let mut new_meter = match &self.transaction_limits {
178			TransactionLimits::EthereumGas { eth_tx_info, .. } => {
179				math::ethereum_execution::new_nested_meter(self, limit, eth_tx_info)
180			},
181			TransactionLimits::WeightAndDeposit { .. } => {
182				math::substrate_execution::new_nested_meter(self, limit)
183			},
184		}?;
185
186		new_meter.adjust_effective_weight_limit()?;
187
188		log::trace!(
189			target: LOG_TARGET,
190			"Creating nested meter done: \
191				weight_left={:?}, \
192				deposit_left={:?}, \
193				weight_consumed={:?}, \
194				deposit_consumed={:?}",
195			new_meter.weight_left(),
196			new_meter.deposit_left(),
197			new_meter.weight_consumed(),
198			new_meter.deposit_consumed(),
199		);
200
201		Ok(new_meter)
202	}
203
204	/// Absorb only the weight consumption from a nested frame meter.
205	pub fn absorb_weight_meter_only(&mut self, other: FrameMeter<T>) {
206		log::trace!(
207			target: LOG_TARGET,
208			"Absorb weight meter only: \
209				parent_weight_left={:?}, \
210				parent_deposit_left={:?}, \
211				parent_weight_consumed={:?}, \
212				parent_deposit_consumed={:?}, \
213				child_weight_left={:?}, \
214				child_deposit_left={:?}, \
215				child_weight_consumed={:?}, \
216				child_deposit_consumed={:?}",
217			self.weight_left(),
218			self.deposit_left(),
219			self.weight_consumed(),
220			self.deposit_consumed(),
221			other.weight_left(),
222			other.deposit_left(),
223			other.weight_consumed(),
224			other.deposit_consumed(),
225		);
226
227		self.weight.absorb_nested(other.weight);
228		self.deposit.absorb_only_max_charged(other.deposit);
229
230		log::trace!(
231			target: LOG_TARGET,
232			"Absorb weight meter done: \
233				parent_weight_left={:?}, \
234				parent_deposit_left={:?}, \
235				parent_weight_consumed={:?}, \
236				parent_deposit_consumed={:?}",
237			self.weight_left(),
238			self.deposit_left(),
239			self.weight_consumed(),
240			self.deposit_consumed(),
241		);
242	}
243
244	/// Absorb all resource consumption from a nested frame meter.
245	pub fn absorb_all_meters(
246		&mut self,
247		other: FrameMeter<T>,
248		contract: &T::AccountId,
249		info: Option<&mut ContractInfo<T>>,
250	) {
251		log::trace!(
252			target: LOG_TARGET,
253			"Absorb all meters: \
254				parent_weight_left={:?}, \
255				parent_deposit_left={:?}, \
256				parent_weight_consumed={:?}, \
257				parent_deposit_consumed={:?}, \
258				child_weight_left={:?}, \
259				child_deposit_left={:?}, \
260				child_weight_consumed={:?}, \
261				child_deposit_consumed={:?}",
262			self.weight_left(),
263			self.deposit_left(),
264			self.weight_consumed(),
265			self.deposit_consumed(),
266			other.weight_left(),
267			other.deposit_left(),
268			other.weight_consumed(),
269			other.deposit_consumed(),
270		);
271
272		self.weight.absorb_nested(other.weight);
273		self.deposit.absorb(other.deposit, contract, info);
274
275		let result = self.adjust_effective_weight_limit();
276		debug_assert!(result.is_ok(), "Absorbing nested meters should not exceed limits");
277
278		log::trace!(
279			target: LOG_TARGET,
280			"Absorb all meters done: \
281				parent_weight_left={:?}, \
282				parent_deposit_left={:?}, \
283				parent_weight_consumed={:?}, \
284				parent_deposit_consumed={:?}",
285			self.weight_left(),
286			self.deposit_left(),
287			self.weight_consumed(),
288			self.deposit_consumed(),
289		);
290	}
291
292	/// Charge a weight token against this meter's remaining weight limit.
293	///
294	/// Returns `Err(Error::OutOfGas)` if the weight limit would be exceeded.
295	#[inline]
296	pub fn charge_weight_token<Tok: Token<T>>(
297		&mut self,
298		token: Tok,
299	) -> Result<ChargedAmount, DispatchError> {
300		self.weight.charge(token)
301	}
302
303	/// Try to charge a weight token or halt if not enough weight is left.
304	#[inline]
305	pub fn charge_or_halt<Tok: Token<T>>(
306		&mut self,
307		token: Tok,
308	) -> ControlFlow<Halt, ChargedAmount> {
309		self.weight.charge_or_halt(token)
310	}
311
312	/// Adjust an earlier weight charge with the actual weight consumed.
313	pub fn adjust_weight<Tok: Token<T>>(&mut self, charged_amount: ChargedAmount, token: Tok) {
314		self.weight.adjust_weight(charged_amount, token);
315	}
316
317	/// Synchronize meter state with PolkaVM executor's fuel consumption.
318	///
319	/// Maps the VM's internal fuel accounting to weight consumption:
320	/// - Converts engine fuel units to weight units
321	/// - Updates meter state to match actual VM resource usage
322	pub fn sync_from_executor(&mut self, engine_fuel: polkavm::Gas) -> Result<(), DispatchError> {
323		self.weight.sync_from_executor(engine_fuel)
324	}
325
326	/// Convert meter state to PolkaVM executor fuel units.
327	///
328	/// Prepares for VM execution by:
329	/// - Computing remaining available weight
330	/// - Converting weight units to VM fuel units and return
331	pub fn sync_to_executor(&mut self) -> polkavm::Gas {
332		self.weight.sync_to_executor()
333	}
334
335	/// Consume all remaining weight in the meter.
336	pub fn consume_all_weight(&mut self) {
337		self.weight.consume_all();
338	}
339
340	/// Record a storage deposit charge against this meter.
341	pub fn charge_deposit(&mut self, deposit: &DepositOf<T>) -> DispatchResult {
342		log::trace!(
343			target: LOG_TARGET,
344			"Charge deposit: \
345				deposit={:?}, \
346				deposit_left={:?}, \
347				deposit_consumed={:?}, \
348				max_charged={:?}",
349			deposit,
350			self.deposit_left(),
351			self.deposit_consumed(),
352			self.deposit.max_charged(),
353		);
354
355		if let StorageDeposit::Charge(amount) = deposit {
356			if self.deposit.is_root && self.deposit_left().map_or(true, |left| left < *amount) {
357				return Err(<Error<T>>::StorageDepositLimitExhausted.into());
358			}
359		}
360
361		self.deposit.record_charge(deposit);
362		self.adjust_effective_weight_limit()
363	}
364
365	/// Get remaining ethereum gas equivalent.
366	///
367	/// Converts remaining resources to ethereum gas units:
368	/// - For ethereum mode: computes directly from gas accounting
369	/// - For substrate mode: converts weight+deposit to gas equivalent
370	/// Returns None if resources are exhausted or conversion fails.
371	pub fn eth_gas_left(&self) -> Option<BalanceOf<T>> {
372		let gas_left = match &self.transaction_limits {
373			TransactionLimits::EthereumGas { eth_tx_info, .. } => {
374				math::ethereum_execution::gas_left(self, eth_tx_info)
375			},
376			TransactionLimits::WeightAndDeposit { .. } => math::substrate_execution::gas_left(self),
377		}?;
378
379		gas_left.to_ethereum_gas()
380	}
381
382	/// Get remaining weight available.
383	///
384	/// Computes remaining computational capacity:
385	/// - For ethereum mode: converts from gas to weight units
386	/// - For substrate mode: subtracts consumed from weight limit
387	/// Returns None if resources are exhausted.
388	pub fn weight_left(&self) -> Option<Weight> {
389		match &self.transaction_limits {
390			TransactionLimits::EthereumGas { eth_tx_info, .. } => {
391				math::ethereum_execution::weight_left(self, eth_tx_info)
392			},
393			TransactionLimits::WeightAndDeposit { .. } => {
394				math::substrate_execution::weight_left(self)
395			},
396		}
397	}
398
399	/// Get remaining deposit available.
400	///
401	/// Computes remaining storage deposit allowance:
402	/// - For ethereum mode: converts from gas to deposit units
403	/// - For substrate mode: subtracts consumed from deposit limit
404	/// Returns None if resources are exhausted.
405	pub fn deposit_left(&self) -> Option<BalanceOf<T>> {
406		match &self.transaction_limits {
407			TransactionLimits::EthereumGas { eth_tx_info, .. } => {
408				math::ethereum_execution::deposit_left(self, eth_tx_info)
409			},
410			TransactionLimits::WeightAndDeposit { .. } => {
411				math::substrate_execution::deposit_left(self)
412			},
413		}
414	}
415
416	/// Calculate total gas consumed so far.
417	///
418	/// Computes the ethereum-gas equivalent of all resource usage:
419	/// - Converts weight and deposit consumption to gas units
420	/// - For ethereum mode: uses direct gas accounting
421	/// - For substrate mode: synthesizes from weight+deposit usage
422	pub fn total_consumed_gas(&self) -> BalanceOf<T> {
423		let signed_gas = match &self.transaction_limits {
424			TransactionLimits::EthereumGas { eth_tx_info, .. } => {
425				math::ethereum_execution::total_consumed_gas(self, eth_tx_info)
426			},
427			TransactionLimits::WeightAndDeposit { .. } => {
428				math::substrate_execution::total_consumed_gas(self)
429			},
430		};
431
432		signed_gas.to_ethereum_gas().unwrap_or_default()
433	}
434
435	/// Get total weight consumed
436	pub fn weight_consumed(&self) -> Weight {
437		self.weight.weight_consumed()
438	}
439
440	/// Get total weight required
441	/// This is the maximum amount of weight consumption that occurred during execution so far
442	/// This is relevant because consumed weight can decrease in case it is asjusted a posteriori
443	/// for some operations
444	pub fn weight_required(&self) -> Weight {
445		self.weight.weight_required()
446	}
447
448	/// Get total storage deposit consumed in the current frame.
449	///
450	/// Returns the net storage deposit change from this frame,
451	pub fn deposit_consumed(&self) -> DepositOf<T> {
452		self.deposit.consumed()
453	}
454
455	/// Get maximum storage deposit required at any point.
456	///
457	/// Returns the highest deposit amount needed during execution,
458	/// accounting for temporary storage spikes before later refunds.
459	pub fn deposit_required(&self) -> DepositOf<T> {
460		self.deposit.max_charged()
461	}
462
463	/// Get the Ethereum gas that has been consumed during the lifetime of this meter
464	pub fn eth_gas_consumed(&self) -> BalanceOf<T> {
465		self.eth_gas_consumed_signed().to_ethereum_gas().unwrap_or_default()
466	}
467
468	/// Same as [`Self::eth_gas_consumed`] but returns the unrounded [`SignedGas`].
469	///
470	/// Prefer this when computing a delta across two snapshots: subtracting in [`SignedGas`] form
471	/// avoids the double ceil-rounding that [`Self::eth_gas_consumed`] performs at each call.
472	pub fn eth_gas_consumed_signed(&self) -> SignedGas<T> {
473		match &self.transaction_limits {
474			TransactionLimits::EthereumGas { eth_tx_info, .. } => {
475				math::ethereum_execution::eth_gas_consumed(self, eth_tx_info)
476			},
477			TransactionLimits::WeightAndDeposit { .. } => {
478				math::substrate_execution::eth_gas_consumed(self)
479			},
480		}
481	}
482
483	/// Take a snapshot of the meter's current consumption for later use with
484	/// [`Self::delta_since`].
485	pub fn snapshot(&self) -> MeterSnapshot<T> {
486		MeterSnapshot { weight: self.weight_consumed(), gas: self.eth_gas_consumed_signed() }
487	}
488
489	/// Ethereum gas and weight consumed since `snapshot` was taken.
490	///
491	/// Gas subtraction happens in [`SignedGas`] form so that the ceil-rounding inside
492	/// `to_ethereum_gas` is applied once to the delta, not to each snapshot.
493	pub fn delta_since(&self, snapshot: &MeterSnapshot<T>) -> (u64, Weight) {
494		let gas = self
495			.eth_gas_consumed_signed()
496			.saturating_sub(&snapshot.gas)
497			.to_ethereum_gas()
498			.unwrap_or_default()
499			.try_into()
500			.unwrap_or(u64::MAX);
501		let weight = self.weight_consumed().saturating_sub(snapshot.weight);
502		(gas, weight)
503	}
504
505	/// Determine and set the new effective weight limit of the weight meter.
506	///
507	/// This function needs to be called whenever there is a change in the deposit meter. It is a
508	/// function of `ResourceMeter` instead of `WeightMeter` because its outcome also depends on the
509	/// consumed storage deposits.
510	fn adjust_effective_weight_limit(&mut self) -> DispatchResult {
511		if matches!(self.transaction_limits, TransactionLimits::WeightAndDeposit { .. }) {
512			return Ok(());
513		}
514
515		if let Some(weight_left) = self.weight_left() {
516			let new_effective_limit = self.weight.weight_consumed().saturating_add(weight_left);
517			self.weight.set_effective_weight_limit(new_effective_limit);
518			Ok(())
519		} else {
520			Err(<Error<T>>::OutOfGas.into())
521		}
522	}
523}
524
525impl<T: Config> TransactionMeter<T> {
526	/// Create a new transaction-level meter with the specified resource limits.
527	///
528	/// Initializes either:
529	/// - An ethereum-style gas-based meter or
530	/// - A substrate-style meter with explicit weight and deposit limits
531	pub fn new(transaction_limits: TransactionLimits<T>) -> Result<Self, DispatchError> {
532		log::debug!(
533			target: LOG_TARGET,
534			"Start new meter: transaction_limits={transaction_limits:?}",
535		);
536
537		let mut transaction_meter = match transaction_limits {
538			TransactionLimits::EthereumGas {
539				eth_gas_limit,
540				weight_limit,
541				eth_tx_info,
542				authorization_deposit,
543			} => {
544				let mut meter =
545					math::ethereum_execution::new_root(eth_gas_limit, weight_limit, eth_tx_info)?;
546				if !authorization_deposit.is_zero() {
547					meter.deposit.record_charge(&authorization_deposit);
548				}
549				meter
550			},
551			TransactionLimits::WeightAndDeposit { weight_limit, deposit_limit } => {
552				math::substrate_execution::new_root(weight_limit, deposit_limit)?
553			},
554		};
555
556		transaction_meter.adjust_effective_weight_limit()?;
557
558		log::trace!(
559			target: LOG_TARGET,
560			"New meter done: \
561				weight_left={:?}, \
562				deposit_left={:?}, \
563				weight_consumed={:?}, \
564				deposit_consumed={:?}",
565			transaction_meter.weight_left(),
566			transaction_meter.deposit_left(),
567			transaction_meter.weight_consumed(),
568			transaction_meter.deposit_consumed(),
569		);
570
571		Ok(transaction_meter)
572	}
573
574	/// Convenience constructor for substrate-style weight+deposit limits.
575	pub fn new_from_limits(
576		weight_limit: Weight,
577		deposit_limit: BalanceOf<T>,
578	) -> Result<Self, DispatchError> {
579		Self::new(TransactionLimits::WeightAndDeposit { weight_limit, deposit_limit })
580	}
581
582	/// Execute all postponed storage deposit operations.
583	///
584	/// Returns `Err(Error::StorageDepositNotEnoughFunds)` if deposit limit would be exceeded.
585	pub fn execute_postponed_deposits(
586		&mut self,
587		origin: &Origin<T>,
588		exec_config: &ExecConfig<T>,
589	) -> Result<DepositOf<T>, DispatchError> {
590		log::debug!(
591			target: LOG_TARGET,
592			"Transaction meter finishes: \
593				weight_left={:?}, \
594				deposit_left={:?}, \
595				weight_consumed={:?}, \
596				deposit_consumed={:?}, \
597				eth_gas_consumed={:?}",
598			self.weight_left(),
599			self.deposit_left(),
600			self.weight_consumed(),
601			self.deposit_consumed(),
602			self.eth_gas_consumed(),
603		);
604
605		if self.deposit_left().is_none() {
606			// Deposit limit exceeded
607			return Err(<Error<T>>::StorageDepositNotEnoughFunds.into());
608		}
609
610		self.deposit.execute_postponed_deposits(origin, exec_config)
611	}
612
613	/// Mark a contract as terminated
614	///
615	/// This will signal to the meter to discard all charged and refunds incured by this
616	/// contract. Furthermore it will record that there was a refund of `refunded` and adapt the
617	/// total deposit accordingly
618	pub fn terminate(&mut self, contract_account: T::AccountId, refunded: BalanceOf<T>) {
619		self.deposit.terminate(contract_account, refunded);
620	}
621}
622
623impl<T: Config> FrameMeter<T> {
624	/// Record a contract's storage deposit and schedule the transfer.
625	///
626	/// Updates the frame's deposit accounting and schedules the actual token transfer
627	/// for later execution – at the end of the transaction execution.
628	pub fn charge_contract_deposit_and_transfer(
629		&mut self,
630		contract: T::AccountId,
631		amount: DepositOf<T>,
632	) -> DispatchResult {
633		log::trace!(
634			target: LOG_TARGET,
635			"Charge deposit and transfer: \
636				amount={:?}, \
637				deposit_left={:?}, \
638				deposit_consumed={:?}, \
639				max_charged={:?}",
640			amount,
641			self.deposit_left(),
642			self.deposit_consumed(),
643			self.deposit.max_charged(),
644		);
645
646		self.deposit.charge_deposit(contract, amount);
647		self.adjust_effective_weight_limit()
648	}
649
650	/// Record storage changes of a contract.
651	pub fn record_contract_storage_changes(&mut self, diff: &Diff) -> DispatchResult {
652		log::trace!(
653			target: LOG_TARGET,
654			"Charge contract storage: \
655				diff={:?}, \
656				deposit_left={:?}, \
657				deposit_consumed={:?}, \
658				max_charged={:?}",
659			diff,
660			self.deposit_left(),
661			self.deposit_consumed(),
662			self.deposit.max_charged(),
663		);
664
665		self.deposit.charge(diff);
666		self.adjust_effective_weight_limit()
667	}
668
669	/// [`Self::charge_contract_deposit_and_transfer`] and [`Self::record_contract_storage_changes`]
670	/// does not enforce the storage limit since we want to do this check as late as possible to
671	/// allow later refunds to offset earlier charges.
672	pub fn finalize(&mut self, info: Option<&mut ContractInfo<T>>) -> DispatchResult {
673		self.deposit.finalize_own_contributions(info);
674
675		if self.deposit_left().is_none() {
676			return Err(<Error<T>>::StorageDepositLimitExhausted.into());
677		}
678
679		Ok(())
680	}
681
682	/// Apply pending storage changes to a ContractInfo without finalizing the meter.
683	///
684	/// This is used before creating a nested frame to ensure the child frame can see
685	/// the parent's pending storage changes when calculating refunds. This fixes the issue
686	/// where storage deposit refunds fail in subframes because the parent's pending
687	/// charges haven't been committed to ContractInfo yet.
688	///
689	/// See: <https://github.com/paritytech/contract-issues/issues/213>
690	pub fn apply_pending_storage_changes(&self, info: &mut ContractInfo<T>) {
691		self.deposit.apply_pending_changes_to_contract(info);
692	}
693
694	/// See [`storage::RawMeter::bank_pending_changes`].
695	pub fn bank_pending_storage_changes(
696		&mut self,
697		contract: T::AccountId,
698		info: &mut ContractInfo<T>,
699	) {
700		self.deposit.bank_pending_changes(contract, info);
701	}
702}
703
704/// Ethereum transaction context for gas conversions.
705///
706/// Contains the parameters needed to convert between ethereum gas and substrate resources
707/// (weight/deposit)
708#[derive(DebugNoBound, Clone)]
709pub struct EthTxInfo<T: Config> {
710	/// The encoding length of the extrinsic
711	pub encoded_len: u32,
712	/// The extra weight of the transaction. The total weight of the extrinsic is `extra_weight` +
713	/// the weight consumed during smart contract execution.
714	pub extra_weight: Weight,
715	_phantom: PhantomData<T>,
716}
717
718impl<T: Config> EthTxInfo<T> {
719	/// Create a new ethereum transaction context with the given parameters.
720	pub fn new(encoded_len: u32, extra_weight: Weight) -> Self {
721		Self { encoded_len, extra_weight, _phantom: PhantomData }
722	}
723
724	/// Calculate total gas consumed by weight and storage operations.
725	pub fn gas_consumption(
726		&self,
727		consumed_weight: &Weight,
728		consumed_deposit: &DepositOf<T>,
729	) -> SignedGas<T> {
730		let fixed_fee = T::FeeInfo::fixed_fee(self.encoded_len);
731		let deposit_and_fixed_fee =
732			consumed_deposit.saturating_add(&DepositOf::<T>::Charge(fixed_fee));
733		let deposit_gas = SignedGas::from_adjusted_deposit_charge(&deposit_and_fixed_fee);
734
735		let weight_gas = SignedGas::from_weight_fee(T::FeeInfo::weight_to_fee(
736			&consumed_weight.saturating_add(self.extra_weight),
737		));
738
739		deposit_gas.saturating_add(&weight_gas)
740	}
741
742	/// Compute the maximum deposit available from a gas budget assuming zero execution weight
743	/// and zero deposit consumed. This is the upper bound on how much deposit the transaction
744	/// could ever spend.
745	pub fn max_deposit(&self, eth_gas_limit: BalanceOf<T>) -> BalanceOf<T> {
746		let max_gas = SignedGas::<T>::from_ethereum_gas(eth_gas_limit);
747		let overhead_gas =
748			self.gas_consumption(&Weight::zero(), &DepositOf::<T>::Charge(Zero::zero()));
749		let remaining = max_gas.saturating_sub(&overhead_gas);
750		remaining.to_adjusted_deposit_charge().unwrap_or_default()
751	}
752
753	/// Calculate maximal possible remaining weight that can be consumed given a particular gas
754	/// limit.
755	///
756	/// Returns None if remaining gas would not allow any more weight consumption.
757	pub fn weight_remaining(
758		&self,
759		max_total_gas: &SignedGas<T>,
760		total_weight_consumption: &Weight,
761		total_deposit_consumption: &DepositOf<T>,
762	) -> Option<Weight> {
763		let fixed_fee = T::FeeInfo::fixed_fee(self.encoded_len);
764		let deposit_and_fixed_fee =
765			total_deposit_consumption.saturating_add(&DepositOf::<T>::Charge(fixed_fee));
766		let deposit_gas = SignedGas::from_adjusted_deposit_charge(&deposit_and_fixed_fee);
767
768		let consumable_fee = max_total_gas.saturating_sub(&deposit_gas).to_weight_fee()?;
769
770		T::FeeInfo::fee_to_weight(consumable_fee)
771			.checked_sub(&total_weight_consumption.saturating_add(self.extra_weight))
772	}
773}