Newer
Older
#[crate::storage_alias]
type GenericData2<T> = StorageMap<
Qinxuan Chen
committed
System,
Blake2_128Concat,
<T as Config>::BlockNumber,
<T as Config>::BlockNumber,
>;
Qinxuan Chen
committed
assert_eq!(Pallet::<Runtime>::generic_data2(5), None);
GenericData2::<Runtime>::insert(5, 5);
assert_eq!(Pallet::<Runtime>::generic_data2(5), Some(5));
/// Some random docs that ensure that docs are accepted
#[crate::storage_alias]
pub type GenericData<T> = StorageMap<
Test2,
Blake2_128Concat,
<T as Config>::BlockNumber,
<T as Config>::BlockNumber,
>;
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
#[test]
fn storage_value_mutate_exists_should_work() {
new_test_ext().execute_with(|| {
#[crate::storage_alias]
pub type Value = StorageValue<Test, u32>;
assert!(!Value::exists());
Value::mutate_exists(|v| *v = Some(1));
assert!(Value::exists());
assert_eq!(Value::get(), Some(1));
// removed if mutated to `None`
Value::mutate_exists(|v| *v = None);
assert!(!Value::exists());
});
}
#[test]
fn storage_value_try_mutate_exists_should_work() {
new_test_ext().execute_with(|| {
#[crate::storage_alias]
pub type Value = StorageValue<Test, u32>;
type TestResult = result::Result<(), &'static str>;
assert!(!Value::exists());
// mutated if `Ok`
assert_ok!(Value::try_mutate_exists(|v| -> TestResult {
*v = Some(1);
Ok(())
}));
assert!(Value::exists());
assert_eq!(Value::get(), Some(1));
// no-op if `Err`
assert_noop!(
Value::try_mutate_exists(|v| -> TestResult {
*v = Some(2);
Err("nah")
}),
"nah"
);
assert_eq!(Value::get(), Some(1));
// removed if mutated to`None`
assert_ok!(Value::try_mutate_exists(|v| -> TestResult {
*v = None;
Ok(())
}));
assert!(!Value::exists());
});
}
new_test_ext().execute_with(|| {
Qinxuan Chen
committed
type OptionLinkedMap = self::frame_system::OptionLinkedMap<Runtime>;
OptionLinkedMap::insert(1, 1);
assert_eq!(OptionLinkedMap::get(1), Some(1));
OptionLinkedMap::insert(1, 2);
assert_eq!(OptionLinkedMap::get(1), Some(2));
});
}
new_test_ext().execute_with(|| {
Qinxuan Chen
committed
type OptionLinkedMap = self::frame_system::OptionLinkedMap<Runtime>;
OptionLinkedMap::insert(0, 0);
OptionLinkedMap::insert(1, 1);
OptionLinkedMap::insert(2, 2);
OptionLinkedMap::insert(3, 3);
let collect = || OptionLinkedMap::iter().collect::<Vec<_>>().sorted();
assert_eq!(collect(), vec![(0, 0), (1, 1), (2, 2), (3, 3)]);
// Two existing
OptionLinkedMap::swap(1, 2);
assert_eq!(collect(), vec![(0, 0), (1, 2), (2, 1), (3, 3)]);
// Back to normal
OptionLinkedMap::swap(2, 1);
assert_eq!(collect(), vec![(0, 0), (1, 1), (2, 2), (3, 3)]);
// Left existing
OptionLinkedMap::swap(2, 5);
assert_eq!(collect(), vec![(0, 0), (1, 1), (3, 3), (5, 2)]);
// Right existing
OptionLinkedMap::swap(5, 2);
assert_eq!(collect(), vec![(0, 0), (1, 1), (2, 2), (3, 3)]);
#[test]
fn double_map_swap_works() {
new_test_ext().execute_with(|| {
Qinxuan Chen
committed
type DataDM = self::frame_system::DataDM<Runtime>;
DataDM::insert(0, 1, 1);
DataDM::insert(1, 0, 2);
DataDM::insert(1, 1, 3);
let get_all = || {
vec![
DataDM::get(0, 1),
DataDM::get(1, 0),
DataDM::get(1, 1),
DataDM::get(2, 0),
DataDM::get(2, 1),
]
};
assert_eq!(get_all(), vec![1, 2, 3, 0, 0]);
// Two existing
DataDM::swap(0, 1, 1, 0);
assert_eq!(get_all(), vec![2, 1, 3, 0, 0]);
// Left existing
DataDM::swap(1, 0, 2, 0);
assert_eq!(get_all(), vec![2, 0, 3, 1, 0]);
// Right existing
DataDM::swap(2, 1, 1, 1);
assert_eq!(get_all(), vec![2, 0, 0, 1, 3]);
});
}
fn map_basic_insert_remove_should_work() {
new_test_ext().execute_with(|| {
Qinxuan Chen
committed
type Map = self::frame_system::Data<Runtime>;
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
assert_eq!(Map::get(&15u32), 42u64);
// get / insert / take
let key = 17u32;
assert_eq!(Map::get(&key), 0u64);
Map::insert(key, 4u64);
assert_eq!(Map::get(&key), 4u64);
assert_eq!(Map::take(&key), 4u64);
assert_eq!(Map::get(&key), 0u64);
// mutate
Map::mutate(&key, |val| {
*val = 15;
});
assert_eq!(Map::get(&key), 15u64);
// remove
Map::remove(&key);
assert_eq!(Map::get(&key), 0u64);
});
}
#[test]
new_test_ext().execute_with(|| {
Qinxuan Chen
committed
type Map = self::frame_system::Data<Runtime>;
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(15, 42)]);
// insert / remove
let key = 17u32;
Map::insert(key, 4u64);
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(15, 42), (key, 4)]);
assert_eq!(Map::take(&15), 42u64);
assert_eq!(Map::take(&key), 4u64);
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![]);
// Add couple of more elements
Map::insert(key, 42u64);
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(key, 42)]);
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(key, 42), (key + 1, 43)]);
// mutate
let key = key + 2;
Map::mutate(&key, |val| {
*val = 15;
});
assert_eq!(
Map::iter().collect::<Vec<_>>().sorted(),
vec![(key - 2, 42), (key - 1, 43), (key, 15)]
);
Map::mutate(&key, |val| {
*val = 17;
});
assert_eq!(
Map::iter().collect::<Vec<_>>().sorted(),
vec![(key - 2, 42), (key - 1, 43), (key, 17)]
);
// remove first
Map::remove(&key);
assert_eq!(
Map::iter().collect::<Vec<_>>().sorted(),
vec![(key - 2, 42), (key - 1, 43)]
);
// remove last from the list
Map::remove(&(key - 2));
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(key - 1, 43)]);
// remove the last element
Map::remove(&(key - 1));
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![]);
#[test]
fn double_map_basic_insert_remove_remove_prefix_with_commit_should_work() {
let key1 = 17u32;
let key2 = 18u32;
Qinxuan Chen
committed
type DoubleMap = self::frame_system::DataDM<Runtime>;
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
let mut e = new_test_ext();
e.execute_with(|| {
// initialized during genesis
assert_eq!(DoubleMap::get(&15u32, &16u32), 42u64);
// get / insert / take
assert_eq!(DoubleMap::get(&key1, &key2), 0u64);
DoubleMap::insert(&key1, &key2, &4u64);
assert_eq!(DoubleMap::get(&key1, &key2), 4u64);
assert_eq!(DoubleMap::take(&key1, &key2), 4u64);
assert_eq!(DoubleMap::get(&key1, &key2), 0u64);
// mutate
DoubleMap::mutate(&key1, &key2, |val| *val = 15);
assert_eq!(DoubleMap::get(&key1, &key2), 15u64);
// remove
DoubleMap::remove(&key1, &key2);
assert_eq!(DoubleMap::get(&key1, &key2), 0u64);
// remove prefix
DoubleMap::insert(&key1, &key2, &4u64);
DoubleMap::insert(&key1, &(key2 + 1), &4u64);
DoubleMap::insert(&(key1 + 1), &key2, &4u64);
DoubleMap::insert(&(key1 + 1), &(key2 + 1), &4u64);
});
e.commit_all().unwrap();
e.execute_with(|| {
assert!(matches!(
DoubleMap::clear_prefix(&key1, u32::max_value(), None),
MultiRemovalResults { maybe_cursor: None, backend: 2, unique: 2, loops: 2 }
));
assert_eq!(DoubleMap::get(&key1, &key2), 0u64);
assert_eq!(DoubleMap::get(&key1, &(key2 + 1)), 0u64);
assert_eq!(DoubleMap::get(&(key1 + 1), &key2), 4u64);
assert_eq!(DoubleMap::get(&(key1 + 1), &(key2 + 1)), 4u64);
});
}
#[test]
fn double_map_basic_insert_remove_remove_prefix_should_work() {
new_test_ext().execute_with(|| {
let key1 = 17u32;
let key2 = 18u32;
Qinxuan Chen
committed
type DoubleMap = self::frame_system::DataDM<Runtime>;
assert_eq!(DoubleMap::get(&15u32, &16u32), 42u64);
// get / insert / take
assert_eq!(DoubleMap::get(&key1, &key2), 0u64);
DoubleMap::insert(&key1, &key2, &4u64);
assert_eq!(DoubleMap::get(&key1, &key2), 4u64);
assert_eq!(DoubleMap::take(&key1, &key2), 4u64);
assert_eq!(DoubleMap::get(&key1, &key2), 0u64);
DoubleMap::mutate(&key1, &key2, |val| *val = 15);
assert_eq!(DoubleMap::get(&key1, &key2), 15u64);
DoubleMap::remove(&key1, &key2);
assert_eq!(DoubleMap::get(&key1, &key2), 0u64);
DoubleMap::insert(&key1, &key2, &4u64);
DoubleMap::insert(&key1, &(key2 + 1), &4u64);
DoubleMap::insert(&(key1 + 1), &key2, &4u64);
DoubleMap::insert(&(key1 + 1), &(key2 + 1), &4u64);
// all in overlay
assert!(matches!(
DoubleMap::clear_prefix(&key1, u32::max_value(), None),
MultiRemovalResults { maybe_cursor: None, backend: 0, unique: 0, loops: 0 }
));
// Note this is the incorrect answer (for now), since we are using v2 of
// `clear_prefix`.
// When we switch to v3, then this will become:
// MultiRemovalResults:: { maybe_cursor: None, backend: 0, unique: 2, loops: 2 },
DoubleMap::clear_prefix(&key1, u32::max_value(), None),
MultiRemovalResults { maybe_cursor: None, backend: 0, unique: 0, loops: 0 }
assert_eq!(DoubleMap::get(&key1, &key2), 0u64);
assert_eq!(DoubleMap::get(&key1, &(key2 + 1)), 0u64);
assert_eq!(DoubleMap::get(&(key1 + 1), &key2), 4u64);
assert_eq!(DoubleMap::get(&(key1 + 1), &(key2 + 1)), 4u64);
});
}
#[test]
fn double_map_append_should_work() {
new_test_ext().execute_with(|| {
Qinxuan Chen
committed
type DoubleMap = self::frame_system::AppendableDM<Runtime>;
let key1 = 17u32;
let key2 = 18u32;
DoubleMap::insert(&key1, &key2, &vec![1]);
DoubleMap::append(&key1, &key2, 2);
assert_eq!(DoubleMap::get(&key1, &key2), &[1, 2]);
#[test]
fn double_map_mutate_exists_should_work() {
new_test_ext().execute_with(|| {
Qinxuan Chen
committed
type DoubleMap = self::frame_system::DataDM<Runtime>;
let (key1, key2) = (11, 13);
// mutated
DoubleMap::mutate_exists(key1, key2, |v| *v = Some(1));
assert_eq!(DoubleMap::get(&key1, key2), 1);
// removed if mutated to `None`
DoubleMap::mutate_exists(key1, key2, |v| *v = None);
assert!(!DoubleMap::contains_key(&key1, key2));
});
}
#[test]
fn double_map_try_mutate_exists_should_work() {
new_test_ext().execute_with(|| {
Qinxuan Chen
committed
type DoubleMap = self::frame_system::DataDM<Runtime>;
type TestResult = Result<(), &'static str>;
let (key1, key2) = (11, 13);
// mutated if `Ok`
assert_ok!(DoubleMap::try_mutate_exists(key1, key2, |v| -> TestResult {
*v = Some(1);
Ok(())
}));
assert_eq!(DoubleMap::get(&key1, key2), 1);
// no-op if `Err`
assert_noop!(
DoubleMap::try_mutate_exists(key1, key2, |v| -> TestResult {
*v = Some(2);
Err("nah")
}),
"nah"
);
// removed if mutated to`None`
assert_ok!(DoubleMap::try_mutate_exists(key1, key2, |v| -> TestResult {
*v = None;
Ok(())
}));
assert!(!DoubleMap::contains_key(&key1, key2));
});
fn expected_metadata() -> PalletStorageMetadataIR {
PalletStorageMetadataIR {
Qinxuan Chen
committed
prefix: "System",
StorageEntryMetadataIR {
modifier: StorageEntryModifierIR::Default,
ty: StorageEntryTypeIR::Map {
hashers: vec![StorageHasherIR::Twox64Concat],
key: scale_info::meta_type::<u32>(),
value: scale_info::meta_type::<u64>(),
},
default: vec![0, 0, 0, 0, 0, 0, 0, 0],
docs: vec![],
StorageEntryMetadataIR {
modifier: StorageEntryModifierIR::Optional,
ty: StorageEntryTypeIR::Map {
hashers: vec![StorageHasherIR::Blake2_128Concat],
key: scale_info::meta_type::<u32>(),
value: scale_info::meta_type::<u32>(),
},
default: vec![0],
docs: vec![],
StorageEntryMetadataIR {
modifier: StorageEntryModifierIR::Default,
ty: StorageEntryTypeIR::Map {
hashers: vec![StorageHasherIR::Identity],
key: scale_info::meta_type::<u32>(),
value: scale_info::meta_type::<u32>(),
},
default: vec![0, 0, 0, 0],
docs: vec![],
StorageEntryMetadataIR {
modifier: StorageEntryModifierIR::Optional,
ty: StorageEntryTypeIR::Map {
hashers: vec![StorageHasherIR::Blake2_128Concat],
key: scale_info::meta_type::<u32>(),
value: scale_info::meta_type::<u32>(),
},
default: vec![0],
docs: vec![],
StorageEntryMetadataIR {
modifier: StorageEntryModifierIR::Default,
ty: StorageEntryTypeIR::Map {
hashers: vec![
StorageHasherIR::Twox64Concat,
StorageHasherIR::Blake2_128Concat,
],
key: scale_info::meta_type::<(u32, u32)>(),
value: scale_info::meta_type::<u64>(),
},
default: vec![0, 0, 0, 0, 0, 0, 0, 0],
docs: vec![],
StorageEntryMetadataIR {
modifier: StorageEntryModifierIR::Default,
ty: StorageEntryTypeIR::Map {
hashers: vec![StorageHasherIR::Blake2_128Concat, StorageHasherIR::Identity],
key: scale_info::meta_type::<(u32, u32)>(),
value: scale_info::meta_type::<u32>(),
},
default: vec![0, 0, 0, 0],
docs: vec![],
StorageEntryMetadataIR {
modifier: StorageEntryModifierIR::Optional,
ty: StorageEntryTypeIR::Map {
hashers: vec![
StorageHasherIR::Blake2_128Concat,
StorageHasherIR::Twox64Concat,
],
key: scale_info::meta_type::<(u32, u32)>(),
value: scale_info::meta_type::<u32>(),
},
default: vec![0],
docs: vec![],
StorageEntryMetadataIR {
modifier: StorageEntryModifierIR::Default,
ty: StorageEntryTypeIR::Map {
StorageHasherIR::Blake2_128Concat,
StorageHasherIR::Blake2_128Concat,
],
key: scale_info::meta_type::<(u32, u32)>(),
value: scale_info::meta_type::<Vec<u32>>(),
},
default: vec![0],
docs: vec![],
#[test]
fn store_metadata() {
Qinxuan Chen
committed
let metadata = Pallet::<Runtime>::storage_metadata();
pretty_assertions::assert_eq!(expected_metadata(), metadata);
parameter_types! {
storage StorageParameter: u64 = 10;
}
#[test]
fn check_storage_parameter_type_works() {
TestExternalities::default().execute_with(|| {
assert_eq!(sp_io::hashing::twox_128(b":StorageParameter:"), StorageParameter::key());
assert_eq!(10, StorageParameter::get());
StorageParameter::set(&300);
assert_eq!(300, StorageParameter::get());
})
}
/// Private module re-exporting items used by frame support macros.
#[doc(hidden)]
pub mod _private {
pub use sp_inherents;
}
/// Prelude to be used for pallet testing, for ease of use.
#[cfg(feature = "std")]
pub mod testing_prelude {
pub use super::{
assert_err, assert_err_ignore_postinfo, assert_err_with_weight, assert_error_encoded_size,
assert_noop, assert_ok, assert_storage_noop, bounded_btree_map, bounded_vec,
parameter_types, traits::Get,
};
pub use sp_arithmetic::assert_eq_error_rate;
}
/// Prelude to be used alongside pallet macro, for ease of use.
pub mod pallet_prelude {
dispatch::{
DispatchClass, DispatchError, DispatchResult, DispatchResultWithPostInfo, Parameter,
Pays,
},
ensure,
inherent::{InherentData, InherentIdentifier, ProvideInherent},
storage,
storage::{
bounded_vec::BoundedVec,
types::{
CountedStorageMap, Key as NMapKey, OptionQuery, ResultQuery, StorageDoubleMap,
StorageMap, StorageNMap, StorageValue, ValueQuery,
![thiolliere thiolliere's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
thiolliere
committed
traits::{
ConstU32, EnsureOrigin, GenesisBuild, Get, GetDefault, GetStorageVersion, Hooks,
IsType, PalletInfoAccess, StorageInfoTrait, StorageVersion, TypedGet,
![thiolliere thiolliere's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
thiolliere
committed
},
Blake2_128, Blake2_128Concat, Blake2_256, CloneNoBound, DebugNoBound, EqNoBound, Identity,
PartialEqNoBound, RuntimeDebug, RuntimeDebugNoBound, Twox128, Twox256, Twox64Concat,
pub use codec::{Decode, Encode, MaxEncodedLen};
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
pub use frame_support::pallet_macros::*;
pub use frame_support_procedural::register_default_impl;
pub use scale_info::TypeInfo;
pub use sp_inherents::MakeFatalError;
pub use sp_runtime::{
traits::{MaybeSerializeDeserialize, Member, ValidateUnsigned},
transaction_validity::{
InvalidTransaction, TransactionLongevity, TransactionPriority, TransactionSource,
TransactionTag, TransactionValidity, TransactionValidityError, UnknownTransaction,
ValidTransaction,
MAX_MODULE_ERROR_ENCODED_SIZE,
pub use sp_std::marker::PhantomData;
pub use sp_weights::Weight;
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// The `pallet` attribute macro defines a pallet that can be used with
/// [`construct_runtime!`]. It must be attached to a module named `pallet` as follows:
///
/// ```ignore
/// #[pallet]
/// pub mod pallet {
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// ...
/// }
/// ```
///
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// Note that various types can be automatically imported using
/// [`frame_support::pallet_prelude`] and `frame_system::pallet_prelude`:
///
/// ```ignore
/// #[pallet]
/// pub mod pallet {
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// use frame_support::pallet_prelude::*;
/// use frame_system::pallet_prelude::*;
/// ...
/// }
/// ```
///
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
/// # pallet::* Attributes
///
/// The `pallet` macro will parse any items within your `pallet` module that are annotated with
/// `#[pallet::*]` attributes. Some of these attributes are mandatory and some are optional,
/// and they can attach to different types of items within your pallet depending on the
/// attribute in question. The full list of `#[pallet::*]` attributes is shown below in the
/// order in which they are mentioned in this document:
///
/// * [`pallet::pallet`](#pallet-struct-placeholder-palletpallet-mandatory)
/// * [`pallet::config`](#config-trait-palletconfig-mandatory)
/// * [`pallet::constant`](#palletconstant)
/// * [`pallet::disable_frame_system_supertrait_check`](#disable_supertrait_check)
/// * [`pallet::generate_store($vis trait Store)`](#palletgenerate_storevis-trait-store)
/// * [`pallet::storage_version`](#palletstorage_version)
/// * [`pallet::hooks`](#hooks-pallethooks-optional)
/// * [`pallet::call`](#call-palletcall-optional)
/// * [`pallet::weight($expr)`](#palletweightexpr)
/// * [`pallet::compact`](#palletcompact-some_arg-some_type)
/// * [`pallet::call_index($idx)`](#palletcall_indexidx)
/// * [`pallet::extra_constants`](#extra-constants-palletextra_constants-optional)
/// * [`pallet::error`](#error-palleterror-optional)
/// * [`pallet::event`](#event-palletevent-optional)
/// * [`pallet::generate_deposit($visibility fn
/// deposit_event)`](#palletgenerate_depositvisibility-fn-deposit_event)
/// * [`pallet::storage`](#storage-palletstorage-optional)
/// * [`pallet::getter(fn $my_getter_fn_name)`](#palletgetterfn-my_getter_fn_name-optional)
/// * [`pallet::storage_prefix = "SomeName"`](#palletstorage_prefix--somename-optional)
/// * [`pallet::unbounded`](#palletunbounded-optional)
/// * [`pallet::whitelist_storage`](#palletwhitelist_storage-optional)
/// * [`cfg(..)`](#cfg-for-storage) (on storage items)
/// * [`pallet::type_value`](#type-value-pallettype_value-optional)
/// * [`pallet::genesis_config`](#genesis-config-palletgenesis_config-optional)
/// * [`pallet::genesis_build`](#genesis-build-palletgenesis_build-optional)
/// * [`pallet::inherent`](#inherent-palletinherent-optional)
/// * [`pallet::validate_unsigned`](#validate-unsigned-palletvalidate_unsigned-optional)
/// * [`pallet::origin`](#origin-palletorigin-optional)
/// * [`pallet::composite_enum`](#composite-enum-palletcomposite_enum-optional)
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
///
/// Note that at compile-time, the `#[pallet]` macro will analyze and expand all of these
/// attributes, ultimately removing their AST nodes before they can be parsed as real
/// attribute macro calls. This means that technically we do not need attribute macro
/// definitions for any of these attributes, however, for consistency and discoverability
/// reasons, we still maintain stub attribute macro definitions for all of these attributes in
/// the [`pallet_macros`] module which is automatically included in all pallets as part of the
/// pallet prelude. The actual "work" for all of these attribute macros can be found in the
/// macro expansion for `#[pallet]`.
///
/// Also note that in this document, pallet attributes are explained using the syntax of
/// non-instantiable pallets. For an example of an instantiable pallet, see [this
/// example](#example-of-an-instantiable-pallet).
///
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
/// # Dev Mode (`#[pallet(dev_mode)]`)
///
/// Specifying the argument `dev_mode` on the `#[pallet]` or `#[frame_support::pallet]`
/// attribute attached to your pallet module will allow you to enable dev mode for a pallet.
/// The aim of dev mode is to loosen some of the restrictions and requirements placed on
/// production pallets for easy tinkering and development. Dev mode pallets should not be used
/// in production. Enabling dev mode has the following effects:
///
/// * Weights no longer need to be specified on every `#[pallet::call]` declaration. By
/// default, dev mode pallets will assume a weight of zero (`0`) if a weight is not
/// specified. This is equivalent to specifying `#[weight(0)]` on all calls that do not
/// specify a weight.
/// * All storages are marked as unbounded, meaning you do not need to implement
/// `MaxEncodedLen` on storage types. This is equivalent to specifying `#[pallet::unbounded]`
/// on all storage type definitions.
///
/// Note that the `dev_mode` argument can only be supplied to the `#[pallet]` or
/// `#[frame_support::pallet]` attribute macro that encloses your pallet module. This argument
/// cannot be specified anywhere else, including but not limited to the `#[pallet::pallet]`
/// attribute macro.
///
/// <div class="example-wrap" style="display:inline-block"><pre class="compile_fail"
/// style="white-space:normal;font:inherit;">
/// <strong>WARNING</strong>:
/// You should not deploy or use dev mode pallets in production. Doing so can break your chain
/// and therefore should never be done. Once you are done tinkering, you should remove the
/// 'dev_mode' argument from your #[pallet] declaration and fix any compile errors before
/// attempting to use your pallet in a production scenario.
/// </pre></div>
///
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// # Pallet struct placeholder: `#[pallet::pallet]` (mandatory)
///
/// The pallet struct placeholder `#[pallet::pallet]` is mandatory and allows you to specify
/// pallet information.
///
/// The struct must be defined as follows:
/// ```ignore
/// #[pallet::pallet]
/// pub struct Pallet<T>(_);
/// ```
/// I.e. a regular struct definition named `Pallet`, with generic T and no where clause.
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
/// ## Macro expansion:
///
/// The macro adds this attribute to the struct definition:
/// ```ignore
/// #[derive(
/// frame_support::CloneNoBound,
/// frame_support::EqNoBound,
/// frame_support::PartialEqNoBound,
/// frame_support::RuntimeDebugNoBound,
/// )]
/// ```
/// and replaces the type `_` with `PhantomData<T>`. It also implements on the pallet:
/// * [`GetStorageVersion`](`traits::GetStorageVersion`)
/// * [`OnGenesis`](`traits::OnGenesis`): contains some logic to write the pallet version into
/// storage.
/// * `PalletErrorTypeInfo`: provides the type information for the pallet error, if defined.
///
/// It declares `type Module` type alias for `Pallet`, used by `construct_runtime`.
///
/// It implements [`PalletInfoAccess`](`traits::PalletInfoAccess') on `Pallet` to ease access
/// to pallet information given by [`frame_support::traits::PalletInfo`]. (The implementation
/// uses the associated type `frame_system::Config::PalletInfo`).
///
/// It implements [`StorageInfoTrait`](`traits::StorageInfoTrait`) on `Pallet` which give
/// information about all storages.
///
/// If the attribute `generate_store` is set then the macro creates the trait `Store` and
/// implements it on `Pallet`.
///
/// If the attribute `set_storage_max_encoded_len` is set then the macro calls
/// [`StorageInfoTrait`](`traits::StorageInfoTrait`) for each storage in the implementation of
/// [`StorageInfoTrait`](`traits::StorageInfoTrait`) for the pallet. Otherwise it implements
/// [`StorageInfoTrait`](`traits::StorageInfoTrait`) for the pallet using the
/// [`PartialStorageInfoTrait`](`traits::PartialStorageInfoTrait`) implementation of storages.
///
/// # Config trait: `#[pallet::config]` (mandatory)
///
/// The mandatory attribute `#[pallet::config]` defines the configurable options for the
/// pallet.
///
/// Item must be defined as:
///
/// ```ignore
/// #[pallet::config]
/// pub trait Config: frame_system::Config + $optionally_some_other_supertraits
/// $optional_where_clause
/// {
/// ...
/// }
/// ```
///
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// I.e. a regular trait definition named `Config`, with the supertrait
/// `frame_system::pallet::Config`, and optionally other supertraits and a where clause.
/// (Specifying other supertraits here is known as [tight
/// coupling](https://docs.substrate.io/reference/how-to-guides/pallet-design/use-tight-coupling/))
///
/// The associated type `RuntimeEvent` is reserved. If defined, it must have the bounds
/// `From<Event>` and `IsType<<Self as frame_system::Config>::RuntimeEvent>`.
///
/// [`pallet::event`](`frame_support::pallet_macros::event`) must be present if `RuntimeEvent`
/// exists as a config item in your `#[pallet::config]`.
///
/// Also see [`pallet::config`](`frame_support::pallet_macros::config`)
///
/// ## `pallet::constant`
///
/// The `#[pallet::constant]` attribute can be used to add an associated type trait bounded by
/// [`Get`](crate::traits::Get) from [`pallet::config`](#palletconfig) into metadata, e.g.:
///
/// ```ignore
/// #[pallet::config]
/// pub trait Config: frame_system::Config {
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// #[pallet::constant]
/// type Foo: Get<u32>;
/// }
/// ```
///
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// Also see [`pallet::constant`](`frame_support::pallet_macros::constant`)
///
/// ## `pallet::disable_frame_system_supertrait_check`
/// <a name="disable_supertrait_check"></a>
///
/// To bypass the `frame_system::Config` supertrait check, use the attribute
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// `pallet::disable_frame_system_supertrait_check`, e.g.:
///
/// ```ignore
/// #[pallet::config]
/// #[pallet::disable_frame_system_supertrait_check]
/// pub trait Config: pallet_timestamp::Config {}
/// ```
///
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// NOTE: Bypassing the `frame_system::Config` supertrait check is typically desirable when you
/// want to write an alternative to the `frame_system` pallet.
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// Also see
/// [`pallet::disable_frame_system_supertrait_check`](`frame_support::pallet_macros::disable_frame_system_supertrait_check`)
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// ## Macro expansion:
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// The macro expands pallet constant metadata with the information given by
/// `#[pallet::constant]`.
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// # `pallet::generate_store($vis trait Store)`
///
/// To generate a `Store` trait associating all storages, annotate your `Pallet` struct with
/// the attribute `#[pallet::generate_store($vis trait Store)]`, e.g.:
///
/// ```ignore
/// #[pallet::pallet]
/// #[pallet::generate_store(pub(super) trait Store)]
![thiolliere thiolliere's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
thiolliere
committed
/// pub struct Pallet<T>(_);
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// More precisely, the `Store` trait contains an associated type for each storage. It is
/// implemented for `Pallet` allowing access to the storage from pallet struct.
///
/// Thus when defining a storage named `Foo`, it can later be accessed from `Pallet` using
/// `<Pallet as Store>::Foo`.
///
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// NOTE: this attribute is only valid when applied _directly_ to your `Pallet` struct
/// definition.
///
/// Also see [`pallet::generate_store`](`frame_support::pallet_macros::generate_store`).
///
/// # `pallet::storage_version`
///
/// Because the [`pallet::pallet`](#pallet-struct-placeholder-palletpallet-mandatory) macro
/// implements [`traits::GetStorageVersion`], the current storage version needs to be
/// communicated to the macro. This can be done by using the `pallet::storage_version`
/// attribute:
///
/// ```ignore
/// const STORAGE_VERSION: StorageVersion = StorageVersion::new(5);
///
/// #[pallet::pallet]
/// #[pallet::storage_version(STORAGE_VERSION)]
/// pub struct Pallet<T>(_);
/// ```
///
/// If not present, the current storage version is set to the default value.
///
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// Also see [`pallet::storage_version`](`frame_support::pallet_macros::storage_version`)
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// # Hooks: `#[pallet::hooks]` (optional)
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// The `pallet::hooks` attribute allows you to specify a `Hooks` implementation for `Pallet`
/// that specifies pallet-specific logic.
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// The item the attribute attaches to must be defined as follows:
/// ```ignore
/// #[pallet::hooks]
/// impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> $optional_where_clause {
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// ...
/// }
/// ```
/// I.e. a regular trait implementation with generic bound: `T: Config`, for the trait
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// `Hooks<BlockNumberFor<T>>` (they are defined in preludes), for the type `Pallet<T>` and
/// with an optional where clause.
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// If no `#[pallet::hooks]` exists, then the following default implementation is
/// automatically generated:
/// ```ignore
/// #[pallet::hooks]
/// impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}
/// ```
///
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// Also see [`pallet::hooks`](`frame_support::pallet_macros::hooks`)
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// # Call: `#[pallet::call]` (optional)
///
/// Implementation of pallet dispatchables.
///
/// Item must be defined as:
/// ```ignore
/// #[pallet::call]
/// impl<T: Config> Pallet<T> {
/// /// $some_doc
/// #[pallet::weight($ExpressionResultingInWeight)]
/// origin: OriginFor<T>,
/// $some_arg: $some_type,
/// // or with compact attribute: #[pallet::compact] $some_arg: $some_type,
/// ...
/// ) -> DispatchResultWithPostInfo { // or `-> DispatchResult`
/// ...
/// }
/// ...
/// }
/// ```
/// I.e. a regular type implementation, with generic `T: Config`, on type `Pallet<T>`, with
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// an optional where clause.
///
/// ## `#[pallet::weight($expr)]`
///
/// Each dispatchable needs to define a weight with `#[pallet::weight($expr)]` attribute, the
/// first argument must be `origin: OriginFor<T>`.
///
/// Also see [`pallet::weight`](`frame_support::pallet_macros::weight`)
///
/// ### `#[pallet::compact] $some_arg: $some_type`
///
/// Compact encoding for arguments can be achieved via `#[pallet::compact]`. The function must
/// return a `DispatchResultWithPostInfo` or `DispatchResult`.
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// Also see [`pallet::compact`](`frame_support::pallet_macros::compact`)
///
/// ## `#[pallet::call_index($idx)]`
/// Each dispatchable may also be annotated with the `#[pallet::call_index($idx)]` attribute,
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// which explicitly defines the codec index for the dispatchable function in the `Call` enum.
///
/// All call indexes start from 0, until it encounters a dispatchable function with a defined
/// call index. The dispatchable function that lexically follows the function with a defined
/// call index will have that call index, but incremented by 1, e.g. if there are 3
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// dispatchable functions `fn foo`, `fn bar` and `fn qux` in that order, and only `fn bar`
/// has a call index of 10, then `fn qux` will have an index of 11, instead of 1.
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// **WARNING**: modifying dispatchables, changing their order, removing some, etc., must be
/// done with care. Indeed this will change the outer runtime call type (which is an enum with
/// one variant per pallet), this outer runtime call can be stored on-chain (e.g. in
/// `pallet-scheduler`). Thus migration might be needed. To mitigate against some of this, the
/// `#[pallet::call_index($idx)]` attribute can be used to fix the order of the dispatchable so
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// that the `Call` enum encoding does not change after modification. As a general rule of
/// thumb, it is therefore adventageous to always add new calls to the end so you can maintain
/// the existing order of calls.
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// Also see [`pallet::call_index`](`frame_support::pallet_macros::call_index`)
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// # Extra constants: `#[pallet::extra_constants]` (optional)
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// Allows you to define some extra constants to be added into constant metadata.
///
/// Item must be defined as:
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
///
/// ```ignore
/// #[pallet::extra_constants]
/// impl<T: Config> Pallet<T> where $optional_where_clause {
/// /// $some_doc
/// $vis fn $fn_name() -> $some_return_type {
/// ...
/// }
/// ...
/// }
/// ```
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// I.e. a regular rust `impl` block with some optional where clause and functions with 0 args,
/// 0 generics, and some return type.
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// ## Macro expansion
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// The macro add some extra constants to pallet constant metadata.
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// Also see: [`pallet::extra_constants`](`frame_support::pallet_macros::extra_constants`)
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// # Error: `#[pallet::error]` (optional)
///
/// The `#[pallet::error]` attribute allows you to define an error enum that will be returned
/// from the dispatchable when an error occurs. The information for this error type is then
/// stored in metadata.
///
/// Item must be defined as:
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
///
/// ```ignore
/// #[pallet::error]
/// pub enum Error<T> {
/// /// $some_optional_doc
/// $SomeFieldLessVariant,
/// /// $some_more_optional_doc
/// $SomeVariantWithOneField(FieldType),
/// ...
/// }
/// ```
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// I.e. a regular enum named `Error`, with generic `T` and fieldless or multiple-field
/// variants.
///
/// Any field type in the enum variants must implement [`scale_info::TypeInfo`] in order to be
/// properly used in the metadata, and its encoded size should be as small as possible,
/// preferably 1 byte in size in order to reduce storage size. The error enum itself has an
/// absolute maximum encoded size specified by [`MAX_MODULE_ERROR_ENCODED_SIZE`].
///
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// (1 byte can still be 256 different errors. The more specific the error, the easier it is to
/// diagnose problems and give a better experience to the user. Don't skimp on having lots of
/// individual error conditions.)
///
/// Field types in enum variants must also implement [`PalletError`](traits::PalletError),
/// otherwise the pallet will fail to compile. Rust primitive types have already implemented
/// the [`PalletError`](traits::PalletError) trait along with some commonly used stdlib types
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// such as [`Option`] and [`PhantomData`](`frame_support::dispatch::marker::PhantomData`), and
/// hence in most use cases, a manual implementation is not necessary and is discouraged.
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// The generic `T` must not bound anything and a `where` clause is not allowed. That said,
/// bounds and/or a where clause should not needed for any use-case.
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// Also see: [`pallet::error`](`frame_support::pallet_macros::error`)
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// # Event: `#[pallet::event]` (optional)
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// Allows you to define pallet events. Pallet events are stored under the `system` / `events`
/// key when the block is applied (and then replaced when the next block writes it's events).
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
/// The Event enum must be defined as follows:
///
/// ```ignore
/// #[pallet::event]
/// #[pallet::generate_deposit($visibility fn deposit_event)] // Optional