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
Open
lib.rs
file for your pallet in a text editor.Import
DispatchClass
andPays
by declaringuse frame_support::dispatch::{DispatchClass, Pays}
.Import
weights
primitives into the pallet.use frame_support:: { dispatch::{DispatchClass, Pays}, }, weights::Weight,
Declare a struct called
Conditional
and write an implementation ofWeighData
forConditional
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:
Open
lib.rs
file for your pallet in a text editor.Import
DispatchClass
andPays
by declaringuse 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
Related material
Last updated