From ac4ff24917340c173365a3ca79aa460fda79d49b Mon Sep 17 00:00:00 2001 From: Henrique Nogara Date: Thu, 18 Dec 2025 14:29:10 -0300 Subject: [PATCH 1/4] Fix fee withdraw --- pallets/transaction-payment/src/lib.rs | 112 ++++++++++++++------- pallets/transaction-payment/src/payment.rs | 19 ++-- 2 files changed, 90 insertions(+), 41 deletions(-) diff --git a/pallets/transaction-payment/src/lib.rs b/pallets/transaction-payment/src/lib.rs index 8e8c47a056..a61a09e6f7 100644 --- a/pallets/transaction-payment/src/lib.rs +++ b/pallets/transaction-payment/src/lib.rs @@ -549,7 +549,7 @@ where let DispatchInfo { class, .. } = dispatch_info; RuntimeDispatchInfo { - weight: dispatch_info.total_weight(), + weight: dispatch_info.call_weight, class, partial_fee, } @@ -587,7 +587,7 @@ where let DispatchInfo { class, .. } = dispatch_info; RuntimeDispatchInfo { - weight: dispatch_info.total_weight(), + weight: dispatch_info.call_weight, class, partial_fee: Self::compute_fee(len, &dispatch_info, 0u32.into()), } @@ -625,7 +625,7 @@ where where T::RuntimeCall: Dispatchable, { - Self::compute_fee_raw(len, info.total_weight(), tip, info.pays_fee, info.class) + Self::compute_fee_raw(len, info.call_weight, tip, info.pays_fee, info.class) } /// Compute the actual post dispatch fee for a particular transaction. @@ -786,14 +786,7 @@ where call: &T::RuntimeCall, info: &DispatchInfoOf, len: usize, - ) -> Result< - ( - BalanceOf, - <::OnChargeTransaction as OnChargeTransaction>::LiquidityInfo, - Option, - ), - TransactionValidityError, - > { + ) -> Result<(BalanceOf, Option), TransactionValidityError> { let tip = self.0; let fee = Pallet::::compute_fee(len as u32, info, tip); @@ -801,7 +794,7 @@ where // ----------------------------------------------------------------- if fee.is_zero() { - return Ok((fee, Default::default(), None)); + return Ok((fee, None)); } // Get the payer for this transaction. @@ -814,15 +807,59 @@ where // key to pay the fee. let fee_key = subsidiser.as_ref().unwrap_or(&payers_key); + <::OnChargeTransaction as OnChargeTransaction>::can_withdraw_fee( + fee_key, call, info, fee, tip, + )?; + + T::CddHandler::set_payer_context(Some(payers_key)); + + // ----------------------------------------------------------------- + + Ok((fee, subsidiser)) + } + + pub(crate) fn withdraw_fee( + &self, + who: &T::AccountId, + call: &T::RuntimeCall, + info: &DispatchInfoOf, + fee_with_tip: BalanceOf, + subsidiser: Option<&T::AccountId>, + ) -> Result< + ( + BalanceOf, + <::OnChargeTransaction as OnChargeTransaction>::LiquidityInfo, + ), + TransactionValidityError, + > { + let tip = self.0; + + // Polymesh change + // ----------------------------------------------------------------- + + if fee_with_tip.is_zero() { + return Ok((fee_with_tip, Default::default())); + } + + let payers_key = + T::CddHandler::get_valid_payer(call, who)?.ok_or(InvalidTransaction::Payment)?; + + let fee_key = subsidiser.unwrap_or(&payers_key); + let liq_info = <::OnChargeTransaction as OnChargeTransaction>::withdraw_fee( - fee_key, call, info, fee, tip, + fee_key, + call, + info, + fee_with_tip, + tip, )?; T::CddHandler::set_payer_context(Some(payers_key)); - Ok((fee, liq_info, subsidiser)) // ----------------------------------------------------------------- + + Ok((fee_with_tip, liq_info)) } // Polymesh change: Used to allow GC/CDD member to include a `tip`. @@ -861,6 +898,8 @@ where DispatchClass::Mandatory => Ok(self.0), } } + + // ----------------------------------------------------------------- } /// The info passed between the validate and prepare steps for the `ChargeAssetTxPayment` extension. @@ -945,7 +984,7 @@ where let tip = self.ensure_valid_tip(caller_acc, info)?; - let (fee, _, subsidiser) = self.can_withdraw_fee(caller_acc, call, info, len)?; + let (fee, subsidiser) = self.can_withdraw_fee(caller_acc, call, info, len)?; let valid_transaction = ValidTransaction { priority: tip.saturated_into::(), @@ -964,28 +1003,33 @@ where fn prepare( self, - _val: Self::Val, - origin: &::RuntimeOrigin, + val: Self::Val, + _origin: &::RuntimeOrigin, call: &T::RuntimeCall, info: &DispatchInfoOf, - len: usize, + _len: usize, ) -> Result { - let caller_acc = origin - .as_system_origin_signer() - .ok_or(InvalidTransaction::BadSigner)?; - - let tip = self.ensure_valid_tip(caller_acc, info)?; - - let (_, imbalance, subsidiser) = self.can_withdraw_fee(caller_acc, call, info, len)?; - - let pre = Pre::Charge { - tip, - who: caller_acc.clone(), - imbalance, - subsidiser, - }; - - Ok(pre) + match val { + Val::NoCharge => Ok(Pre::NoCharge { + refund: self.weight(call), + }), + Val::Charge { + tip, + who, + fee, + subsidiser, + } => { + let (_, imbalance) = + self.withdraw_fee(&who, call, info, fee, subsidiser.as_ref())?; + + Ok(Pre::Charge { + tip, + who, + imbalance, + subsidiser, + }) + } + } } fn post_dispatch( diff --git a/pallets/transaction-payment/src/payment.rs b/pallets/transaction-payment/src/payment.rs index f719d2cb01..62512c3105 100644 --- a/pallets/transaction-payment/src/payment.rs +++ b/pallets/transaction-payment/src/payment.rs @@ -97,10 +97,10 @@ where who: &T::AccountId, _call: &T::RuntimeCall, _info: &DispatchInfoOf, - fee: Self::Balance, + fee_with_tip: Self::Balance, tip: Self::Balance, ) -> Result { - if fee.is_zero() { + if fee_with_tip.is_zero() { return Ok(None); } @@ -110,7 +110,12 @@ where WithdrawReasons::TRANSACTION_PAYMENT | WithdrawReasons::TIP }; - match C::withdraw(who, fee, withdraw_reason, ExistenceRequirement::KeepAlive) { + match C::withdraw( + who, + fee_with_tip, + withdraw_reason, + ExistenceRequirement::KeepAlive, + ) { Ok(imbalance) => Ok(Some(imbalance)), Err(_) => Err(InvalidTransaction::Payment.into()), } @@ -123,10 +128,10 @@ where who: &T::AccountId, _call: &T::RuntimeCall, _info: &DispatchInfoOf, - fee: Self::Balance, + fee_with_tip: Self::Balance, tip: Self::Balance, ) -> Result<(), TransactionValidityError> { - if fee.is_zero() { + if fee_with_tip.is_zero() { return Ok(()); } @@ -137,9 +142,9 @@ where }; let new_balance = C::free_balance(who) - .checked_sub(&fee) + .checked_sub(&fee_with_tip) .ok_or(InvalidTransaction::Payment)?; - C::ensure_can_withdraw(who, fee, withdraw_reason, new_balance) + C::ensure_can_withdraw(who, fee_with_tip, withdraw_reason, new_balance) .map(|_| ()) .map_err(|_| InvalidTransaction::Payment.into()) } From 5c3e9ec464569719ca1b95898d26e02d9a5ef33d Mon Sep 17 00:00:00 2001 From: Henrique Nogara Date: Mon, 22 Dec 2025 09:59:35 -0300 Subject: [PATCH 2/4] Fix tests; Improve validate and prepare code --- pallets/runtime/tests/src/balances_test.rs | 18 +- pallets/runtime/tests/src/relayer_test.rs | 57 +++++- .../tests/src/transaction_payment_test.rs | 181 +++++++++++++++--- pallets/runtime/tests/src/utility_test.rs | 25 ++- pallets/transaction-payment/src/lib.rs | 58 ++++-- 5 files changed, 262 insertions(+), 77 deletions(-) diff --git a/pallets/runtime/tests/src/balances_test.rs b/pallets/runtime/tests/src/balances_test.rs index 69f48d84b0..016961b87f 100644 --- a/pallets/runtime/tests/src/balances_test.rs +++ b/pallets/runtime/tests/src/balances_test.rs @@ -3,7 +3,8 @@ use frame_support::dispatch::DispatchInfo; use frame_support::traits::Currency; use frame_support::weights::Weight; use sp_keyring::Sr25519Keyring; -use sp_runtime::traits::TransactionExtension; +use sp_runtime::traits::{TransactionExtension, TxBaseImplication}; +use sp_runtime::transaction_validity::TransactionSource; use pallet_balances::{self as balances, Event as BalancesRawEvent}; use pallet_identity as identity; @@ -75,16 +76,15 @@ fn tipping_fails() { .monied(true) .build() .execute_with(|| { - let charge_tx_payment = ChargeTransactionPayment::::from(5); - - let call = StorageRuntimeCall::System(frame_system::Call::remark { remark: vec![] }); - assert!(charge_tx_payment - .prepare( - Val::NoCharge, - &Origin::signed(Sr25519Keyring::Alice.to_account_id()), - &call, + assert!(ChargeTransactionPayment::::from(5) + .validate( + Origin::signed(Sr25519Keyring::Alice.to_account_id()), + &StorageRuntimeCall::System(frame_system::Call::remark { remark: vec![] }), &info_from_weight(3), 10, + Default::default(), + &TxBaseImplication(()), + TransactionSource::InBlock, ) .is_err()); }); diff --git a/pallets/runtime/tests/src/relayer_test.rs b/pallets/runtime/tests/src/relayer_test.rs index f31a2a2716..ecc1a543f3 100644 --- a/pallets/runtime/tests/src/relayer_test.rs +++ b/pallets/runtime/tests/src/relayer_test.rs @@ -120,8 +120,14 @@ fn assert_invalid_subsidy_call(caller: &AccountId, call: &RuntimeCall) { .unwrap_err(); assert_eq!(pre_err, expected_err); + let val_charge = Val::Charge { + tip: 0, + who: caller.clone(), + fee: 1, + subsidiser: None, + }; let pre_err = ChargeTransactionPayment::from(0) - .prepare(Val::NoCharge, &origin, call, &info_from_weight(5), 10) + .prepare(val_charge, &origin, call, &info_from_weight(5), 10) .map(|_| ()) .unwrap_err(); assert_eq!(pre_err, expected_err); @@ -450,9 +456,21 @@ fn do_user_remove_paying_key_transaction_fee_test() { let transaction_fee = TransactionPayment::compute_fee(len as u32, &call_info, 0); // 1. Call `pre_dispatch`. + let val = ChargeTransactionPayment::from(0) + .validate( + RuntimeOrigin::signed(bob.acc()), + &call, + &call_info, + len, + Default::default(), + &TxBaseImplication(()), + TransactionSource::InBlock, + ) + .unwrap(); + let pre = ChargeTransactionPayment::from(0) .prepare( - Val::NoCharge, + val.1, &RuntimeOrigin::signed(bob.acc()), &call, &call_info, @@ -524,7 +542,12 @@ fn do_relayer_transaction_and_protocol_fees_test() { // test `pre_dispatch` let pre_err = ChargeTransactionPayment::from(0) .prepare( - Val::NoCharge, + Val::Charge { + tip: 0, + who: bob.acc(), + fee: 1, + subsidiser: Some(alice.acc()), + }, &RuntimeOrigin::signed(bob.acc()), &call_system_remark(42), &info_from_weight(5), @@ -550,9 +573,21 @@ fn do_relayer_transaction_and_protocol_fees_test() { let total_fee = transaction_fee + protocol_fee; // 1. Call `pre_dispatch`. + let val = ChargeTransactionPayment::from(0) + .validate( + RuntimeOrigin::signed(bob.acc()), + &call, + &call_info, + len, + Default::default(), + &TxBaseImplication(()), + TransactionSource::InBlock, + ) + .unwrap(); + let pre = ChargeTransactionPayment::from(0) .prepare( - Val::NoCharge, + val.1, &RuntimeOrigin::signed(bob.acc()), &call, &call_info, @@ -661,9 +696,21 @@ fn do_relayer_batched_subsidy_calls_test() { let total_fee = transaction_fee + protocol_fee; // 1. Call `pre_dispatch`. + let val = ChargeTransactionPayment::from(0) + .validate( + RuntimeOrigin::signed(bob.acc()), + &call, + &call_info, + len, + Default::default(), + &TxBaseImplication(()), + TransactionSource::InBlock, + ) + .unwrap(); + let pre = ChargeTransactionPayment::from(0) .prepare( - Val::NoCharge, + val.1, &RuntimeOrigin::signed(bob.acc()), &call, &call_info, diff --git a/pallets/runtime/tests/src/transaction_payment_test.rs b/pallets/runtime/tests/src/transaction_payment_test.rs index e80384fbe9..4642440b74 100644 --- a/pallets/runtime/tests/src/transaction_payment_test.rs +++ b/pallets/runtime/tests/src/transaction_payment_test.rs @@ -80,15 +80,22 @@ fn signed_extension_transaction_payment_work() { let len = 10; let bob_origin = RuntimeOrigin::signed(bob.clone()); - let pre = ChargeTransactionPayment::::from(0) - .prepare( - Val::NoCharge, - &bob_origin, + + let val = ChargeTransactionPayment::::from(0) + .validate( + bob_origin.clone(), &call(), &info_from_weight(5), len, + Default::default(), + &TxBaseImplication(()), + TransactionSource::InBlock, ) .unwrap(); + + let pre = ChargeTransactionPayment::::from(0) + .prepare(val.1, &bob_origin, &call(), &info_from_weight(5), len) + .unwrap(); assert_eq!(Balances::free_balance(&bob), 1999969000); assert!(ChargeTransactionPayment::::post_dispatch( @@ -102,15 +109,22 @@ fn signed_extension_transaction_payment_work() { assert_eq!(Balances::free_balance(&bob), 1999969000); let alice_origin = RuntimeOrigin::signed(alice.clone()); - let pre = ChargeTransactionPayment::::from(0 /* tipped */) - .prepare( - Val::NoCharge, - &alice_origin, + + let val = ChargeTransactionPayment::::from(0) + .validate( + alice_origin.clone(), &call(), &info_from_weight(100), len, + Default::default(), + &TxBaseImplication(()), + TransactionSource::InBlock, ) .unwrap(); + + let pre = ChargeTransactionPayment::::from(0 /* tipped */) + .prepare(val.1, &alice_origin, &call(), &info_from_weight(100), len) + .unwrap(); assert_eq!(Balances::free_balance(&alice), 999969000); assert!(ChargeTransactionPayment::::post_dispatch( @@ -137,15 +151,22 @@ fn signed_extension_transaction_payment_multiplied_refund_works() { TransactionPayment::put_next_fee_multiplier(Multiplier::saturating_from_rational(3, 2)); let alice_origin = RuntimeOrigin::signed(user.clone()); - let pre = ChargeTransactionPayment::::from(0 /* tipped */) - .prepare( - Val::NoCharge, - &alice_origin, + + let val = ChargeTransactionPayment::::from(0) + .validate( + alice_origin.clone(), &call(), &info_from_weight(100), len, + Default::default(), + &TxBaseImplication(()), + TransactionSource::InBlock, ) .unwrap(); + + let pre = ChargeTransactionPayment::::from(0 /* tipped */) + .prepare(val.1, &alice_origin, &call(), &info_from_weight(100), len) + .unwrap(); // 5 base fee, 10 byte fee, 3/2 * 100 weight fee, 5 tip assert_eq!(Balances::free_balance(&user), 999969000); @@ -185,15 +206,22 @@ fn signed_extension_transaction_payment_is_bounded() { // maximum weight possible let bob_origin = RuntimeOrigin::signed(user.clone()); - ChargeTransactionPayment::::from(0) - .prepare( - Val::NoCharge, - &bob_origin, + + let val = ChargeTransactionPayment::::from(0) + .validate( + bob_origin.clone(), &call(), &info_from_weight(u64::MAX), 10, + Default::default(), + &TxBaseImplication(()), + TransactionSource::InBlock, ) .unwrap(); + + ChargeTransactionPayment::::from(0) + .prepare(val.1, &bob_origin, &call(), &info_from_weight(u64::MAX), 10) + .unwrap(); // fee will be proportional to what is the actual maximum weight in the runtime. assert_eq!(Balances::free_balance(&user), (free_user - max_fee)); }); @@ -266,9 +294,22 @@ fn signed_ext_length_fee_is_also_updated_per_congestion() { let len = 10; let user = Sr25519Keyring::Bob.to_account_id(); let bob_origin = RuntimeOrigin::signed(user.clone()); + + let val = ChargeTransactionPayment::::from(0) + .validate( + bob_origin.clone(), + &call(), + &info_from_weight(3), + len, + Default::default(), + &TxBaseImplication(()), + TransactionSource::InBlock, + ) + .unwrap(); + assert!(ChargeTransactionPayment::::from(0) // tipped .prepare( - Val::NoCharge, + val.1, &bob_origin, &call(), &info_from_weight(3), @@ -302,7 +343,7 @@ fn query_info_works() { unchecked_extrinsic.encode().len() as u32 ), RuntimeDispatchInfo { - weight: info.total_weight(), + weight: info.call_weight, class: info.class, partial_fee: 43700 }, @@ -471,15 +512,22 @@ fn actual_weight_higher_than_max_refunds_nothing() { let len = 10; let user = Sr25519Keyring::Alice.to_account_id(); let alice_origin = RuntimeOrigin::signed(user.clone()); - let pre = ChargeTransactionPayment::::from(0 /* tipped */) - .prepare( - Val::NoCharge, - &alice_origin, + + let val = ChargeTransactionPayment::::from(0) + .validate( + alice_origin.clone(), &call(), - &info_from_weight(100), + &info_from_weight(5), len, + Default::default(), + &TxBaseImplication(()), + TransactionSource::InBlock, ) .unwrap(); + + let pre = ChargeTransactionPayment::::from(0 /* tipped */) + .prepare(val.1, &alice_origin, &call(), &info_from_weight(100), len) + .unwrap(); assert_eq!(Balances::free_balance(&user), 999969000); ChargeTransactionPayment::::post_dispatch( @@ -513,9 +561,23 @@ fn zero_transfer_on_free_transaction() { let user = Sr25519Keyring::Alice.to_account_id(); let bal_init = Balances::total_balance(&user); let alice_origin = RuntimeOrigin::signed(user.clone()); + + let val = ChargeTransactionPayment::::from(0) + .validate( + alice_origin.clone(), + &call(), + &dispatch_info, + len, + Default::default(), + &TxBaseImplication(()), + TransactionSource::InBlock, + ) + .unwrap(); + let pre = ChargeTransactionPayment::::from(0) - .prepare(Val::NoCharge, &alice_origin, &call(), &dispatch_info, len) + .prepare(val.1, &alice_origin, &call(), &dispatch_info, len) .unwrap(); + assert_eq!(Balances::total_balance(&user), bal_init); assert!(ChargeTransactionPayment::::post_dispatch( pre, @@ -548,8 +610,21 @@ fn refund_consistent_with_actual_weight() { TransactionPayment::put_next_fee_multiplier(Multiplier::saturating_from_rational(5, 4)); let alice_origin = RuntimeOrigin::signed(alice.clone()); + + let val = ChargeTransactionPayment::::from(0) + .validate( + alice_origin.clone(), + &call(), + &info_from_weight(100), + len, + Default::default(), + &TxBaseImplication(()), + TransactionSource::InBlock, + ) + .unwrap(); + let pre = ChargeTransactionPayment::::from(tip) - .prepare(Val::NoCharge, &alice_origin, &call(), &info, len) + .prepare(val.1, &alice_origin, &call(), &info, len) .unwrap(); ChargeTransactionPayment::::post_dispatch( @@ -592,7 +667,18 @@ fn normal_tx_with_tip_ext() { TransactionError::ZeroTip as u8, )); let pre_err = ChargeTransactionPayment::::from(tip) - .prepare(Val::NoCharge, &alice_origin, &call, &normal_info, len) + .prepare( + Val::Charge { + tip: 1, + who: user.clone(), + fee: 0, + subsidiser: None, + }, + &alice_origin, + &call, + &normal_info, + len, + ) .map(|_| ()) .unwrap_err(); assert!(pre_err == expected_err); @@ -631,18 +717,55 @@ fn operational_tx_with_tip_ext(cdd: AccountId, gc: AccountId) { // Valid operational tx with tip. Only CDD and Governance members can tip. assert!(ChargeTransactionPayment::::from(tip) - .prepare(Val::NoCharge, &alice_origin, &call, &operational_info, len) + .prepare( + Val::Charge { + tip: tip, + who: user.clone(), + fee: TransactionPayment::compute_fee(len as u32, &operational_info, tip), + subsidiser: None, + }, + &alice_origin, + &call, + &operational_info, + len + ) .is_err()); // Governance can tip. let gc_origin = RuntimeOrigin::signed(gc.clone()); + + let val = ChargeTransactionPayment::::from(0) + .validate( + gc_origin.clone(), + &call, + &operational_info, + len, + Default::default(), + &TxBaseImplication(()), + TransactionSource::InBlock, + ) + .unwrap(); + assert!(ChargeTransactionPayment::::from(tip) - .prepare(Val::NoCharge, &gc_origin, &call, &operational_info, len) + .prepare(val.1, &gc_origin, &call, &operational_info, len) .is_ok()); // CDD can also tip. let cdd_origin = RuntimeOrigin::signed(cdd.clone()); + + let val = ChargeTransactionPayment::::from(0) + .validate( + cdd_origin.clone(), + &call, + &operational_info, + len, + Default::default(), + &TxBaseImplication(()), + TransactionSource::InBlock, + ) + .unwrap(); + assert!(ChargeTransactionPayment::::from(tip) - .prepare(Val::NoCharge, &cdd_origin, &call, &operational_info, len) + .prepare(val.1, &cdd_origin, &call, &operational_info, len) .is_ok()); } diff --git a/pallets/runtime/tests/src/utility_test.rs b/pallets/runtime/tests/src/utility_test.rs index 9e0074d5d4..4ffa7004e4 100644 --- a/pallets/runtime/tests/src/utility_test.rs +++ b/pallets/runtime/tests/src/utility_test.rs @@ -494,7 +494,7 @@ fn sub_batch_handles_weight_refund() { let info = call.get_dispatch_info(); let result = call.dispatch(charlie.origin()); assert_ok!(result); - assert_eq!(extract_actual_weight(&result, &info), info.total_weight()); + assert_eq!(extract_actual_weight(&result, &info), info.call_weight); // Refund weight when ok let inner_call = call_foobar(false, start_weight, Some(end_weight)); @@ -506,7 +506,7 @@ fn sub_batch_handles_weight_refund() { // Diff is refunded assert_eq!( extract_actual_weight(&result, &info), - info.total_weight() - diff * batch_len + info.call_weight - diff * batch_len ); // Full weight when err @@ -525,7 +525,7 @@ fn sub_batch_handles_weight_refund() { .into(), ); // No weight is refunded - assert_eq!(extract_actual_weight(&result, &info), info.total_weight()); + assert_eq!(extract_actual_weight(&result, &info), info.call_weight); // Refund weight when err let good_call = call_foobar(false, start_weight, Some(end_weight)); @@ -545,7 +545,7 @@ fn sub_batch_handles_weight_refund() { ); assert_eq!( extract_actual_weight(&result, &info), - info.total_weight() - diff * batch_len + info.call_weight - diff * batch_len ); // Partial batch completion @@ -610,7 +610,7 @@ fn sub_batch_all_revert() { post_info: PostDispatchInfo { actual_weight: Some( ::WeightInfo::batch_all(2) - + info.total_weight() * 2 + + info.call_weight * 2 ), pays_fee: Pays::Yes }, @@ -638,7 +638,7 @@ fn sub_batch_all_handles_weight_refund() { let info = call.get_dispatch_info(); let result = call.dispatch(charlie.origin()); assert_ok!(result); - assert_eq!(extract_actual_weight(&result, &info), info.total_weight()); + assert_eq!(extract_actual_weight(&result, &info), info.call_weight); // Refund weight when ok let inner_call = call_foobar(false, start_weight, Some(end_weight)); @@ -650,7 +650,7 @@ fn sub_batch_all_handles_weight_refund() { // Diff is refunded assert_eq!( extract_actual_weight(&result, &info), - info.total_weight() - diff * batch_len + info.call_weight - diff * batch_len ); // Full weight when err @@ -662,7 +662,7 @@ fn sub_batch_all_handles_weight_refund() { let result = call.dispatch(charlie.origin()); assert_err_ignore_postinfo!(result, "The cake is a lie."); // No weight is refunded - assert_eq!(extract_actual_weight(&result, &info), info.total_weight()); + assert_eq!(extract_actual_weight(&result, &info), info.call_weight); // Refund weight when err let good_call = call_foobar(false, start_weight, Some(end_weight)); @@ -675,7 +675,7 @@ fn sub_batch_all_handles_weight_refund() { assert_err_ignore_postinfo!(result, "The cake is a lie."); assert_eq!( extract_actual_weight(&result, &info), - info.total_weight() - diff * batch_len + info.call_weight - diff * batch_len ); // Partial batch completion @@ -717,8 +717,7 @@ fn sub_batch_all_does_not_nest() { DispatchErrorWithPostInfo { post_info: PostDispatchInfo { actual_weight: Some( - ::WeightInfo::batch_all(1) - + info.total_weight() + ::WeightInfo::batch_all(1) + info.call_weight ), pays_fee: Pays::Yes }, @@ -860,7 +859,7 @@ fn sub_batch_all_doesnt_work_with_inherents() { batch_all.dispatch(charlie.origin()), DispatchErrorWithPostInfo { post_info: PostDispatchInfo { - actual_weight: Some(info.total_weight()), + actual_weight: Some(info.call_weight), pays_fee: Pays::Yes }, error: BadOrigin.into(), @@ -988,7 +987,7 @@ fn sub_with_weight_works() { }; // Weight after is set by Root. assert_eq!( - with_weight_call.get_dispatch_info().total_weight(), + with_weight_call.get_dispatch_info().call_weight, Weight::from_parts(123, 456) ); assert_eq!( diff --git a/pallets/transaction-payment/src/lib.rs b/pallets/transaction-payment/src/lib.rs index a61a09e6f7..4a2fb59096 100644 --- a/pallets/transaction-payment/src/lib.rs +++ b/pallets/transaction-payment/src/lib.rs @@ -788,34 +788,33 @@ where len: usize, ) -> Result<(BalanceOf, Option), TransactionValidityError> { let tip = self.0; - let fee = Pallet::::compute_fee(len as u32, info, tip); + let fee_with_tip = Pallet::::compute_fee(len as u32, info, tip); // Polymesh change // ----------------------------------------------------------------- - if fee.is_zero() { - return Ok((fee, None)); + if fee_with_tip.is_zero() { + return Ok((fee_with_tip, None)); } - // Get the payer for this transaction. - let payers_key = - T::CddHandler::get_valid_payer(call, who)?.ok_or(InvalidTransaction::Payment)?; - - // Check if the payer is being subsidised. - let subsidiser = T::Subsidiser::check_subsidy(&payers_key, fee.into(), Some(call))?; + let (payers_key, subsidiser) = Self::check_subsidy_conditions(who, call, fee_with_tip)?; // key to pay the fee. let fee_key = subsidiser.as_ref().unwrap_or(&payers_key); <::OnChargeTransaction as OnChargeTransaction>::can_withdraw_fee( - fee_key, call, info, fee, tip, + fee_key, + call, + info, + fee_with_tip, + tip, )?; T::CddHandler::set_payer_context(Some(payers_key)); // ----------------------------------------------------------------- - Ok((fee, subsidiser)) + Ok((fee_with_tip, subsidiser)) } pub(crate) fn withdraw_fee( @@ -824,10 +823,10 @@ where call: &T::RuntimeCall, info: &DispatchInfoOf, fee_with_tip: BalanceOf, - subsidiser: Option<&T::AccountId>, ) -> Result< ( BalanceOf, + Option, <::OnChargeTransaction as OnChargeTransaction>::LiquidityInfo, ), TransactionValidityError, @@ -838,13 +837,13 @@ where // ----------------------------------------------------------------- if fee_with_tip.is_zero() { - return Ok((fee_with_tip, Default::default())); + return Ok((fee_with_tip, None, Default::default())); } - let payers_key = - T::CddHandler::get_valid_payer(call, who)?.ok_or(InvalidTransaction::Payment)?; + let (payers_key, subsidiser) = Self::check_subsidy_conditions(who, call, fee_with_tip)?; - let fee_key = subsidiser.unwrap_or(&payers_key); + // key to pay the fee. + let fee_key = subsidiser.as_ref().unwrap_or(&payers_key); let liq_info = <::OnChargeTransaction as OnChargeTransaction>::withdraw_fee( @@ -859,7 +858,23 @@ where // ----------------------------------------------------------------- - Ok((fee_with_tip, liq_info)) + Ok((fee_with_tip, subsidiser, liq_info)) + } + + fn check_subsidy_conditions( + caller_acc: &T::AccountId, + call: &T::RuntimeCall, + fee_with_tip: BalanceOf, + ) -> Result<(T::AccountId, Option), InvalidTransaction> { + // Get the payer for this transaction. + let payers_key = + T::CddHandler::get_valid_payer(call, caller_acc)?.ok_or(InvalidTransaction::Payment)?; + + // Check if the payer is being subsidised. + let subsidiser = + T::Subsidiser::check_subsidy(&payers_key, fee_with_tip.into(), Some(call))?; + + Ok((payers_key, subsidiser)) } // Polymesh change: Used to allow GC/CDD member to include a `tip`. @@ -1014,13 +1029,14 @@ where refund: self.weight(call), }), Val::Charge { - tip, + tip: _, who, fee, - subsidiser, + subsidiser: _, } => { - let (_, imbalance) = - self.withdraw_fee(&who, call, info, fee, subsidiser.as_ref())?; + let tip = self.ensure_valid_tip(&who, info)?; + + let (_, subsidiser, imbalance) = self.withdraw_fee(&who, call, info, fee)?; Ok(Pre::Charge { tip, From 094a7ee09b2a6d2ba00dbeccd2752a9bd048fb15 Mon Sep 17 00:00:00 2001 From: Henrique Nogara Date: Mon, 22 Dec 2025 10:00:14 -0300 Subject: [PATCH 3/4] Change total_weight to call_weight --- pallets/committee/src/lib.rs | 2 +- pallets/contracts/src/chain_extension.rs | 2 +- pallets/multisig/src/lib.rs | 6 +++--- pallets/runtime/build_tool/src/lib.rs | 1 - pallets/utility/src/lib.rs | 8 ++++---- 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/pallets/committee/src/lib.rs b/pallets/committee/src/lib.rs index d9ec015a99..56aa60b62d 100644 --- a/pallets/committee/src/lib.rs +++ b/pallets/committee/src/lib.rs @@ -401,7 +401,7 @@ pub mod pallet { /// * `FirstVoteReject`, if `call` hasn't been proposed and `approve == false`. /// * `NotAMember`, if the `origin` is not a member of this committee. #[pallet::weight((>::WeightInfo::vote_or_propose_new_proposal() - .saturating_add(call.get_dispatch_info().total_weight()), + .saturating_add(call.get_dispatch_info().call_weight), DispatchClass::Operational ))] #[pallet::call_index(3)] diff --git a/pallets/contracts/src/chain_extension.rs b/pallets/contracts/src/chain_extension.rs index b3b4ed9899..dabfc21cbf 100644 --- a/pallets/contracts/src/chain_extension.rs +++ b/pallets/contracts/src/chain_extension.rs @@ -417,7 +417,7 @@ where // Charge weight for the call. let di = call.get_dispatch_info(); - let charged_amount = env.charge_weight(di.total_weight())?; + let charged_amount = env.charge_weight(di.call_weight)?; // Execute call requested by contract, with current DID set to the contract owner. let addr = env.ext().address().clone(); diff --git a/pallets/multisig/src/lib.rs b/pallets/multisig/src/lib.rs index 794532f5a7..798ae646ed 100644 --- a/pallets/multisig/src/lib.rs +++ b/pallets/multisig/src/lib.rs @@ -257,7 +257,7 @@ pub mod pallet { #[pallet::weight({ ::WeightInfo::create_proposal() .saturating_add(::WeightInfo::execute_proposal()) - .saturating_add(proposal.get_dispatch_info().total_weight()) + .saturating_add(proposal.get_dispatch_info().call_weight) })] pub fn create_proposal( origin: OriginFor, @@ -1044,7 +1044,7 @@ impl Pallet { expiry: Option, ) -> DispatchResultWithPostInfo { Self::ensure_ms_signer(multisig, &signer)?; - let max_weight = proposal.get_dispatch_info().total_weight(); + let max_weight = proposal.get_dispatch_info().call_weight; let caller_did = Self::ensure_ms_get_did(multisig)?; let proposal_id = NextProposalId::::get(multisig); Self::ensure_valid_expiry(&expiry)?; @@ -1123,7 +1123,7 @@ impl Pallet { .ok_or_else(|| Error::::ProposalMissing)?; // Ensure `max_weight` was enough to cover the worst-case weight. - let proposal_weight = proposal.get_dispatch_info().total_weight(); + let proposal_weight = proposal.get_dispatch_info().call_weight; ensure!( proposal_weight.all_lte(max_weight), Error::::MaxWeightTooLow diff --git a/pallets/runtime/build_tool/src/lib.rs b/pallets/runtime/build_tool/src/lib.rs index 86a7e29c23..1c93c16fd9 100644 --- a/pallets/runtime/build_tool/src/lib.rs +++ b/pallets/runtime/build_tool/src/lib.rs @@ -3,7 +3,6 @@ pub fn build() { { substrate_wasm_builder::WasmBuilder::new() .with_current_project() - .disable_runtime_version_section_check() .export_heap_base() .import_memory() .build() diff --git a/pallets/utility/src/lib.rs b/pallets/utility/src/lib.rs index 8b3fbafe4c..6e9da763b2 100644 --- a/pallets/utility/src/lib.rs +++ b/pallets/utility/src/lib.rs @@ -338,7 +338,7 @@ pub mod pallet { let dispatch_info = call.call.get_dispatch_info(); ( ::WeightInfo::relay_tx() - .saturating_add(dispatch_info.total_weight()), + .saturating_add(dispatch_info.call_weight), dispatch_info.class, ) })] @@ -461,7 +461,7 @@ pub mod pallet { let dispatch_info = call.get_dispatch_info(); ( ::WeightInfo::dispatch_as() - .saturating_add(dispatch_info.total_weight()), + .saturating_add(dispatch_info.call_weight), dispatch_info.class, ) })] @@ -568,7 +568,7 @@ pub mod pallet { ( ::WeightInfo::as_derivative() .saturating_add(T::DbWeight::get().reads_writes(1, 1)) - .saturating_add(dispatch_info.total_weight()), + .saturating_add(dispatch_info.call_weight), dispatch_info.class, ) })] @@ -590,7 +590,7 @@ impl Pallet { (Weight::zero(), DispatchClass::Operational), |(total_weight, dispatch_class): (Weight, DispatchClass), di| { ( - total_weight.saturating_add(di.total_weight()), + total_weight.saturating_add(di.call_weight), // If not all are `Operational`, we want to use `DispatchClass::Normal`. if di.class == DispatchClass::Normal { di.class From b51b5d5ee8474eb465c580293241d976c13eecfc Mon Sep 17 00:00:00 2001 From: Henrique Nogara Date: Mon, 29 Dec 2025 10:16:36 -0300 Subject: [PATCH 4/4] Remove duplicated check in tx extension; Fix call_weight usage --- .../tests/src/transaction_payment_test.rs | 33 ++++++++----------- pallets/transaction-payment/src/lib.rs | 8 ++--- 2 files changed, 16 insertions(+), 25 deletions(-) diff --git a/pallets/runtime/tests/src/transaction_payment_test.rs b/pallets/runtime/tests/src/transaction_payment_test.rs index 4642440b74..6d0541b1af 100644 --- a/pallets/runtime/tests/src/transaction_payment_test.rs +++ b/pallets/runtime/tests/src/transaction_payment_test.rs @@ -667,19 +667,15 @@ fn normal_tx_with_tip_ext() { TransactionError::ZeroTip as u8, )); let pre_err = ChargeTransactionPayment::::from(tip) - .prepare( - Val::Charge { - tip: 1, - who: user.clone(), - fee: 0, - subsidiser: None, - }, - &alice_origin, + .validate( + alice_origin.clone(), &call, &normal_info, len, + Default::default(), + &TxBaseImplication(()), + TransactionSource::InBlock, ) - .map(|_| ()) .unwrap_err(); assert!(pre_err == expected_err); @@ -717,24 +713,21 @@ fn operational_tx_with_tip_ext(cdd: AccountId, gc: AccountId) { // Valid operational tx with tip. Only CDD and Governance members can tip. assert!(ChargeTransactionPayment::::from(tip) - .prepare( - Val::Charge { - tip: tip, - who: user.clone(), - fee: TransactionPayment::compute_fee(len as u32, &operational_info, tip), - subsidiser: None, - }, - &alice_origin, + .validate( + alice_origin.clone(), &call, &operational_info, - len + len, + Default::default(), + &TxBaseImplication(()), + TransactionSource::InBlock, ) .is_err()); // Governance can tip. let gc_origin = RuntimeOrigin::signed(gc.clone()); - let val = ChargeTransactionPayment::::from(0) + let val = ChargeTransactionPayment::::from(tip) .validate( gc_origin.clone(), &call, @@ -753,7 +746,7 @@ fn operational_tx_with_tip_ext(cdd: AccountId, gc: AccountId) { // CDD can also tip. let cdd_origin = RuntimeOrigin::signed(cdd.clone()); - let val = ChargeTransactionPayment::::from(0) + let val = ChargeTransactionPayment::::from(tip) .validate( cdd_origin.clone(), &call, diff --git a/pallets/transaction-payment/src/lib.rs b/pallets/transaction-payment/src/lib.rs index 4a2fb59096..3d1e923fc8 100644 --- a/pallets/transaction-payment/src/lib.rs +++ b/pallets/transaction-payment/src/lib.rs @@ -549,7 +549,7 @@ where let DispatchInfo { class, .. } = dispatch_info; RuntimeDispatchInfo { - weight: dispatch_info.call_weight, + weight: dispatch_info.total_weight(), class, partial_fee, } @@ -587,7 +587,7 @@ where let DispatchInfo { class, .. } = dispatch_info; RuntimeDispatchInfo { - weight: dispatch_info.call_weight, + weight: dispatch_info.total_weight(), class, partial_fee: Self::compute_fee(len, &dispatch_info, 0u32.into()), } @@ -1029,13 +1029,11 @@ where refund: self.weight(call), }), Val::Charge { - tip: _, + tip, who, fee, subsidiser: _, } => { - let tip = self.ensure_valid_tip(&who, info)?; - let (_, subsidiser, imbalance) = self.withdraw_fee(&who, call, info, fee)?; Ok(Pre::Charge {