Can we get the pallet and function data from call (type) passed as a parameter to a function in substrate?

Viewed 160
fn pre_dispatch(
        self,
        who: &Self::AccountId,
        call: &Self::Call,
        info: &DispatchInfoOf<Self::Call>,
        len: usize
    ) -> Result<Self::Pre, TransactionValidityError> {
        let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;
        Ok((self.0, who.clone(), imbalance))
    }

(The above code is copied from txn-payment-pallet)
Here can we get function name and parameters from the call(one of the parameter), And based on the function-name , pallet , parameters passed by user, I want to compute fee.
For example , If it is call from pallet-staking::bond(x : amount_of_tokens_to_be_bonded) and I want to set fee for txn based on x. Is that possible?? Like wise I want to set fee based on function-call parameters entered by user.

1 Answers

You can, but it requires a bit of type juggling to do so.

First, you need to realize that type Call = T::Call; in ChargeTransactionPayment. Looking at trait Config in pallet_transaction_payment, no type Call can be seen there. Instead, this type is coming from frame_system::Config (which is the super-trait of all pallets).

A brief look at the top level runtime aggregator file unravels that this Call type is essentially the outer-call of the runtime, an enum that encapsulates the call of all pallets.

That being said, the main point here is that from within pallet_transaction_payment, we cannot know of this outer-call contains this particular call from staking or not. To do so, you need to enforce this assumption via a new trait bound, namely IsSubType. This trait is specifically made to convert from wrapping type (like the outer-call) into its inner variants. See an example of this type being implemented for node_runtime's Call type.

Applying the following diff to substrate master should do exactly what you want .

diff --git a/Cargo.lock b/Cargo.lock
index ea54adf99e..df66185163 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -6074,6 +6074,7 @@ dependencies = [
  "frame-support",
  "frame-system",
  "pallet-balances",
+ "pallet-staking",
  "parity-scale-codec",
  "scale-info",
  "serde",
diff --git a/frame/transaction-payment/Cargo.toml b/frame/transaction-payment/Cargo.toml
index 1d3066e39f..0e705514bb 100644
--- a/frame/transaction-payment/Cargo.toml
+++ b/frame/transaction-payment/Cargo.toml
@@ -27,6 +27,7 @@ sp-std = { version = "4.0.0-dev", default-features = false, path = "../../primit
 
 frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" }
 frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" }
+pallet-staking = { version = "4.0.0-dev", default-features = false, path = "../staking" }
 
 [dev-dependencies]
 serde_json = "1.0.68"
@@ -44,5 +45,6 @@ std = [
    "sp-std/std",
    "frame-support/std",
    "frame-system/std",
+   "pallet-staking/std",
 ]
 try-runtime = ["frame-support/try-runtime"]
diff --git a/frame/transaction-payment/src/lib.rs b/frame/transaction-payment/src/lib.rs
index 59d94a8237..3b0803663d 100644
--- a/frame/transaction-payment/src/lib.rs
+++ b/frame/transaction-payment/src/lib.rs
@@ -251,7 +251,7 @@ pub mod pallet {
    pub struct Pallet<T>(_);
 
    #[pallet::config]
-   pub trait Config: frame_system::Config {
+   pub trait Config: frame_system::Config + pallet_staking::Config {
        /// Handler for withdrawing, refunding and depositing the transaction fee.
        /// Transaction fees are withdrawn before the transaction is executed.
        /// After the transaction was executed the transaction weight can be
@@ -696,7 +696,8 @@ impl<T: Config> sp_std::fmt::Debug for ChargeTransactionPayment<T> {
 impl<T: Config> SignedExtension for ChargeTransactionPayment<T>
 where
    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,
-   T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
+   T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>
+       + frame_support::traits::IsSubType<pallet_staking::Call<T>>,
 {
    const IDENTIFIER: &'static str = "ChargeTransactionPayment";
    type AccountId = T::AccountId;
@@ -736,8 +737,15 @@ where
        info: &DispatchInfoOf<Self::Call>,
        len: usize,
    ) -> Result<Self::Pre, TransactionValidityError> {
-       let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;
-       Ok((self.0, who.clone(), imbalance))
+       use frame_support::traits::IsSubType;
+       if let Some(pallet_staking::Call::bond_extra { .. }) = call.is_sub_type() {
+           // skip
+           todo!()
+       } else {
+           // default impl
+           let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;
+           Ok((self.0, who.clone(), imbalance))
+       }
    }
 
    fn post_dispatch(

Note that this approach is implying that the pallet_staking::Config be present in the runtime, which is not aligned with the modularity of Frame, and ergo is not implemented. If you want to have this feature, as of now, the only way is to fork pallet_transaction_payment and customize it a bit for your runtime.

Related