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 implementsWeighData<T>
to replaceT
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 {
//...
}