1use crate::{
21 BalanceOf, CallOf, Config, DispatchErrorWithPostInfo, DispatchResultWithPostInfo, Error,
22 LOG_TARGET, PostDispatchInfo,
23 evm::{
24 OnChargeTransactionBalanceOf,
25 runtime::{EthExtra, SetWeightLimit},
26 },
27};
28use codec::Encode;
29use core::marker::PhantomData;
30use frame_support::{
31 dispatch::{DispatchClass, DispatchInfo, GetDispatchInfo},
32 pallet_prelude::Weight,
33 traits::{Get, SuppressedDrop, fungible::Credit, tokens::Balance},
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 FixedPointNumber, FixedU128, SaturatedConversion, Saturating,
44 generic::UncheckedExtrinsic,
45 traits::{
46 Block as BlockT, Dispatchable, ExtensionPostDispatchWeightHandler, TransactionExtension,
47 },
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, B>(PhantomData<(T, B)>);
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: SysConfig, B> BlockRatioFee<P, Q, T, B> {
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 SysConfig>::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, B> WeightToFee for BlockRatioFee<P, Q, T, B>
193where
194 T: SysConfig,
195 B: Balance,
196{
197 type Balance = B;
198
199 fn weight_to_fee(weight: &Weight) -> Self::Balance {
200 let ref_time_fee = Self::REF_TIME_TO_FEE
201 .saturating_mul_int(Self::Balance::saturated_from(weight.ref_time()));
202 let proof_size_fee = Self::proof_size_to_fee()
203 .saturating_mul_int(Self::Balance::saturated_from(weight.proof_size()));
204 ref_time_fee.max(proof_size_fee)
205 }
206}
207
208impl<const P: u128, const Q: u128, Address, Signature, E: EthExtra> InfoT<E::Config>
209 for Info<Address, Signature, E>
210where
211 E::Config: TxConfig<WeightToFee = BlockRatioFee<P, Q, E::Config, BalanceOf<E::Config>>>,
212 BalanceOf<E::Config>: From<OnChargeTransactionBalanceOf<E::Config>>,
213 <E::Config as frame_system::Config>::RuntimeCall:
214 Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
215 CallOf<E::Config>: SetWeightLimit,
216 <<E::Config as SysConfig>::Block as BlockT>::Extrinsic: From<
217 UncheckedExtrinsic<
218 Address,
219 CallOf<E::Config>,
220 Signature,
221 E::ExtensionV0,
222 E::ExtensionOtherVersions,
223 >,
224 >,
225 <<E::Config as TxConfig>::OnChargeTransaction as TxCreditHold<E::Config>>::Credit:
226 SuppressedDrop<Inner = CreditOf<E::Config>>,
227{
228 fn integrity_test() {
229 let min_multiplier = <E::Config as TxConfig>::FeeMultiplierUpdate::min();
230 assert!(!min_multiplier.is_zero(), "The multiplier is never allowed to be zero.");
231 assert!(
232 min_multiplier.saturating_mul_int(<E::Config as Config>::NativeToEthRatio::get()) > 0,
233 "The gas price needs to be greater zero."
234 );
235 assert!(
236 !<E::Config as TxConfig>::WeightToFee::REF_TIME_TO_FEE.is_zero(),
237 "ref_time to fee is not allowed to be zero."
238 );
239 assert!(
240 !<E::Config as TxConfig>::WeightToFee::proof_size_to_fee().is_zero(),
241 "proof_size to fee is not allowed to be zero."
242 );
243 }
244
245 fn next_fee_multiplier() -> FixedU128 {
246 <NextFeeMultiplier<E::Config>>::get()
247 }
248
249 fn tx_fee(len: u32, call: &CallOf<E::Config>) -> BalanceOf<E::Config> {
250 let dispatch_info = Self::dispatch_info(call);
251 TxPallet::<E::Config>::compute_fee(len, &dispatch_info, 0u32.into()).into()
252 }
253
254 fn tx_fee_from_weight(encoded_len: u32, weight: &Weight) -> BalanceOf<E::Config> {
256 let fixed_fee = Self::fixed_fee(encoded_len);
257 let weight_fee =
258 Self::next_fee_multiplier().saturating_mul_int(Self::weight_to_fee(weight));
259 fixed_fee.saturating_add(weight_fee)
260 }
261
262 fn fixed_fee(encoded_len: u32) -> BalanceOf<E::Config> {
263 Self::weight_to_fee(
264 &<E::Config as frame_system::Config>::BlockWeights::get()
265 .get(DispatchClass::Normal)
266 .base_extrinsic,
267 )
268 .saturating_add(Self::length_to_fee(encoded_len))
269 }
270
271 fn ensure_not_overdrawn(
272 fee: BalanceOf<E::Config>,
273 result: DispatchResultWithPostInfo,
274 ) -> DispatchResultWithPostInfo {
275 let Ok(post_info) = result else {
278 return result;
279 };
280
281 let available = Self::remaining_txfee();
282 if fee > available {
283 log::debug!(target: LOG_TARGET, "Drew too much from the txhold. \
284 fee={fee:?} \
285 available={available:?} \
286 overdrawn_by={:?}",
287 fee.saturating_sub(available),
288 );
289 Err(DispatchErrorWithPostInfo {
290 post_info,
291 error: <Error<E::Config>>::TxFeeOverdraw.into(),
292 })
293 } else {
294 log::trace!(target: LOG_TARGET, "Enough left in the txhold. \
295 fee={fee:?} \
296 available={available:?} \
297 refund={:?}",
298 available.saturating_sub(fee),
299 );
300 result
301 }
302 }
303
304 fn dispatch_info(call: &CallOf<E::Config>) -> DispatchInfo {
305 let mut dispatch_info = call.get_dispatch_info();
306 dispatch_info.extension_weight =
307 E::get_eth_extension(0u32.into(), 0u32.into()).weight(call);
308 dispatch_info
309 }
310
311 fn base_dispatch_info(call: &mut CallOf<E::Config>) -> DispatchInfo {
312 let pre_weight = call.set_weight_limit(Zero::zero());
313 let info = Self::dispatch_info(call);
314 call.set_weight_limit(pre_weight);
315 info
316 }
317
318 fn encoded_len(eth_transact_call: CallOf<E::Config>) -> u32 {
319 let uxt: <<E::Config as SysConfig>::Block as BlockT>::Extrinsic =
320 UncheckedExtrinsic::new_bare(eth_transact_call).into();
321 uxt.encoded_size() as u32
322 }
323
324 fn weight_to_fee(weight: &Weight) -> BalanceOf<E::Config> {
325 <E::Config as TxConfig>::WeightToFee::weight_to_fee(weight)
326 }
327
328 fn weight_to_fee_average(weight: &Weight) -> BalanceOf<E::Config> {
330 let ref_time_part = <E::Config as TxConfig>::WeightToFee::REF_TIME_TO_FEE
331 .saturating_mul_int(weight.ref_time());
332 let proof_size_part = <E::Config as TxConfig>::WeightToFee::proof_size_to_fee()
333 .saturating_mul_int(weight.proof_size());
334
335 ((ref_time_part / 2).saturating_add(proof_size_part / 2)).saturated_into()
337 }
338
339 fn fee_to_weight(fee: BalanceOf<E::Config>) -> Weight {
341 let ref_time_to_fee = <E::Config as TxConfig>::WeightToFee::REF_TIME_TO_FEE;
342 let proof_size_to_fee = <E::Config as TxConfig>::WeightToFee::proof_size_to_fee();
343
344 let (ref_time, proof_size) =
345 compute_max_integer_pair_quotient((ref_time_to_fee, proof_size_to_fee), fee);
346
347 Weight::from_parts(ref_time.saturated_into(), proof_size.saturated_into())
348 }
349
350 fn length_to_fee(len: u32) -> BalanceOf<E::Config> {
351 TxPallet::<E::Config>::length_to_fee(len).into()
352 }
353
354 fn deposit_txfee(credit: CreditOf<E::Config>) {
355 TxPallet::<E::Config>::deposit_txfee(credit)
356 }
357
358 fn withdraw_txfee(amount: BalanceOf<E::Config>) -> Option<CreditOf<E::Config>> {
359 TxPallet::<E::Config>::withdraw_txfee(amount)
360 }
361
362 fn remaining_txfee() -> BalanceOf<E::Config> {
363 TxPallet::<E::Config>::remaining_txfee()
364 }
365
366 fn compute_actual_fee(
367 encoded_len: u32,
368 info: &DispatchInfo,
369 result: &DispatchResultWithPostInfo,
370 ) -> BalanceOf<E::Config> {
371 let mut post_info = *match result {
372 Ok(post_info) => post_info,
373 Err(err) => &err.post_info,
374 };
375
376 post_info.set_extension_weight(info);
377 <TxPallet<E::Config>>::compute_actual_fee(encoded_len, info, &post_info, Zero::zero())
378 .into()
379 }
380}
381
382impl<T: Config> InfoT<T> for () {}
383
384mod seal {
385 pub trait Sealed {}
386 impl<Address, Signature, E: super::EthExtra> Sealed for super::Info<Address, Signature, E> {}
387 impl Sealed for () {}
388}
389
390pub fn compute_max_integer_quotient<F: FixedPointOperand + One>(
422 multiplier: FixedU128,
423 product: F,
424) -> F {
425 let one = F::one();
426 let product_plus_one = FixedU128::from_inner(product.saturating_add(one).saturated_into());
427
428 product_plus_one
429 .checked_rounding_div(multiplier, SignedRounding::Major)
430 .map(|f| f.into_inner().saturated_into::<F>().saturating_sub(one))
431 .unwrap_or(F::max_value())
432}
433
434pub fn compute_max_integer_pair_quotient<F: FixedPointOperand + One>(
436 multiplier: (FixedU128, FixedU128),
437 product: F,
438) -> (F, F) {
439 let one = F::one();
440 let product_plus_one = FixedU128::from_inner(product.saturating_add(one).saturated_into());
441
442 let result1 = product_plus_one
443 .checked_rounding_div(multiplier.0, SignedRounding::Major)
444 .map(|f| f.into_inner().saturated_into::<F>().saturating_sub(one))
445 .unwrap_or(F::max_value());
446
447 let result2 = product_plus_one
448 .checked_rounding_div(multiplier.1, SignedRounding::Major)
449 .map(|f| f.into_inner().saturated_into::<F>().saturating_sub(one))
450 .unwrap_or(F::max_value());
451
452 (result1, result2)
453}
454
455#[cfg(test)]
456mod tests {
457 use super::*;
458 use proptest::proptest;
459
460 #[test]
461 fn compute_max_quotient_works() {
462 let product1 = 8625031518u64;
463 let product2 = 2597808837u64;
464
465 let multiplier = FixedU128::from_rational(4_000_000_000_000, 10 * 1024 * 1024);
466
467 assert_eq!(compute_max_integer_quotient(multiplier, product1), 22610);
468 assert_eq!(compute_max_integer_quotient(multiplier, product2), 6810);
469
470 assert_eq!(multiplier.reciprocal().unwrap().saturating_mul_int(product1), 22610);
473 assert_eq!(multiplier.reciprocal().unwrap().saturating_mul_int(product2), 6809);
474 }
475
476 #[test]
477 fn proptest_max_quotient_works() {
478 proptest!(|(numerator: u128, denominator: u128, product: u128)| {
479 let multiplier = FixedU128::from_rational(numerator.saturating_add(1), denominator.saturating_add(1));
480 let max_quotient = compute_max_integer_quotient(multiplier, product);
481
482 assert!(multiplier.saturating_mul_int(max_quotient) <= product);
483 if max_quotient < u128::MAX {
484 assert!(multiplier.saturating_mul_int(max_quotient + 1) > product);
485 }
486 });
487 }
488
489 #[test]
490 fn proptest_max_pair_quotient_works() {
491 proptest!(|(numerator1: u128, denominator1: u128, numerator2: u128, denominator2: u128, product: u128)| {
492 let multiplier1 = FixedU128::from_rational(numerator1.saturating_add(1), denominator1.saturating_add(1));
493 let multiplier2 = FixedU128::from_rational(numerator2.saturating_add(1), denominator2.saturating_add(1));
494 let (max_quotient1, max_quotient2) = compute_max_integer_pair_quotient((multiplier1, multiplier2), product);
495
496 assert!(multiplier1.saturating_mul_int(max_quotient1) <= product);
497 if max_quotient1 < u128::MAX {
498 assert!(multiplier1.saturating_mul_int(max_quotient1 + 1) > product);
499 }
500
501 assert!(multiplier2.saturating_mul_int(max_quotient2) <= product);
502 if max_quotient2 < u128::MAX {
503 assert!(multiplier2.saturating_mul_int(max_quotient2 + 1) > product);
504 }
505 });
506 }
507}