Configure runtime constants
Declaring a constant value in a runtime is a useful tool to either define fixed values or define values that change dynamically according to some factor.
This guide shows you how to create pallet constants that are used to reset a u32
value in storage.
This value, we'll call SingleValue
, can also be modified using a method called add_value
.
Configure your pallet's types, events and errors
-
Define the constants in your pallet.
MaxAddend
will be the value displayed in metadata.ClearFrequency
keeps track of the block numbers and will be used to resetSingleValue
.
#[pallet::config] pub trait Config: frame_system::Config { type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>; #[pallet::constant] // put the constant in metadata /// Maximum amount added per invocation. type MaxAddend: Get<u32>; /// Frequency with which the stored value is deleted. type ClearFrequency: Get<Self::BlockNumber>; }
-
Declare your storage items and events.
Using the storage attribute macro, declare
SingleValue
, the value that gets modified every block cycle.#[pallet::storage] #[pallet::getter(fn single_value)] pub(super) type SingleValue<T: Config> = StorageValue<_, u32, ValueQuery>;
-
Define your pallet's events.
#[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event<T: Config> { /// The value has been added to. The parameters are /// (initial amount, amount added, final amount). Added(u32, u32, u32), /// The value has been cleared. The parameter is the value before clearing. Cleared(u32) }
-
Add an error that handles operation overflow:
#[pallet::error] pub enum Error<T> { /// An operation would lead to an overflow. Overflow }
Create pallet methods and runtime constants
-
Configure
on_finalize
.SingleValue
is set to 0 everyClearFrequency
number of blocks in theon_finalize
function that runs at the end of block execution. Specify this logic under the#[pallet::hooks]
attribute:#[pallet::hooks] impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> { fn on_finalize(n: T::BlockNumber) { if (n % T::ClearFrequency::get()).is_zero() { let current_value = <SingleValue<T>>::get(); <SingleValue<T>>::put(0u32); Self::deposit_event(Event::Cleared(current_value)); } } }
-
Create a method that allows users to specify the value.
The
add_value
method increasesSingleValue
so long as each call adds to less than theMaxAddend
value.For this method you must:
- Include checks.
- Keep track of the previous value.
- Check for overflow.
- Update
SingleValue
.
// Extrinsics callable from outside the runtime. #[pallet::call] impl<T: Config> Pallet<T> { #[pallet::weight(1_000)] 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 = c_val.checked_add(val_to_add).ok_or(Error::<T>::Overflow)?; <SingleValue<T>>::put(result); Self::deposit_event(Event::Added(c_val, val_to_add, result)); Ok(().into()) } }
-
Supply the constant value for your runtime.
In
runtime/src/lib.rs
, declare the values for your pallet's runtime implementation ofMaxAddend
andClearFrequency
:parameter_types! { pub const MaxAddend: u32 = 1738; pub const ClearFrequency: u32 = 10; } impl constant_config::Config for Runtime { type RuntimeEvent = RuntimeEvent; type MaxAddend = MaxAddend; type ClearFrequency = ClearFrequency; }