Use Conditional Weights

Substrate provides a mechanism known as transaction weighting to quantify the resources consumed while executing a transaction. Typically, we use the weight functions returned from our benchmarking for this. But Substrate also allow us to apply a totally different weight function based on certain condition. We will walk through an example in this guide. Once defined, it can be used directly in your pallet, written as such:

#[pallet::weight(Conditional(\<your condition\>)

Objectives

  • Create and use custom weighting in your pallet.

  • Apply different weight functions based on certain condition on computing extrinsic's weight value.

Here are the different traits we'll be implementing:

  • `WeighData`: Weigh the data in a function. `pallet::weight` expects whatever implements `WeighData` to replace `T` with a tuple of the dispatch arguments.

  • `PaysFee`: Designate whether the dispatch pays a fee or not.

  • `ClassifyDispatch`: A way to tell the runtime about the type of dispatch being made.

Write the Weight struct

  1. Open lib.rs file for your pallet in a text editor.

  2. Import DispatchClass and Pays by declaring use frame_support::dispatch::{DispatchClass, Pays}.

  3. Import weights primitives into the pallet.

    use frame_support:: {
     dispatch::{DispatchClass, Pays},
    },
    weights::Weight,
    
  4. Declare a struct called Conditional and write an implementation of WeighData for Conditional where the first parameter is the condition that evaluates to a boolean value.

    In the following example, if the condition is true, the weight will be linear to the input. Otherwise the weight will be a constant.

    pub struct Conditional(u32);
    impl WeighData<(&bool, &u32)> for Conditional {
       fn weigh_data(&self, (switch, val): (&bool, &u32)) -> Weight {
    
       // If the first parameter is true, then the weight is linear in the second parameter.
          if *switch {
             val.saturating_mul(self.0)
          }
       // Otherwise the weight is constant.
          else {
             self.0
          }
       }
    }

Classify dispatch calls

Add dispatch::{ClassifyDispatch, DispatchClass, Pays} to your pallet's frame_support imports. Since this implementation requires a DispatchClass, use default to classify all calls as normal:

  1. Open lib.rs file for your pallet in a text editor.

  2. Import DispatchClass and Pays by declaring use frame_support::dispatch::{DispatchClass, Pays}.

    use frame_support::dispatch::{ClassifyDispatch, DispatchClass, Pays};
    // -- snip --
    
    // Implement ClassifyDispatch
    impl<T> ClassifyDispatch<T> for Conditional {
       fn classify_dispatch(&self, _: T) -> DispatchClass {
          Default::default()
       }
    }

## Implement Pays

Specify how `Pays` is used for the custom `WeighData` struct.
Setting this to `true` require the caller of this dispatch to pay a fee:

1. Open `lib.rs` file for your pallet in a text editor.

1. Implement `Pays` for the Conditional structure.

 ```rust
 impl Pays for Conditional {
    fn pays_fee(&self) -> bool {
       true
    }
 }

Use the weighting struct for an extrinsic

Use the conditional weighting struct on your pallet's extrinsics like this:

#[pallet::weight(Conditional(200))]
   fn example(origin: OriginFor<T>, add_flag: bool, val: u32>) -> DispatchResult {
   //...
   }

Examples

Last updated