Configurable Pallet Constants
To declare constant values within a runtime, it is necessary to import the
Get
trait from frame_support
use frame_support::traits::Get;
Configurable constants are declared as associated types in the pallet's configuration trait using
the Get<T>
syntax for any type T
.
pub trait Config: frame_system::Config {
type Event: From<Event> + Into<<Self as frame_system::Config>::Event>;
/// Maximum amount added per invocation
type MaxAddend: Get<u32>;
/// Frequency with which the stored value is deleted
type ClearFrequency: Get<Self::BlockNumber>;
}
In order to make these constants and their values appear in the runtime metadata, it is necessary to
declare them with the const
syntax. Usually constants are declared at
the top of this block, right after fn deposit_event
.
#[pallet::config]
pub trait Config: frame_system::Config {
type Event: From<Event> + IsType<<Self as frame_system::Config>::Event>;
/// Maximum amount added per invocation
type MaxAddend: Get<u32>;
/// Frequency with which the stored value is deleted
type ClearFrequency: Get<Self::BlockNumber>;
}
This example manipulates a single value in storage declared as SingleValue
.
#[pallet::storage]
#[pallet::getter(fn single_value)]
pub(super) type SingleValue<T: Config> = StorageValue<_, u32, ValueQuery>;
SingleValue
is set to 0
every ClearFrequency
number of blocks in the on_finalize
function
that runs at the end of blocks execution.
#[pallet::hooks]
impl<T: Config> Hooks<T::BlockNumber> for Pallet<T> {
fn on_finalize(n: T::BlockNumber) {
if (n % T::ClearFrequency::get()).is_zero() {
let c_val = SingleValue::<T>::get();
SingleValue::<T>::put(0u32);
Self::deposit_event(Event::Cleared(c_val));
}
}
}
Signed transactions may invoke the add_value
runtime method to increase SingleValue
as long as
each call adds less than MaxAddend
. There is no anti-sybil mechanism so a user could just split a
larger request into multiple smaller requests to overcome the MaxAddend
, but overflow is still
handled appropriately.
#[pallet::weight(10_000)]
pub fn add_value(origin: OriginFor<T>, val_to_add: u32) -> DispatchResultWithPostInfo {
let _ = ensure_signed(origin)?;
ensure!(
val_to_add <= T::MaxAddend::get(),
"value must be <= maximum add amount constant"
);
// previous value got
let c_val = SingleValue::<T>::get();
// checks for overflow when new value added
let result = match c_val.checked_add(val_to_add) {
Some(r) => r,
None => {
return Err(DispatchErrorWithPostInfo {
post_info: PostDispatchInfo::from(()),
error: DispatchError::Other("Addition overflowed"),
})
}
};
SingleValue::<T>::put(result);
Self::deposit_event(Event::Added(c_val, val_to_add, result));
Ok(().into())
}
In more complex patterns, the constant value may be used as a static, base value that is scaled by a multiplier to incorporate stateful context for calculating some dynamic fee (i.e. floating transaction fees).
Supplying the Constant Value
When the pallet is included in a runtime, the runtime developer supplies the value of the constant
using the
parameter_types!
macro. This
pallet is included in the super-runtime
where we see the following macro invocation and trait
implementation.
#![allow(unused)] fn main() { parameter_types! { pub const MaxAddend: u32 = 1738; pub const ClearFrequency: u32 = 10; } #[pallet::config] pub trait Config: frame_system::Config { type Event: From<Event> + IsType<<Self as frame_system::Config>::Event>; /// Maximum amount added per invocation type MaxAddend: Get<u32>; /// Frequency with which the stored value is deleted type ClearFrequency: Get<Self::BlockNumber>; } }