|
14 | 14 | //! > FRAME-based runtimes use various techniques to re-use a currency pallet instead of writing
|
15 | 15 | //! > one. Further advanced FRAME related topics are discussed in [`crate::reference_docs`].
|
16 | 16 | //!
|
17 |
| -//! ## Topics Covered |
| 17 | +//! ## Writing Your First Pallet |
18 | 18 | //!
|
19 |
| -//! The following FRAME topics are covered in this guide: |
| 19 | +//! To get started, use one of the templates mentioned in [`crate::polkadot_sdk::templates`]. We |
| 20 | +//! recommend using the `polkadot-sdk-minimal-template`. You might need to change small parts of |
| 21 | +//! this guide, namely the crate/package names, based on which tutorial you use. |
20 | 22 | //!
|
21 |
| -//! - [Storage](frame::pallet_macros::storage) |
22 |
| -//! - [Call](frame::pallet_macros::call) |
23 |
| -//! - [Event](frame::pallet_macros::event) |
24 |
| -//! - [Error](frame::pallet_macros::error) |
25 |
| -//! - Basics of testing a pallet |
26 |
| -//! - [Constructing a runtime](frame::runtime::prelude::construct_runtime) |
27 |
| -//! |
28 |
| -//! ## Writing Your First Pallet |
| 23 | +//! > Be aware that you can read the entire source code backing this tutorial by clicking on the |
| 24 | +//! > [`source`](./mod.rs.html) button at the top right of the page. |
29 | 25 | //!
|
30 | 26 | //! You should have studied the following modules as a prelude to this guide:
|
31 | 27 | //!
|
32 | 28 | //! - [`crate::reference_docs::blockchain_state_machines`]
|
33 | 29 | //! - [`crate::reference_docs::trait_based_programming`]
|
34 | 30 | //! - [`crate::polkadot_sdk::frame_runtime`]
|
35 | 31 | //!
|
| 32 | +//! ## Topics Covered |
| 33 | +//! |
| 34 | +//! The following FRAME topics are covered in this guide: |
| 35 | +//! |
| 36 | +//! - [`pallet::storage`] |
| 37 | +//! - [`pallet::call`] |
| 38 | +//! - [`pallet::event`] |
| 39 | +//! - [`pallet::error`] |
| 40 | +//! - Basics of testing a pallet |
| 41 | +//! - [Constructing a runtime](frame::runtime::prelude::construct_runtime) |
| 42 | +//! |
36 | 43 | //! ### Shell Pallet
|
37 | 44 | //!
|
38 | 45 | //! Consider the following as a "shell pallet". We continue building the rest of this pallet based
|
39 | 46 | //! on this template.
|
40 | 47 | //!
|
41 |
| -//! [`pallet::config`](frame::pallet_macros::config) and |
42 |
| -//! [`pallet::pallet`](frame::pallet_macros::pallet) are both mandatory parts of any pallet. Refer |
43 |
| -//! to the documentation of each to get an overview of what they do. |
| 48 | +//! [`pallet::config`] and [`pallet::pallet`] are both mandatory parts of any pallet. Refer to the |
| 49 | +//! documentation of each to get an overview of what they do. |
44 | 50 | #![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", shell_pallet)]
|
45 | 51 | //!
|
| 52 | +//! All of the code that follows in this guide should live inside of the `mod pallet`. |
| 53 | +//! |
46 | 54 | //! ### Storage
|
47 | 55 | //!
|
48 | 56 | //! First, we will need to create two onchain storage declarations.
|
|
55 | 63 | //! > generic bounded type in the `Config` trait, and then specify it in the implementation.
|
56 | 64 | #![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", Balance)]
|
57 | 65 | //!
|
58 |
| -//! The definition of these two storage items, based on [`frame::pallet_macros::storage`] details, |
59 |
| -//! is as follows: |
| 66 | +//! The definition of these two storage items, based on [`pallet::storage`] details, is as follows: |
60 | 67 | #![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", TotalIssuance)]
|
61 | 68 | #![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", Balances)]
|
62 | 69 | //!
|
63 | 70 | //! ### Dispatchables
|
64 | 71 | //!
|
65 |
| -//! Next, we will define the dispatchable functions. As per [`frame::pallet_macros::call`], these |
66 |
| -//! will be defined as normal `fn`s attached to `struct Pallet`. |
| 72 | +//! Next, we will define the dispatchable functions. As per [`pallet::call`], these will be defined |
| 73 | +//! as normal `fn`s attached to `struct Pallet`. |
67 | 74 | #![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", impl_pallet)]
|
68 | 75 | //!
|
69 | 76 | //! The logic of the functions is self-explanatory. Instead, we will focus on the FRAME-related
|
|
79 | 86 | //! was signed by `who`.
|
80 | 87 | #![doc = docify::embed!("../../substrate/frame/system/src/lib.rs", ensure_signed)]
|
81 | 88 | //!
|
82 |
| -//! |
83 | 89 | //! - Where does `mutate`, `get` and `insert` and other storage APIs come from? All of them are
|
84 | 90 | //! explained in the corresponding `type`, for example, for `Balances::<T>::insert`, you can look
|
85 | 91 | //! into [`frame::prelude::StorageMap::insert`].
|
|
95 | 101 | //!
|
96 | 102 | //! - Why are all `get` and `mutate` functions returning an `Option`? This is the default behavior
|
97 | 103 | //! of FRAME storage APIs. You can learn more about how to override this by looking into
|
98 |
| -//! [`frame::pallet_macros::storage`], and |
99 |
| -//! [`frame::prelude::ValueQuery`]/[`frame::prelude::OptionQuery`] |
| 104 | +//! [`pallet::storage`], and [`frame::prelude::ValueQuery`]/[`frame::prelude::OptionQuery`] |
100 | 105 | //!
|
101 | 106 | //! ### Improving Errors
|
102 | 107 | //!
|
|
116 | 121 | //!
|
117 | 122 | //! ### Your First (Test) Runtime
|
118 | 123 | //!
|
| 124 | +//! The typical testing code of a pallet lives in a module that imports some preludes useful for |
| 125 | +//! testing, similar to: |
| 126 | +//! |
| 127 | +//! ``` |
| 128 | +//! pub mod pallet { |
| 129 | +//! // snip -- actually pallet code. |
| 130 | +//! } |
| 131 | +//! |
| 132 | +//! #[cfg(test)] |
| 133 | +//! mod tests { |
| 134 | +//! // bring in the testing prelude of frame |
| 135 | +//! use frame::testing_prelude::*; |
| 136 | +//! // bring in all pallet items |
| 137 | +//! use super::pallet::*; |
| 138 | +//! |
| 139 | +//! // snip -- rest of the testing code. |
| 140 | +//! } |
| 141 | +//! ``` |
| 142 | +//! |
119 | 143 | //! Next, we create a "test runtime" in order to test our pallet. Recall from
|
120 | 144 | //! [`crate::polkadot_sdk::frame_runtime`] that a runtime is a collection of pallets, expressed
|
121 | 145 | //! through [`frame::runtime::prelude::construct_runtime`]. All runtimes also have to include
|
|
166 | 190 | //! As noted above, the `T::AccountId` is now `u64`. Moreover, `Runtime` is replacing `<T: Config>`.
|
167 | 191 | //! This is why for example you see `Balances::<Runtime>::get(..)`. Finally, notice that the
|
168 | 192 | //! dispatchables are simply functions that can be called on top of the `Pallet` struct.
|
169 |
| -// TODO: hard to explain exactly `RuntimeOrigin::signed(ALICE)` at this point. |
170 | 193 | //!
|
171 | 194 | //! Congratulations! You have written your first pallet and tested it! Next, we learn a few optional
|
172 | 195 | //! steps to improve our pallet.
|
|
236 | 259 | //! by one character. FRAME errors are exactly a solution to maintain readability, whilst fixing
|
237 | 260 | //! the drawbacks mentioned. In short, we use an enum to represent different variants of our
|
238 | 261 | //! error. These variants are then mapped in an efficient way (using only `u8` indices) to
|
239 |
| -//! [`sp_runtime::DispatchError::Module`]. Read more about this in |
240 |
| -//! [`frame::pallet_macros::error`]. |
| 262 | +//! [`sp_runtime::DispatchError::Module`]. Read more about this in [`pallet::error`]. |
241 | 263 | //!
|
242 | 264 | //! - **Event**: Events are akin to the return type of dispatchables. They are mostly data blobs
|
243 | 265 | //! emitted by the runtime to let outside world know what is happening inside the pallet. Since
|
|
246 | 268 | //! use passive tense for event names (eg. `SomethingHappened`). This allows other sub-systems or
|
247 | 269 | //! external parties (eg. a light-node, a DApp) to listen to particular events happening, without
|
248 | 270 | //! needing to re-execute the whole state transition function.
|
249 |
| -// TODO: both need to be improved a lot at the pallet-macro rust-doc level. Also my explanation |
250 |
| -// of event is probably not the best. |
251 | 271 | //!
|
252 | 272 | //! With the explanation out of the way, let's see how these components can be added. Both follow a
|
253 |
| -//! fairly familiar syntax: normal Rust enums, with extra |
254 |
| -//! [`#[frame::event]`](frame::pallet_macros::event) and |
255 |
| -//! [`#[frame::error]`](frame::pallet_macros::error) attributes attached. |
| 273 | +//! fairly familiar syntax: normal Rust enums, with extra [`pallet::event`] and [`pallet::error`] |
| 274 | +//! attributes attached. |
256 | 275 | #![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", Event)]
|
257 | 276 | #![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", Error)]
|
258 | 277 | //!
|
259 |
| -//! One slightly custom part of this is the [`#[pallet::generate_deposit(pub(super) fn |
260 |
| -//! deposit_event)]`](frame::pallet_macros::generate_deposit) part. Without going into too |
261 |
| -//! much detail, in order for a pallet to emit events to the rest of the system, it needs to do two |
262 |
| -//! things: |
| 278 | +//! One slightly custom part of this is the [`pallet::generate_deposit`] part. Without going into |
| 279 | +//! too much detail, in order for a pallet to emit events to the rest of the system, it needs to do |
| 280 | +//! two things: |
263 | 281 | //!
|
264 | 282 | //! 1. Declare a type in its `Config` that refers to the overarching event type of the runtime. In
|
265 | 283 | //! short, by doing this, the pallet is expressing an important bound: `type RuntimeEvent:
|
|
268 | 286 | //! store it where needed.
|
269 | 287 | //!
|
270 | 288 | //! 2. But, doing this conversion and storing is too much to expect each pallet to define. FRAME
|
271 |
| -//! provides a default way of storing events, and this is what |
272 |
| -//! [`pallet::generate_deposit`](frame::pallet_macros::generate_deposit) is doing. |
| 289 | +//! provides a default way of storing events, and this is what [`pallet::generate_deposit`] is |
| 290 | +//! doing. |
273 | 291 | #![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", config_v2)]
|
274 | 292 | //!
|
275 | 293 | //! > These `Runtime*` types are better explained in
|
|
297 | 315 | //! - [`crate::reference_docs::defensive_programming`].
|
298 | 316 | //! - [`crate::reference_docs::frame_origin`].
|
299 | 317 | //! - [`crate::reference_docs::frame_runtime_types`].
|
300 |
| -//! - The pallet we wrote in this guide was using `dev_mode`, learn more in |
301 |
| -//! [`frame::pallet_macros::config`]. |
| 318 | +//! - The pallet we wrote in this guide was using `dev_mode`, learn more in [`pallet::config`]. |
302 | 319 | //! - Learn more about the individual pallet items/macros, such as event and errors and call, in
|
303 | 320 | //! [`frame::pallet_macros`].
|
| 321 | +//! |
| 322 | +//! [`pallet::storage`]: ../../../frame_support/pallet_macros/attr.config.html |
| 323 | +//! [`pallet::call`]: ../../../frame_support/pallet_macros/attr.call.html |
| 324 | +//! [`pallet::event`]: ../../../frame_support/pallet_macros/attr.event.html |
| 325 | +//! [`pallet::error`]: ../../../frame_support/pallet_macros/attr.error.html |
| 326 | +//! [`pallet::pallet`]: ../../../frame_support/pallet_macros/attr.pallet.html |
| 327 | +//! [`pallet::config`]: ../../../frame_support/pallet_macros/attr.config.html |
| 328 | +//! [`pallet::generate_deposit`]: ../../../frame_support/pallet_macros/attr.generate_deposit.html |
304 | 329 |
|
305 | 330 | #[docify::export]
|
306 | 331 | #[frame::pallet(dev_mode)]
|
@@ -418,16 +443,22 @@ pub mod pallet {
|
418 | 443 | #[cfg(any(test, doc))]
|
419 | 444 | pub(crate) mod tests {
|
420 | 445 | use crate::guides::your_first_pallet::pallet::*;
|
| 446 | + |
| 447 | + #[docify::export(testing_prelude)] |
421 | 448 | use frame::testing_prelude::*;
|
422 |
| - const ALICE: u64 = 1; |
423 |
| - const BOB: u64 = 2; |
424 |
| - const CHARLIE: u64 = 3; |
| 449 | + |
| 450 | + pub(crate) const ALICE: u64 = 1; |
| 451 | + pub(crate) const BOB: u64 = 2; |
| 452 | + pub(crate) const CHARLIE: u64 = 3; |
425 | 453 |
|
426 | 454 | #[docify::export]
|
| 455 | + // This runtime is only used for testing, so it should be somewhere like `#[cfg(test)] mod |
| 456 | + // tests { .. }` |
427 | 457 | mod runtime {
|
428 | 458 | use super::*;
|
429 | 459 | // we need to reference our `mod pallet` as an identifier to pass to
|
430 | 460 | // `construct_runtime`.
|
| 461 | + // YOU HAVE TO CHANGE THIS LINE BASED ON YOUR TEMPLATE |
431 | 462 | use crate::guides::your_first_pallet::pallet as pallet_currency;
|
432 | 463 |
|
433 | 464 | construct_runtime!(
|
|
0 commit comments