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	evm::fees::InfoT, exec::CallResources, storage::ContractInfo, vm::evm::Halt, BalanceOf, Config,
28	Error, ExecConfig, ExecOrigin as Origin, StorageDeposit, LOG_TARGET,
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/// Resource meter tracking weight and storage deposit consumption.
77#[derive(DefaultNoBound)]
78pub struct ResourceMeter<T: Config, S: State> {
79	/// The weight meter. Tracks consumed weight and weight limits.
80	weight: WeightMeter<T>,
81
82	/// The deposit meter. Tracks consumed storage deposit and storage deposit limits.
83	deposit: GenericStorageMeter<T, S>,
84
85	/// This is the maximum total consumable gas.
86	///
87	/// It is the sum of a) the total consumed gas (i.e., including all previous frames) at the
88	/// time the frame started and b) the gas limit of the frame. We don't store the gas limit of
89	/// the frame separately, it can be derived from `max_total_gas` by subtracting the total gas
90	/// at the beginning of the frame.
91	///
92	/// `max_total_gas` is only required for Ethereum execution, it is always zero for Substrate
93	/// executions.
94	max_total_gas: SignedGas<T>,
95
96	/// The total consumed weight at the time the frame started.
97	total_consumed_weight_before: Weight,
98
99	/// The total consumed storage deposit at the time the frame started.
100	total_consumed_deposit_before: DepositOf<T>,
101
102	/// The limits defined for the transaction. This determines whether this transaction uses the
103	/// Ethereum or Substrate execution mode.
104	transaction_limits: TransactionLimits<T>,
105
106	_phantom: PhantomData<S>,
107}
108
109/// Transaction-wide resource limit configuration.
110///
111/// Represents the two supported resource accounting modes:
112/// - EthereumGas: Single gas limit
113/// - WeightAndDeposit: Explicit limits for both computational weight and storage deposit
114#[derive(DebugNoBound, Clone)]
115pub enum TransactionLimits<T: Config> {
116	/// Ethereum execution mode: the transaction only specifies a gas limit.
117	EthereumGas {
118		/// The Ethereum gas limit
119		eth_gas_limit: BalanceOf<T>,
120		/// If this is provided, we will additionally ensure that execution will not exhaust this
121		/// weight limit. This is required for eth_transact extrinsic execution to ensure that the
122		/// max extrinsic weights is not overstepped.
123		maybe_weight_limit: Option<Weight>,
124		/// Some extra information about the transaction that is required to calculate gas usage.
125		eth_tx_info: EthTxInfo<T>,
126	},
127	/// Substrate execution mode: the transaction specifies a weight limit and a storage deposit
128	/// limit
129	WeightAndDeposit { weight_limit: Weight, deposit_limit: BalanceOf<T> },
130}
131
132impl<T: Config> Default for TransactionLimits<T> {
133	fn default() -> Self {
134		Self::WeightAndDeposit {
135			weight_limit: Default::default(),
136			deposit_limit: Default::default(),
137		}
138	}
139}
140
141impl<T: Config, S: State> ResourceMeter<T, S> {
142	/// Create a new nested meter with derived resource limits.
143	pub fn new_nested(&self, limit: &CallResources<T>) -> Result<FrameMeter<T>, DispatchError> {
144		log::trace!(
145			target: LOG_TARGET,
146			"Creating nested meter from parent: \
147				limit={limit:?}, \
148				weight_left={:?}, \
149				deposit_left={:?}, \
150				weight_consumed={:?}, \
151				deposit_consumed={:?}",
152			self.weight_left(),
153			self.deposit_left(),
154			self.weight_consumed(),
155			self.deposit_consumed(),
156		);
157
158		let mut new_meter = match &self.transaction_limits {
159			TransactionLimits::EthereumGas { eth_tx_info, .. } =>
160				math::ethereum_execution::new_nested_meter(self, limit, eth_tx_info),
161			TransactionLimits::WeightAndDeposit { .. } =>
162				math::substrate_execution::new_nested_meter(self, limit),
163		}?;
164
165		new_meter.adjust_effective_weight_limit()?;
166
167		log::trace!(
168			target: LOG_TARGET,
169			"Creating nested meter done: \
170				weight_left={:?}, \
171				deposit_left={:?}, \
172				weight_consumed={:?}, \
173				deposit_consumed={:?}",
174			new_meter.weight_left(),
175			new_meter.deposit_left(),
176			new_meter.weight_consumed(),
177			new_meter.deposit_consumed(),
178		);
179
180		Ok(new_meter)
181	}
182
183	/// Absorb only the weight consumption from a nested frame meter.
184	pub fn absorb_weight_meter_only(&mut self, other: FrameMeter<T>) {
185		log::trace!(
186			target: LOG_TARGET,
187			"Absorb weight meter only: \
188				parent_weight_left={:?}, \
189				parent_deposit_left={:?}, \
190				parent_weight_consumed={:?}, \
191				parent_deposit_consumed={:?}, \
192				child_weight_left={:?}, \
193				child_deposit_left={:?}, \
194				child_weight_consumed={:?}, \
195				child_deposit_consumed={:?}",
196			self.weight_left(),
197			self.deposit_left(),
198			self.weight_consumed(),
199			self.deposit_consumed(),
200			other.weight_left(),
201			other.deposit_left(),
202			other.weight_consumed(),
203			other.deposit_consumed(),
204		);
205
206		self.weight.absorb_nested(other.weight);
207		self.deposit.absorb_only_max_charged(other.deposit);
208
209		log::trace!(
210			target: LOG_TARGET,
211			"Absorb weight meter done: \
212				parent_weight_left={:?}, \
213				parent_deposit_left={:?}, \
214				parent_weight_consumed={:?}, \
215				parent_deposit_consumed={:?}",
216			self.weight_left(),
217			self.deposit_left(),
218			self.weight_consumed(),
219			self.deposit_consumed(),
220		);
221	}
222
223	/// Absorb all resource consumption from a nested frame meter.
224	pub fn absorb_all_meters(
225		&mut self,
226		other: FrameMeter<T>,
227		contract: &T::AccountId,
228		info: Option<&mut ContractInfo<T>>,
229	) {
230		log::trace!(
231			target: LOG_TARGET,
232			"Absorb all meters: \
233				parent_weight_left={:?}, \
234				parent_deposit_left={:?}, \
235				parent_weight_consumed={:?}, \
236				parent_deposit_consumed={:?}, \
237				child_weight_left={:?}, \
238				child_deposit_left={:?}, \
239				child_weight_consumed={:?}, \
240				child_deposit_consumed={:?}",
241			self.weight_left(),
242			self.deposit_left(),
243			self.weight_consumed(),
244			self.deposit_consumed(),
245			other.weight_left(),
246			other.deposit_left(),
247			other.weight_consumed(),
248			other.deposit_consumed(),
249		);
250
251		self.weight.absorb_nested(other.weight);
252		self.deposit.absorb(other.deposit, contract, info);
253
254		let result = self.adjust_effective_weight_limit();
255		debug_assert!(result.is_ok(), "Absorbing nested meters should not exceed limits");
256
257		log::trace!(
258			target: LOG_TARGET,
259			"Absorb all meters done: \
260				parent_weight_left={:?}, \
261				parent_deposit_left={:?}, \
262				parent_weight_consumed={:?}, \
263				parent_deposit_consumed={:?}",
264			self.weight_left(),
265			self.deposit_left(),
266			self.weight_consumed(),
267			self.deposit_consumed(),
268		);
269	}
270
271	/// Charge a weight token against this meter's remaining weight limit.
272	///
273	/// Returns `Err(Error::OutOfGas)` if the weight limit would be exceeded.
274	#[inline]
275	pub fn charge_weight_token<Tok: Token<T>>(
276		&mut self,
277		token: Tok,
278	) -> Result<ChargedAmount, DispatchError> {
279		self.weight.charge(token)
280	}
281
282	/// Try to charge a weight token or halt if not enough weight is left.
283	#[inline]
284	pub fn charge_or_halt<Tok: Token<T>>(
285		&mut self,
286		token: Tok,
287	) -> ControlFlow<Halt, ChargedAmount> {
288		self.weight.charge_or_halt(token)
289	}
290
291	/// Adjust an earlier weight charge with the actual weight consumed.
292	pub fn adjust_weight<Tok: Token<T>>(&mut self, charged_amount: ChargedAmount, token: Tok) {
293		self.weight.adjust_weight(charged_amount, token);
294	}
295
296	/// Synchronize meter state with PolkaVM executor's fuel consumption.
297	///
298	/// Maps the VM's internal fuel accounting to weight consumption:
299	/// - Converts engine fuel units to weight units
300	/// - Updates meter state to match actual VM resource usage
301	pub fn sync_from_executor(&mut self, engine_fuel: polkavm::Gas) -> Result<(), DispatchError> {
302		self.weight.sync_from_executor(engine_fuel)
303	}
304
305	/// Convert meter state to PolkaVM executor fuel units.
306	///
307	/// Prepares for VM execution by:
308	/// - Computing remaining available weight
309	/// - Converting weight units to VM fuel units and return
310	pub fn sync_to_executor(&mut self) -> polkavm::Gas {
311		self.weight.sync_to_executor()
312	}
313
314	/// Consume all remaining weight in the meter.
315	pub fn consume_all_weight(&mut self) {
316		self.weight.consume_all();
317	}
318
319	/// Record a storage deposit charge against this meter.
320	pub fn charge_deposit(&mut self, deposit: &DepositOf<T>) -> DispatchResult {
321		log::trace!(
322			target: LOG_TARGET,
323			"Charge deposit: \
324				deposit={:?}, \
325				deposit_left={:?}, \
326				deposit_consumed={:?}, \
327				max_charged={:?}",
328			deposit,
329			self.deposit_left(),
330			self.deposit_consumed(),
331			self.deposit.max_charged(),
332		);
333
334		self.deposit.record_charge(deposit);
335		self.adjust_effective_weight_limit()?;
336
337		if self.deposit.is_root {
338			if self.deposit_left().is_none() {
339				self.deposit.reset();
340				self.adjust_effective_weight_limit()?;
341				return Err(<Error<T>>::StorageDepositLimitExhausted.into());
342			}
343		}
344
345		Ok(())
346	}
347
348	/// Get remaining ethereum gas equivalent.
349	///
350	/// Converts remaining resources to ethereum gas units:
351	/// - For ethereum mode: computes directly from gas accounting
352	/// - For substrate mode: converts weight+deposit to gas equivalent
353	/// Returns None if resources are exhausted or conversion fails.
354	pub fn eth_gas_left(&self) -> Option<BalanceOf<T>> {
355		let gas_left = match &self.transaction_limits {
356			TransactionLimits::EthereumGas { eth_tx_info, .. } =>
357				math::ethereum_execution::gas_left(self, eth_tx_info),
358			TransactionLimits::WeightAndDeposit { .. } => math::substrate_execution::gas_left(self),
359		}?;
360
361		gas_left.to_ethereum_gas()
362	}
363
364	/// Get remaining weight available.
365	///
366	/// Computes remaining computational capacity:
367	/// - For ethereum mode: converts from gas to weight units
368	/// - For substrate mode: subtracts consumed from weight limit
369	/// Returns None if resources are exhausted.
370	pub fn weight_left(&self) -> Option<Weight> {
371		match &self.transaction_limits {
372			TransactionLimits::EthereumGas { eth_tx_info, .. } =>
373				math::ethereum_execution::weight_left(self, eth_tx_info),
374			TransactionLimits::WeightAndDeposit { .. } =>
375				math::substrate_execution::weight_left(self),
376		}
377	}
378
379	/// Get remaining deposit available.
380	///
381	/// Computes remaining storage deposit allowance:
382	/// - For ethereum mode: converts from gas to deposit units
383	/// - For substrate mode: subtracts consumed from deposit limit
384	/// Returns None if resources are exhausted.
385	pub fn deposit_left(&self) -> Option<BalanceOf<T>> {
386		match &self.transaction_limits {
387			TransactionLimits::EthereumGas { eth_tx_info, .. } =>
388				math::ethereum_execution::deposit_left(self, eth_tx_info),
389			TransactionLimits::WeightAndDeposit { .. } =>
390				math::substrate_execution::deposit_left(self),
391		}
392	}
393
394	/// Calculate total gas consumed so far.
395	///
396	/// Computes the ethereum-gas equivalent of all resource usage:
397	/// - Converts weight and deposit consumption to gas units
398	/// - For ethereum mode: uses direct gas accounting
399	/// - For substrate mode: synthesizes from weight+deposit usage
400	pub fn total_consumed_gas(&self) -> BalanceOf<T> {
401		let signed_gas = match &self.transaction_limits {
402			TransactionLimits::EthereumGas { eth_tx_info, .. } =>
403				math::ethereum_execution::total_consumed_gas(self, eth_tx_info),
404			TransactionLimits::WeightAndDeposit { .. } =>
405				math::substrate_execution::total_consumed_gas(self),
406		};
407
408		signed_gas.to_ethereum_gas().unwrap_or_default()
409	}
410
411	/// Get total weight consumed
412	pub fn weight_consumed(&self) -> Weight {
413		self.weight.weight_consumed()
414	}
415
416	/// Get total weight required
417	/// This is the maximum amount of weight consumption that occurred during execution so far
418	/// This is relevant because consumed weight can decrease in case it is asjusted a posteriori
419	/// for some operations
420	pub fn weight_required(&self) -> Weight {
421		self.weight.weight_required()
422	}
423
424	/// Get total storage deposit consumed in the current frame.
425	///
426	/// Returns the net storage deposit change from this frame,
427	pub fn deposit_consumed(&self) -> DepositOf<T> {
428		self.deposit.consumed()
429	}
430
431	/// Get maximum storage deposit required at any point.
432	///
433	/// Returns the highest deposit amount needed during execution,
434	/// accounting for temporary storage spikes before later refunds.
435	pub fn deposit_required(&self) -> DepositOf<T> {
436		self.deposit.max_charged()
437	}
438
439	/// Get the Ethereum gas that has been consumed during the lifetime of this meter
440	pub fn eth_gas_consumed(&self) -> BalanceOf<T> {
441		let signed_gas = match &self.transaction_limits {
442			TransactionLimits::EthereumGas { eth_tx_info, .. } =>
443				math::ethereum_execution::eth_gas_consumed(self, eth_tx_info),
444			TransactionLimits::WeightAndDeposit { .. } =>
445				math::substrate_execution::eth_gas_consumed(self),
446		};
447
448		signed_gas.to_ethereum_gas().unwrap_or_default()
449	}
450
451	/// Determine and set the new effective weight limit of the weight meter.
452	///
453	/// This function needs to be called whenever there is a change in the deposit meter. It is a
454	/// function of `ResourceMeter` instead of `WeightMeter` because its outcome also depends on the
455	/// consumed storage deposits.
456	fn adjust_effective_weight_limit(&mut self) -> DispatchResult {
457		if matches!(self.transaction_limits, TransactionLimits::WeightAndDeposit { .. }) {
458			return Ok(())
459		}
460
461		if let Some(weight_left) = self.weight_left() {
462			let new_effective_limit = self.weight.weight_consumed().saturating_add(weight_left);
463			self.weight.set_effective_weight_limit(new_effective_limit);
464			Ok(())
465		} else {
466			Err(<Error<T>>::OutOfGas.into())
467		}
468	}
469}
470
471impl<T: Config> TransactionMeter<T> {
472	/// Create a new transaction-level meter with the specified resource limits.
473	///
474	/// Initializes either:
475	/// - An ethereum-style gas-based meter or
476	/// - A substrate-style meter with explicit weight and deposit limits
477	pub fn new(transaction_limits: TransactionLimits<T>) -> Result<Self, DispatchError> {
478		log::debug!(
479			target: LOG_TARGET,
480			"Start new meter: transaction_limits={transaction_limits:?}",
481		);
482
483		let mut transaction_meter = match transaction_limits {
484			TransactionLimits::EthereumGas { eth_gas_limit, maybe_weight_limit, eth_tx_info } =>
485				math::ethereum_execution::new_root(eth_gas_limit, maybe_weight_limit, eth_tx_info),
486			TransactionLimits::WeightAndDeposit { weight_limit, deposit_limit } =>
487				math::substrate_execution::new_root(weight_limit, deposit_limit),
488		}?;
489
490		transaction_meter.adjust_effective_weight_limit()?;
491
492		log::trace!(
493			target: LOG_TARGET,
494			"New meter done: \
495				weight_left={:?}, \
496				deposit_left={:?}, \
497				weight_consumed={:?}, \
498				deposit_consumed={:?}",
499			transaction_meter.weight_left(),
500			transaction_meter.deposit_left(),
501			transaction_meter.weight_consumed(),
502			transaction_meter.deposit_consumed(),
503		);
504
505		Ok(transaction_meter)
506	}
507
508	/// Convenience constructor for substrate-style weight+deposit limits.
509	pub fn new_from_limits(
510		weight_limit: Weight,
511		deposit_limit: BalanceOf<T>,
512	) -> Result<Self, DispatchError> {
513		Self::new(TransactionLimits::WeightAndDeposit { weight_limit, deposit_limit })
514	}
515
516	/// Execute all postponed storage deposit operations.
517	///
518	/// Returns `Err(Error::StorageDepositNotEnoughFunds)` if deposit limit would be exceeded.
519	pub fn execute_postponed_deposits(
520		&mut self,
521		origin: &Origin<T>,
522		exec_config: &ExecConfig<T>,
523	) -> Result<DepositOf<T>, DispatchError> {
524		log::debug!(
525			target: LOG_TARGET,
526			"Transaction meter finishes: \
527				weight_left={:?}, \
528				deposit_left={:?}, \
529				weight_consumed={:?}, \
530				deposit_consumed={:?}, \
531				eth_gas_consumed={:?}",
532			self.weight_left(),
533			self.deposit_left(),
534			self.weight_consumed(),
535			self.deposit_consumed(),
536			self.eth_gas_consumed(),
537		);
538
539		if self.deposit_left().is_none() {
540			// Deposit limit exceeded
541			return Err(<Error<T>>::StorageDepositNotEnoughFunds.into());
542		}
543
544		self.deposit.execute_postponed_deposits(origin, exec_config)
545	}
546
547	/// Mark a contract as terminated
548	///
549	/// This will signal to the meter to discard all charged and refunds incured by this
550	/// contract. Furthermore it will record that there was a refund of `refunded` and adapt the
551	/// total deposit accordingly
552	pub fn terminate(&mut self, contract_account: T::AccountId, refunded: BalanceOf<T>) {
553		self.deposit.terminate(contract_account, refunded);
554	}
555}
556
557impl<T: Config> FrameMeter<T> {
558	/// Record a contract's storage deposit and schedule the transfer.
559	///
560	/// Updates the frame's deposit accounting and schedules the actual token transfer
561	/// for later execution – at the end of the transaction execution.
562	pub fn charge_contract_deposit_and_transfer(
563		&mut self,
564		contract: T::AccountId,
565		amount: DepositOf<T>,
566	) -> DispatchResult {
567		log::trace!(
568			target: LOG_TARGET,
569			"Charge deposit and transfer: \
570				amount={:?}, \
571				deposit_left={:?}, \
572				deposit_consumed={:?}, \
573				max_charged={:?}",
574			amount,
575			self.deposit_left(),
576			self.deposit_consumed(),
577			self.deposit.max_charged(),
578		);
579
580		self.deposit.charge_deposit(contract, amount);
581		self.adjust_effective_weight_limit()
582	}
583
584	/// Record storage changes of a contract.
585	pub fn record_contract_storage_changes(&mut self, diff: &Diff) -> DispatchResult {
586		log::trace!(
587			target: LOG_TARGET,
588			"Charge contract storage: \
589				diff={:?}, \
590				deposit_left={:?}, \
591				deposit_consumed={:?}, \
592				max_charged={:?}",
593			diff,
594			self.deposit_left(),
595			self.deposit_consumed(),
596			self.deposit.max_charged(),
597		);
598
599		self.deposit.charge(diff);
600		self.adjust_effective_weight_limit()
601	}
602
603	/// [`Self::charge_contract_deposit_and_transfer`] and [`Self::record_contract_storage_changes`]
604	/// does not enforce the storage limit since we want to do this check as late as possible to
605	/// allow later refunds to offset earlier charges.
606	pub fn finalize(&mut self, info: Option<&mut ContractInfo<T>>) -> DispatchResult {
607		self.deposit.finalize_own_contributions(info);
608
609		if self.deposit_left().is_none() {
610			return Err(<Error<T>>::StorageDepositLimitExhausted.into());
611		}
612
613		Ok(())
614	}
615}
616
617/// Ethereum transaction context for gas conversions.
618///
619/// Contains the parameters needed to convert between ethereum gas and substrate resources
620/// (weight/deposit)
621#[derive(DebugNoBound, Clone)]
622pub struct EthTxInfo<T: Config> {
623	/// The encoding length of the extrinsic
624	pub encoded_len: u32,
625	/// The extra weight of the transaction. The total weight of the extrinsic is `extra_weight` +
626	/// the weight consumed during smart contract execution.
627	pub extra_weight: Weight,
628	_phantom: PhantomData<T>,
629}
630
631impl<T: Config> EthTxInfo<T> {
632	/// Create a new ethereum transaction context with the given parameters.
633	pub fn new(encoded_len: u32, extra_weight: Weight) -> Self {
634		Self { encoded_len, extra_weight, _phantom: PhantomData }
635	}
636
637	/// Calculate total gas consumed by weight and storage operations.
638	pub fn gas_consumption(
639		&self,
640		consumed_weight: &Weight,
641		consumed_deposit: &DepositOf<T>,
642	) -> SignedGas<T> {
643		let fixed_fee = T::FeeInfo::fixed_fee(self.encoded_len);
644		let deposit_and_fixed_fee =
645			consumed_deposit.saturating_add(&DepositOf::<T>::Charge(fixed_fee));
646		let deposit_gas = SignedGas::from_adjusted_deposit_charge(&deposit_and_fixed_fee);
647
648		let weight_gas = SignedGas::from_weight_fee(T::FeeInfo::weight_to_fee(
649			&consumed_weight.saturating_add(self.extra_weight),
650		));
651
652		deposit_gas.saturating_add(&weight_gas)
653	}
654
655	/// Calculate maximal possible remaining weight that can be consumed given a particular gas
656	/// limit.
657	///
658	/// Returns None if remaining gas would not allow any more weight consumption.
659	pub fn weight_remaining(
660		&self,
661		max_total_gas: &SignedGas<T>,
662		total_weight_consumption: &Weight,
663		total_deposit_consumption: &DepositOf<T>,
664	) -> Option<Weight> {
665		let fixed_fee = T::FeeInfo::fixed_fee(self.encoded_len);
666		let deposit_and_fixed_fee =
667			total_deposit_consumption.saturating_add(&DepositOf::<T>::Charge(fixed_fee));
668		let deposit_gas = SignedGas::from_adjusted_deposit_charge(&deposit_and_fixed_fee);
669
670		let consumable_fee = max_total_gas.saturating_sub(&deposit_gas).to_weight_fee()?;
671
672		T::FeeInfo::fee_to_weight(consumable_fee)
673			.checked_sub(&total_weight_consumption.saturating_add(self.extra_weight))
674	}
675}