1use crate::{
21 evm::{
22 runtime::{EthExtra, SetWeightLimit},
23 OnChargeTransactionBalanceOf,
24 },
25 BalanceOf, CallOf, Config, DispatchErrorWithPostInfo, DispatchResultWithPostInfo, Error,
26 PostDispatchInfo, LOG_TARGET,
27};
28use codec::Encode;
29use core::marker::PhantomData;
30use frame_support::{
31 dispatch::{DispatchClass, DispatchInfo, GetDispatchInfo},
32 pallet_prelude::Weight,
33 traits::{fungible::Credit, Get, SuppressedDrop},
34 weights::WeightToFee,
35};
36use frame_system::Config as SysConfig;
37use num_traits::{One, Zero};
38use pallet_transaction_payment::{
39 Config as TxConfig, MultiplierUpdate, NextFeeMultiplier, Pallet as TxPallet, TxCreditHold,
40};
41use sp_arithmetic::{FixedPointOperand, SignedRounding};
42use sp_runtime::{
43 generic::UncheckedExtrinsic,
44 traits::{
45 Block as BlockT, Dispatchable, ExtensionPostDispatchWeightHandler, TransactionExtension,
46 },
47 FixedPointNumber, FixedU128, SaturatedConversion, Saturating,
48};
49
50type CreditOf<T> = Credit<<T as frame_system::Config>::AccountId, <T as Config>::Currency>;
51
52pub struct BlockRatioFee<const P: u128, const Q: u128, T: Config>(PhantomData<T>);
64
65pub struct Info<Address, Signature, Extra>(PhantomData<(Address, Signature, Extra)>);
70
71pub trait InfoT<T: Config>: seal::Sealed {
75 fn integrity_test() {}
79
80 fn next_fee_multiplier() -> FixedU128 {
82 FixedU128::from_rational(1, 1)
83 }
84
85 fn next_fee_multiplier_reciprocal() -> FixedU128 {
91 Self::next_fee_multiplier()
92 .reciprocal()
93 .expect("The minimum multiplier is not 0. We check that in `integrity_test`; qed")
94 }
95
96 fn tx_fee(_len: u32, _call: &CallOf<T>) -> BalanceOf<T> {
98 Zero::zero()
99 }
100
101 fn tx_fee_from_weight(_encoded_len: u32, _weight: &Weight) -> BalanceOf<T> {
103 Zero::zero()
104 }
105
106 fn fixed_fee(_encoded_len: u32) -> BalanceOf<T> {
108 Zero::zero()
109 }
110
111 fn ensure_not_overdrawn(
113 _fee: BalanceOf<T>,
114 result: DispatchResultWithPostInfo,
115 ) -> DispatchResultWithPostInfo {
116 result
117 }
118
119 fn dispatch_info(_call: &CallOf<T>) -> DispatchInfo {
121 Default::default()
122 }
123
124 fn base_dispatch_info(_call: &mut CallOf<T>) -> DispatchInfo {
126 Default::default()
127 }
128
129 fn encoded_len(_eth_transact_call: CallOf<T>) -> u32 {
131 0
132 }
133
134 fn weight_to_fee(_weight: &Weight) -> BalanceOf<T> {
136 Zero::zero()
137 }
138
139 fn weight_to_fee_average(_weight: &Weight) -> BalanceOf<T> {
141 Zero::zero()
142 }
143
144 fn fee_to_weight(_fee: BalanceOf<T>) -> Weight {
146 Zero::zero()
147 }
148
149 fn length_to_fee(_len: u32) -> BalanceOf<T> {
151 Zero::zero()
152 }
153
154 fn deposit_txfee(_credit: CreditOf<T>) {}
156
157 fn withdraw_txfee(_amount: BalanceOf<T>) -> Option<CreditOf<T>> {
159 Default::default()
160 }
161
162 fn remaining_txfee() -> BalanceOf<T> {
164 Default::default()
165 }
166
167 fn compute_actual_fee(
169 _encoded_len: u32,
170 _info: &DispatchInfo,
171 _result: &DispatchResultWithPostInfo,
172 ) -> BalanceOf<T> {
173 Default::default()
174 }
175}
176
177impl<const P: u128, const Q: u128, T: Config> BlockRatioFee<P, Q, T> {
178 const REF_TIME_TO_FEE: FixedU128 = {
179 assert!(P > 0 && Q > 0);
180 FixedU128::from_rational(P, Q)
181 };
182
183 fn proof_size_to_fee() -> FixedU128 {
185 let max_weight = <T as frame_system::Config>::BlockWeights::get().max_block;
186 let ratio =
187 FixedU128::from_rational(max_weight.ref_time().into(), max_weight.proof_size().into());
188 Self::REF_TIME_TO_FEE.saturating_mul(ratio)
189 }
190}
191
192impl<const P: u128, const Q: u128, T: Config> WeightToFee for BlockRatioFee<P, Q, T> {
193 type Balance = BalanceOf<T>;
194
195 fn weight_to_fee(weight: &Weight) -> Self::Balance {
196 let ref_time_fee = Self::REF_TIME_TO_FEE
197 .saturating_mul_int(BalanceOf::<T>::saturated_from(weight.ref_time()));
198 let proof_size_fee = Self::proof_size_to_fee()
199 .saturating_mul_int(BalanceOf::<T>::saturated_from(weight.proof_size()));
200 ref_time_fee.max(proof_size_fee)
201 }
202}
203
204impl<const P: u128, const Q: u128, Address, Signature, E: EthExtra> InfoT<E::Config>
205 for Info<Address, Signature, E>
206where
207 E::Config: TxConfig<WeightToFee = BlockRatioFee<P, Q, E::Config>>,
208 BalanceOf<E::Config>: From<OnChargeTransactionBalanceOf<E::Config>>,
209 <E::Config as frame_system::Config>::RuntimeCall:
210 Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
211 CallOf<E::Config>: SetWeightLimit,
212 <<E::Config as SysConfig>::Block as BlockT>::Extrinsic:
213 From<UncheckedExtrinsic<Address, CallOf<E::Config>, Signature, E::Extension>>,
214 <<E::Config as TxConfig>::OnChargeTransaction as TxCreditHold<E::Config>>::Credit:
215 SuppressedDrop<Inner = CreditOf<E::Config>>,
216{
217 fn integrity_test() {
218 let min_multiplier = <E::Config as TxConfig>::FeeMultiplierUpdate::min();
219 assert!(!min_multiplier.is_zero(), "The multiplier is never allowed to be zero.");
220 assert!(
221 min_multiplier.saturating_mul_int(<E::Config as Config>::NativeToEthRatio::get()) > 0,
222 "The gas price needs to be greater zero."
223 );
224 assert!(
225 !<E::Config as TxConfig>::WeightToFee::REF_TIME_TO_FEE.is_zero(),
226 "ref_time to fee is not allowed to be zero."
227 );
228 assert!(
229 !<E::Config as TxConfig>::WeightToFee::proof_size_to_fee().is_zero(),
230 "proof_size to fee is not allowed to be zero."
231 );
232 }
233
234 fn next_fee_multiplier() -> FixedU128 {
235 <NextFeeMultiplier<E::Config>>::get()
236 }
237
238 fn tx_fee(len: u32, call: &CallOf<E::Config>) -> BalanceOf<E::Config> {
239 let dispatch_info = Self::dispatch_info(call);
240 TxPallet::<E::Config>::compute_fee(len, &dispatch_info, 0u32.into()).into()
241 }
242
243 fn tx_fee_from_weight(encoded_len: u32, weight: &Weight) -> BalanceOf<E::Config> {
245 let fixed_fee = Self::fixed_fee(encoded_len);
246 let weight_fee =
247 Self::next_fee_multiplier().saturating_mul_int(Self::weight_to_fee(weight));
248 fixed_fee.saturating_add(weight_fee)
249 }
250
251 fn fixed_fee(encoded_len: u32) -> BalanceOf<E::Config> {
252 Self::weight_to_fee(
253 &<E::Config as frame_system::Config>::BlockWeights::get()
254 .get(DispatchClass::Normal)
255 .base_extrinsic,
256 )
257 .saturating_add(Self::length_to_fee(encoded_len))
258 }
259
260 fn ensure_not_overdrawn(
261 fee: BalanceOf<E::Config>,
262 result: DispatchResultWithPostInfo,
263 ) -> DispatchResultWithPostInfo {
264 let Ok(post_info) = result else {
267 return result;
268 };
269
270 let available = Self::remaining_txfee();
271 if fee > available {
272 log::debug!(target: LOG_TARGET, "Drew too much from the txhold. \
273 fee={fee:?} \
274 available={available:?} \
275 overdrawn_by={:?}",
276 fee.saturating_sub(available),
277 );
278 Err(DispatchErrorWithPostInfo {
279 post_info,
280 error: <Error<E::Config>>::TxFeeOverdraw.into(),
281 })
282 } else {
283 log::trace!(target: LOG_TARGET, "Enough left in the txhold. \
284 fee={fee:?} \
285 available={available:?} \
286 refund={:?}",
287 available.saturating_sub(fee),
288 );
289 result
290 }
291 }
292
293 fn dispatch_info(call: &CallOf<E::Config>) -> DispatchInfo {
294 let mut dispatch_info = call.get_dispatch_info();
295 dispatch_info.extension_weight =
296 E::get_eth_extension(0u32.into(), 0u32.into()).weight(call);
297 dispatch_info
298 }
299
300 fn base_dispatch_info(call: &mut CallOf<E::Config>) -> DispatchInfo {
301 let pre_weight = call.set_weight_limit(Zero::zero());
302 let info = Self::dispatch_info(call);
303 call.set_weight_limit(pre_weight);
304 info
305 }
306
307 fn encoded_len(eth_transact_call: CallOf<E::Config>) -> u32 {
308 let uxt: <<E::Config as SysConfig>::Block as BlockT>::Extrinsic =
309 UncheckedExtrinsic::new_bare(eth_transact_call).into();
310 uxt.encoded_size() as u32
311 }
312
313 fn weight_to_fee(weight: &Weight) -> BalanceOf<E::Config> {
314 <E::Config as TxConfig>::WeightToFee::weight_to_fee(weight)
315 }
316
317 fn weight_to_fee_average(weight: &Weight) -> BalanceOf<E::Config> {
319 let ref_time_part = <E::Config as TxConfig>::WeightToFee::REF_TIME_TO_FEE
320 .saturating_mul_int(weight.ref_time());
321 let proof_size_part = <E::Config as TxConfig>::WeightToFee::proof_size_to_fee()
322 .saturating_mul_int(weight.proof_size());
323
324 ((ref_time_part / 2).saturating_add(proof_size_part / 2)).saturated_into()
326 }
327
328 fn fee_to_weight(fee: BalanceOf<E::Config>) -> Weight {
330 let ref_time_to_fee = <E::Config as TxConfig>::WeightToFee::REF_TIME_TO_FEE;
331 let proof_size_to_fee = <E::Config as TxConfig>::WeightToFee::proof_size_to_fee();
332
333 let (ref_time, proof_size) =
334 compute_max_integer_pair_quotient((ref_time_to_fee, proof_size_to_fee), fee);
335
336 Weight::from_parts(ref_time.saturated_into(), proof_size.saturated_into())
337 }
338
339 fn length_to_fee(len: u32) -> BalanceOf<E::Config> {
340 TxPallet::<E::Config>::length_to_fee(len).into()
341 }
342
343 fn deposit_txfee(credit: CreditOf<E::Config>) {
344 TxPallet::<E::Config>::deposit_txfee(credit)
345 }
346
347 fn withdraw_txfee(amount: BalanceOf<E::Config>) -> Option<CreditOf<E::Config>> {
348 TxPallet::<E::Config>::withdraw_txfee(amount)
349 }
350
351 fn remaining_txfee() -> BalanceOf<E::Config> {
352 TxPallet::<E::Config>::remaining_txfee()
353 }
354
355 fn compute_actual_fee(
356 encoded_len: u32,
357 info: &DispatchInfo,
358 result: &DispatchResultWithPostInfo,
359 ) -> BalanceOf<E::Config> {
360 let mut post_info = *match result {
361 Ok(post_info) => post_info,
362 Err(err) => &err.post_info,
363 };
364
365 post_info.set_extension_weight(info);
366 <TxPallet<E::Config>>::compute_actual_fee(encoded_len, info, &post_info, Zero::zero())
367 .into()
368 }
369}
370
371impl<T: Config> InfoT<T> for () {}
372
373mod seal {
374 pub trait Sealed {}
375 impl<Address, Signature, E: super::EthExtra> Sealed for super::Info<Address, Signature, E> {}
376 impl Sealed for () {}
377}
378
379pub fn compute_max_integer_quotient<F: FixedPointOperand + One>(
411 multiplier: FixedU128,
412 product: F,
413) -> F {
414 let one = F::one();
415 let product_plus_one = FixedU128::from_inner(product.saturating_add(one).saturated_into());
416
417 product_plus_one
418 .checked_rounding_div(multiplier, SignedRounding::Major)
419 .map(|f| f.into_inner().saturated_into::<F>().saturating_sub(one))
420 .unwrap_or(F::max_value())
421}
422
423pub fn compute_max_integer_pair_quotient<F: FixedPointOperand + One>(
425 multiplier: (FixedU128, FixedU128),
426 product: F,
427) -> (F, F) {
428 let one = F::one();
429 let product_plus_one = FixedU128::from_inner(product.saturating_add(one).saturated_into());
430
431 let result1 = product_plus_one
432 .checked_rounding_div(multiplier.0, SignedRounding::Major)
433 .map(|f| f.into_inner().saturated_into::<F>().saturating_sub(one))
434 .unwrap_or(F::max_value());
435
436 let result2 = product_plus_one
437 .checked_rounding_div(multiplier.1, SignedRounding::Major)
438 .map(|f| f.into_inner().saturated_into::<F>().saturating_sub(one))
439 .unwrap_or(F::max_value());
440
441 (result1, result2)
442}
443
444#[cfg(test)]
445mod tests {
446 use super::*;
447 use proptest::proptest;
448
449 #[test]
450 fn compute_max_quotient_works() {
451 let product1 = 8625031518u64;
452 let product2 = 2597808837u64;
453
454 let multiplier = FixedU128::from_rational(4_000_000_000_000, 10 * 1024 * 1024);
455
456 assert_eq!(compute_max_integer_quotient(multiplier, product1), 22610);
457 assert_eq!(compute_max_integer_quotient(multiplier, product2), 6810);
458
459 assert_eq!(multiplier.reciprocal().unwrap().saturating_mul_int(product1), 22610);
462 assert_eq!(multiplier.reciprocal().unwrap().saturating_mul_int(product2), 6809);
463 }
464
465 #[test]
466 fn proptest_max_quotient_works() {
467 proptest!(|(numerator: u128, denominator: u128, product: u128)| {
468 let multiplier = FixedU128::from_rational(numerator.saturating_add(1), denominator.saturating_add(1));
469 let max_quotient = compute_max_integer_quotient(multiplier, product);
470
471 assert!(multiplier.saturating_mul_int(max_quotient) <= product);
472 if max_quotient < u128::MAX {
473 assert!(multiplier.saturating_mul_int(max_quotient + 1) > product);
474 }
475 });
476 }
477
478 #[test]
479 fn proptest_max_pair_quotient_works() {
480 proptest!(|(numerator1: u128, denominator1: u128, numerator2: u128, denominator2: u128, product: u128)| {
481 let multiplier1 = FixedU128::from_rational(numerator1.saturating_add(1), denominator1.saturating_add(1));
482 let multiplier2 = FixedU128::from_rational(numerator2.saturating_add(1), denominator2.saturating_add(1));
483 let (max_quotient1, max_quotient2) = compute_max_integer_pair_quotient((multiplier1, multiplier2), product);
484
485 assert!(multiplier1.saturating_mul_int(max_quotient1) <= product);
486 if max_quotient1 < u128::MAX {
487 assert!(multiplier1.saturating_mul_int(max_quotient1 + 1) > product);
488 }
489
490 assert!(multiplier2.saturating_mul_int(max_quotient2) <= product);
491 if max_quotient2 < u128::MAX {
492 assert!(multiplier2.saturating_mul_int(max_quotient2 + 1) > product);
493 }
494 });
495 }
496}