Developers Home»How-to Guide»Conditional weighting struct

Conditional weighting struct

Goal

Create and use a custom weighting struct in your pallet.

Use Cases

Applying totally different weight functions based on certain condition on computing extrinsic's weight value.

Overview

Substrate provides a mechanism known as transaction weighting to quantify the resources consumed while executing a transaction. Typically we will just 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>)].

Here are the different traits we'll be implementing:

  • WeighData: Weigh the data in a function. pallet::weight expects whatever implements WeighData<T> 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.

Steps

1. Write the WeighData struct

Import DispatchClass, WeighData and PaysFee by declaring use frame_support::dispatch::{DispatchClass, PaysFee, WeighData} at the top of your pallet.

In your pallet's lib.rs file, declare a struct called Conditional and write an implementation of WeighData for Conditional, where the first parameter is the condition that evaluate 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.

pallets/example/src/lib.rs

use frame_support::dispatch::{DispatchClass, PaysFee, WeighData};

// -- snip --

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
    }
  }
}

2. Classify dispatch calls

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

pallets/example/src/lib.rs

use frame_support::weights::{ClassifyDispatch, DispatchClass, Pays, Weight};

// -- snip --

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

3. Implement PaysFee

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

pallets/example/src/lib.rs

use frame_support::weights::PaysFee

// -- snip --

// Implement PaysFee
impl PaysFee for Conditional {
    fn pays_fee(&self) -> bool {
      true
    }
}

4. Use the weighting struct for an extrinsic

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

pallets/example/src/lib.rs

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

Examples

Docs

Last edit: on

Run into problems?
Let us Know