polkadot_sdk_docs/guides/your_first_pallet/mod.rs
1//! # Currency Pallet
2//!
3//! By the end of this guide, you will have written a small FRAME pallet (see
4//! [`crate::polkadot_sdk::frame_runtime`]) that is capable of handling a simple crypto-currency.
5//! This pallet will:
6//!
7//! 1. Allow anyone to mint new tokens into accounts (which is obviously not a great idea for a real
8//! system).
9//! 2. Allow any user that owns tokens to transfer them to others.
10//! 3. Track the total issuance of all tokens at all times.
11//!
12//! > This guide will build a currency pallet from scratch using only the lowest primitives of
13//! > FRAME, and is mainly intended for education, not *applicability*. For example, almost all
14//! > FRAME-based runtimes use various techniques to re-use a currency pallet instead of writing
15//! > one. Further advanced FRAME related topics are discussed in [`crate::reference_docs`].
16//!
17//! ## Writing Your First Pallet
18//!
19//! To get started, clone 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 template you use.
22//!
23//! > Be aware that you can read the entire source code backing this tutorial by clicking on the
24//! > `source` button at the top right of the page.
25//!
26//! You should have studied the following modules as a prelude to this guide:
27//!
28//! - [`crate::reference_docs::blockchain_state_machines`]
29//! - [`crate::reference_docs::trait_based_programming`]
30//! - [`crate::polkadot_sdk::frame_runtime`]
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//!
43//! ### Shell Pallet
44//!
45//! Consider the following as a "shell pallet". We continue building the rest of this pallet based
46//! on this template.
47//!
48//! [`pallet::config`] and [`pallet::pallet`] are both mandatory parts of any
49//! pallet. Refer to the documentation of each to get an overview of what they do.
50#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", shell_pallet)]
51//!
52//! All of the code that follows in this guide should live inside of the `mod pallet`.
53//!
54//! ### Storage
55//!
56//! First, we will need to create two onchain storage declarations.
57//!
58//! One should be a mapping from account-ids to a balance type, and one value that is the total
59//! issuance.
60//!
61//! > For the rest of this guide, we will opt for a balance type of `u128`. For the sake of
62//! > simplicity, we are hardcoding this type. In a real pallet is best practice to define it as a
63//! > generic bounded type in the `Config` trait, and then specify it in the implementation.
64#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", Balance)]
65//!
66//! The definition of these two storage items, based on [`pallet::storage`] details, is as follows:
67#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", TotalIssuance)]
68#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", Balances)]
69//!
70//! ### Dispatchables
71//!
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`.
74#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", impl_pallet)]
75//!
76//! The logic of these functions is self-explanatory. Instead, we will focus on the FRAME-related
77//! details:
78//!
79//! - Where do `T::AccountId` and `T::RuntimeOrigin` come from? These are both defined in
80//! [`frame::prelude::frame_system::Config`], therefore we can access them in `T`.
81//! - What is `ensure_signed`, and what does it do with the aforementioned `T::RuntimeOrigin`? This
82//! is outside the scope of this guide, and you can learn more about it in the origin reference
83//! document ([`crate::reference_docs::frame_origin`]). For now, you should only know the
84//! signature of the function: it takes a generic `T::RuntimeOrigin` and returns a
85//! `Result<T::AccountId, _>`. So by the end of this function call, we know that this dispatchable
86//! was signed by `sender`.
87#![doc = docify::embed!("../../substrate/frame/system/src/lib.rs", ensure_signed)]
88//!
89//! - Where does `mutate`, `get` and `insert` and other storage APIs come from? All of them are
90//! explained in the corresponding `type`, for example, for `Balances::<T>::insert`, you can look
91//! into [`frame::prelude::StorageMap::insert`].
92//!
93//! - The return type of all dispatchable functions is [`frame::prelude::DispatchResult`]:
94#![doc = docify::embed!("../../substrate/frame/support/src/dispatch.rs", DispatchResult)]
95//!
96//! Which is more or less a normal Rust `Result`, with a custom [`frame::prelude::DispatchError`] as
97//! the `Err` variant. We won't cover this error in detail here, but importantly you should know
98//! that there is an `impl From<&'static string> for DispatchError` provided (see
99//! [here](`frame::prelude::DispatchError#impl-From<%26str>-for-DispatchError`)). Therefore,
100//! we can use basic string literals as our error type and `.into()` them into `DispatchError`.
101//!
102//! - Why are all `get` and `mutate` functions returning an `Option`? This is the default behavior
103//! of FRAME storage APIs. You can learn more about how to override this by looking into
104//! [`pallet::storage`], and [`frame::prelude::ValueQuery`]/[`frame::prelude::OptionQuery`]
105//!
106//! ### Improving Errors
107//!
108//! How we handle error in the above snippets is fairly rudimentary. Let's look at how this can be
109//! improved. First, we can use [`frame::prelude::ensure`] to express the error slightly better.
110//! This macro will call `.into()` under the hood.
111#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", transfer_better)]
112//!
113//! Moreover, you will learn in the [Defensive Programming
114//! section](crate::reference_docs::defensive_programming) that it is always recommended to use
115//! safe arithmetic operations in your runtime. By using [`frame::traits::CheckedSub`], we can not
116//! only take a step in that direction, but also improve the error handing and make it slightly more
117//! ergonomic.
118#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", transfer_better_checked)]
119//!
120//! This is more or less all the logic that there is in this basic currency pallet!
121//!
122//! ### Your First (Test) Runtime
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//!
143//! Next, we create a "test runtime" in order to test our pallet. Recall from
144//! [`crate::polkadot_sdk::frame_runtime`] that a runtime is a collection of pallets, expressed
145//! through [`frame::runtime::prelude::construct_runtime`]. All runtimes also have to include
146//! [`frame::prelude::frame_system`]. So we expect to see a runtime with two pallet, `frame_system`
147//! and the one we just wrote.
148#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", runtime)]
149//!
150//! > [`frame::pallet_macros::derive_impl`] is a FRAME feature that enables developers to have
151//! > defaults for associated types.
152//!
153//! Recall that within our pallet, (almost) all blocks of code are generic over `<T: Config>`. And,
154//! because `trait Config: frame_system::Config`, we can get access to all items in `Config` (or
155//! `frame_system::Config`) using `T::NameOfItem`. This is all within the boundaries of how
156//! Rust traits and generics work. If unfamiliar with this pattern, read
157//! [`crate::reference_docs::trait_based_programming`] before going further.
158//!
159//! Crucially, a typical FRAME runtime contains a `struct Runtime`. The main role of this `struct`
160//! is to implement the `trait Config` of all pallets. That is, anywhere within your pallet code
161//! where you see `<T: Config>` (read: *"some type `T` that implements `Config`"*), in the runtime,
162//! it can be replaced with `<Runtime>`, because `Runtime` implements `Config` of all pallets, as we
163//! see above.
164//!
165//! Another way to think about this is that within a pallet, a lot of types are "unknown" and, we
166//! only know that they will be provided at some later point. For example, when you write
167//! `T::AccountId` (which is short for `<T as frame_system::Config>::AccountId`) in your pallet,
168//! you are in fact saying "*Some type `AccountId` that will be known later*". That "later" is in
169//! fact when you specify these types when you implement all `Config` traits for `Runtime`.
170//!
171//! As you see above, `frame_system::Config` is setting the `AccountId` to `u64`. Of course, a real
172//! runtime will not use this type, and instead reside to a proper type like a 32-byte standard
173//! public key. This is a HUGE benefit that FRAME developers can tap into: through the framework
174//! being so generic, different types can always be customized to simple things when needed.
175//!
176//! > Imagine how hard it would have been if all tests had to use a real 32-byte account id, as
177//! > opposed to just a u64 number ๐.
178//!
179//! ### Your First Test
180//!
181//! The above is all you need to execute the dispatchables of your pallet. The last thing you need
182//! to learn is that all of your pallet testing code should be wrapped in
183//! [`frame::testing_prelude::TestState`]. This is a type that provides access to an in-memory state
184//! to be used in our tests.
185#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", first_test)]
186//!
187//! In the first test, we simply assert that there is no total issuance, and no balance associated
188//! with Alice's account. Then, we mint some balance into Alice's, and re-check.
189//!
190//! As noted above, the `T::AccountId` is now `u64`. Moreover, `Runtime` is replacing `<T: Config>`.
191//! This is why for example you see `Balances::<Runtime>::get(..)`. Finally, notice that the
192//! dispatchables are simply functions that can be called on top of the `Pallet` struct.
193//!
194//! Congratulations! You have written your first pallet and tested it! Next, we learn a few optional
195//! steps to improve our pallet.
196//!
197//! ## Improving the Currency Pallet
198//!
199//! ### Better Test Setup
200//!
201//! Idiomatic FRAME pallets often use Builder pattern to define their initial state.
202//!
203//! > The Polkadot Blockchain Academy's Rust entrance exam has a
204//! > [section](https://github.com/Polkadot-Blockchain-Academy/pba-qualifier-exam/blob/main/src/m_builder.rs)
205//! > on this that you can use to learn the Builder Pattern.
206//!
207//! Let's see how we can implement a better test setup using this pattern. First, we define a
208//! `struct StateBuilder`.
209#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", StateBuilder)]
210//!
211//! This struct is meant to contain the same list of accounts and balances that we want to have at
212//! the beginning of each block. We hardcoded this to `let accounts = vec![(ALICE, 100), (2, 100)];`
213//! so far. Then, if desired, we attach a default value for this struct.
214#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", default_state_builder)]
215//!
216//! Like any other builder pattern, we attach functions to the type to mutate its internal
217//! properties.
218#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", impl_state_builder_add)]
219//!
220//! Finally --the useful part-- we write our own custom `build_and_execute` function on
221//! this type. This function will do multiple things:
222//!
223//! 1. It would consume `self` to produce our `TestState` based on the properties that we attached
224//! to `self`.
225//! 2. It would execute any test function that we pass in as closure.
226//! 3. A nifty trick, this allows our test setup to have some code that is executed both before and
227//! after each test. For example, in this test, we do some additional checking about the
228//! correctness of the `TotalIssuance`. We leave it up to you as an exercise to learn why the
229//! assertion should always hold, and how it is checked.
230#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", impl_state_builder_build)]
231//!
232//! We can write tests that specifically check the initial state, and making sure our `StateBuilder`
233//! is working exactly as intended.
234#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", state_builder_works)]
235#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", state_builder_add_balance)]
236//!
237//! ### More Tests
238//!
239//! Now that we have a more ergonomic test setup, let's see how a well written test for transfer and
240//! mint would look like.
241#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", transfer_works)]
242#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", mint_works)]
243//!
244//! It is always a good idea to build a mental model where you write *at least* one test for each
245//! "success path" of a dispatchable, and one test for each "failure path", such as:
246#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", transfer_from_non_existent_fails)]
247//!
248//! We leave it up to you to write a test that triggers the `InsufficientBalance` error.
249//!
250//! ### Event and Error
251//!
252//! Our pallet is mainly missing two parts that are common in most FRAME pallets: Events, and
253//! Errors. First, let's understand what each is.
254//!
255//! - **Error**: The static string-based error scheme we used so far is good for readability, but it
256//! has a few drawbacks. The biggest problem with strings are that they are not type safe, e.g. a
257//! match statement cannot be exhaustive. These string literals will bloat the final wasm blob,
258//! and are relatively heavy to transmit and encode/decode. Moreover, it is easy to mistype them
259//! by one character. FRAME errors are exactly a solution to maintain readability, whilst fixing
260//! the drawbacks mentioned. In short, we use an enum to represent different variants of our
261//! error. These variants are then mapped in an efficient way (using only `u8` indices) to
262//! [`sp_runtime::DispatchError::Module`]. Read more about this in [`pallet::error`].
263//!
264//! - **Event**: Events are akin to the return type of dispatchables. They are mostly data blobs
265//! emitted by the runtime to let outside world know what is happening inside the pallet. Since
266//! otherwise, the outside world does not have an easy access to the state changes. They should
267//! represent what happened at the end of a dispatch operation. Therefore, the convention is to
268//! use passive tense for event names (eg. `SomethingHappened`). This allows other sub-systems or
269//! external parties (eg. a light-node, a DApp) to listen to particular events happening, without
270//! needing to re-execute the whole state transition function.
271//!
272//! With the explanation out of the way, let's see how these components can be added. Both follow a
273//! fairly familiar syntax: normal Rust enums, with extra [`pallet::event`] and [`pallet::error`]
274//! attributes attached.
275#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", Event)]
276#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", Error)]
277//!
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:
281//!
282//! 1. Declare a type in its `Config` that refers to the overarching event type of the runtime. In
283//! short, by doing this, the pallet is expressing an important bound: `type RuntimeEvent:
284//! From<Event<Self>>`. Read: a `RuntimeEvent` exists, and it can be created from the local `enum
285//! Event` of this pallet. This enables the pallet to convert its `Event` into `RuntimeEvent`, and
286//! store it where needed.
287//!
288//! 2. But, doing this conversion and storing is too much to expect each pallet to define. FRAME
289//! provides a default way of storing events, and this is what [`pallet::generate_deposit`] is
290//! doing.
291#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", config_v2)]
292//!
293//! > These `Runtime*` types are better explained in
294//! > [`crate::reference_docs::frame_runtime_types`].
295//!
296//! Then, we can rewrite the `transfer` dispatchable as such:
297#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", transfer_v2)]
298//!
299//! Then, notice how now we would need to provide this `type RuntimeEvent` in our test runtime
300//! setup.
301#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", runtime_v2)]
302//!
303//! In this snippet, the actual `RuntimeEvent` type (right hand side of `type RuntimeEvent =
304//! RuntimeEvent`) is generated by
305//! [`construct_runtime`](frame::runtime::prelude::construct_runtime). An interesting way to inspect
306//! this type is to see its definition in rust-docs:
307//! [`crate::guides::your_first_pallet::pallet_v2::tests::runtime_v2::RuntimeEvent`].
308//!
309//!
310//! ## What Next?
311//!
312//! The following topics where used in this guide, but not covered in depth. It is suggested to
313//! study them subsequently:
314//!
315//! - [`crate::reference_docs::defensive_programming`].
316//! - [`crate::reference_docs::frame_origin`].
317//! - [`crate::reference_docs::frame_runtime_types`].
318//! - The pallet we wrote in this guide was using `dev_mode`, learn more in [`pallet::config`].
319//! - Learn more about the individual pallet items/macros, such as event and errors and call, in
320//! [`frame::pallet_macros`].
321//!
322//! [`pallet::storage`]: frame_support::pallet_macros::storage
323//! [`pallet::call`]: frame_support::pallet_macros::call
324//! [`pallet::event`]: frame_support::pallet_macros::event
325//! [`pallet::error`]: frame_support::pallet_macros::error
326//! [`pallet::pallet`]: frame_support::pallet
327//! [`pallet::config`]: frame_support::pallet_macros::config
328//! [`pallet::generate_deposit`]: frame_support::pallet_macros::generate_deposit
329
330#[docify::export]
331#[frame::pallet(dev_mode)]
332pub mod shell_pallet {
333 use frame::prelude::*;
334
335 #[pallet::config]
336 pub trait Config: frame_system::Config {}
337
338 #[pallet::pallet]
339 pub struct Pallet<T>(_);
340}
341
342#[frame::pallet(dev_mode)]
343pub mod pallet {
344 use frame::prelude::*;
345
346 #[docify::export]
347 pub type Balance = u128;
348
349 #[pallet::config]
350 pub trait Config: frame_system::Config {}
351
352 #[pallet::pallet]
353 pub struct Pallet<T>(_);
354
355 #[docify::export]
356 /// Single storage item, of type `Balance`.
357 #[pallet::storage]
358 pub type TotalIssuance<T: Config> = StorageValue<_, Balance>;
359
360 #[docify::export]
361 /// A mapping from `T::AccountId` to `Balance`
362 #[pallet::storage]
363 pub type Balances<T: Config> = StorageMap<_, _, T::AccountId, Balance>;
364
365 #[docify::export(impl_pallet)]
366 #[pallet::call]
367 impl<T: Config> Pallet<T> {
368 /// An unsafe mint that can be called by anyone. Not a great idea.
369 pub fn mint_unsafe(
370 origin: T::RuntimeOrigin,
371 dest: T::AccountId,
372 amount: Balance,
373 ) -> DispatchResult {
374 // ensure that this is a signed account, but we don't really check `_anyone`.
375 let _anyone = ensure_signed(origin)?;
376
377 // update the balances map. Notice how all `<T: Config>` remains as `<T>`.
378 Balances::<T>::mutate(dest, |b| *b = Some(b.unwrap_or(0) + amount));
379 // update total issuance.
380 TotalIssuance::<T>::mutate(|t| *t = Some(t.unwrap_or(0) + amount));
381
382 Ok(())
383 }
384
385 /// Transfer `amount` from `origin` to `dest`.
386 pub fn transfer(
387 origin: T::RuntimeOrigin,
388 dest: T::AccountId,
389 amount: Balance,
390 ) -> DispatchResult {
391 let sender = ensure_signed(origin)?;
392
393 // ensure sender has enough balance, and if so, calculate what is left after `amount`.
394 let sender_balance = Balances::<T>::get(&sender).ok_or("NonExistentAccount")?;
395 if sender_balance < amount {
396 return Err("InsufficientBalance".into())
397 }
398 let remainder = sender_balance - amount;
399
400 // update sender and dest balances.
401 Balances::<T>::mutate(dest, |b| *b = Some(b.unwrap_or(0) + amount));
402 Balances::<T>::insert(&sender, remainder);
403
404 Ok(())
405 }
406 }
407
408 #[allow(unused)]
409 impl<T: Config> Pallet<T> {
410 #[docify::export]
411 pub fn transfer_better(
412 origin: T::RuntimeOrigin,
413 dest: T::AccountId,
414 amount: Balance,
415 ) -> DispatchResult {
416 let sender = ensure_signed(origin)?;
417
418 let sender_balance = Balances::<T>::get(&sender).ok_or("NonExistentAccount")?;
419 ensure!(sender_balance >= amount, "InsufficientBalance");
420 let remainder = sender_balance - amount;
421
422 // .. snip
423 Ok(())
424 }
425
426 #[docify::export]
427 /// Transfer `amount` from `origin` to `dest`.
428 pub fn transfer_better_checked(
429 origin: T::RuntimeOrigin,
430 dest: T::AccountId,
431 amount: Balance,
432 ) -> DispatchResult {
433 let sender = ensure_signed(origin)?;
434
435 let sender_balance = Balances::<T>::get(&sender).ok_or("NonExistentAccount")?;
436 let remainder = sender_balance.checked_sub(amount).ok_or("InsufficientBalance")?;
437
438 // .. snip
439 Ok(())
440 }
441 }
442
443 #[cfg(any(test, doc))]
444 pub(crate) mod tests {
445 use crate::guides::your_first_pallet::pallet::*;
446
447 #[docify::export(testing_prelude)]
448 use frame::testing_prelude::*;
449
450 pub(crate) const ALICE: u64 = 1;
451 pub(crate) const BOB: u64 = 2;
452 pub(crate) const CHARLIE: u64 = 3;
453
454 #[docify::export]
455 // This runtime is only used for testing, so it should be somewhere like `#[cfg(test)] mod
456 // tests { .. }`
457 mod runtime {
458 use super::*;
459 // we need to reference our `mod pallet` as an identifier to pass to
460 // `construct_runtime`.
461 // YOU HAVE TO CHANGE THIS LINE BASED ON YOUR TEMPLATE
462 use crate::guides::your_first_pallet::pallet as pallet_currency;
463
464 construct_runtime!(
465 pub enum Runtime {
466 // ---^^^^^^ This is where `enum Runtime` is defined.
467 System: frame_system,
468 Currency: pallet_currency,
469 }
470 );
471
472 #[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
473 impl frame_system::Config for Runtime {
474 type Block = MockBlock<Runtime>;
475 // within pallet we just said `<T as frame_system::Config>::AccountId`, now we
476 // finally specified it.
477 type AccountId = u64;
478 }
479
480 // our simple pallet has nothing to be configured.
481 impl pallet_currency::Config for Runtime {}
482 }
483
484 pub(crate) use runtime::*;
485
486 #[allow(unused)]
487 #[docify::export]
488 fn new_test_state_basic() -> TestState {
489 let mut state = TestState::new_empty();
490 let accounts = vec![(ALICE, 100), (BOB, 100)];
491 state.execute_with(|| {
492 for (who, amount) in &accounts {
493 Balances::<Runtime>::insert(who, amount);
494 TotalIssuance::<Runtime>::mutate(|b| *b = Some(b.unwrap_or(0) + amount));
495 }
496 });
497
498 state
499 }
500
501 #[docify::export]
502 pub(crate) struct StateBuilder {
503 balances: Vec<(<Runtime as frame_system::Config>::AccountId, Balance)>,
504 }
505
506 #[docify::export(default_state_builder)]
507 impl Default for StateBuilder {
508 fn default() -> Self {
509 Self { balances: vec![(ALICE, 100), (BOB, 100)] }
510 }
511 }
512
513 #[docify::export(impl_state_builder_add)]
514 impl StateBuilder {
515 fn add_balance(
516 mut self,
517 who: <Runtime as frame_system::Config>::AccountId,
518 amount: Balance,
519 ) -> Self {
520 self.balances.push((who, amount));
521 self
522 }
523 }
524
525 #[docify::export(impl_state_builder_build)]
526 impl StateBuilder {
527 pub(crate) fn build_and_execute(self, test: impl FnOnce() -> ()) {
528 let mut ext = TestState::new_empty();
529 ext.execute_with(|| {
530 for (who, amount) in &self.balances {
531 Balances::<Runtime>::insert(who, amount);
532 TotalIssuance::<Runtime>::mutate(|b| *b = Some(b.unwrap_or(0) + amount));
533 }
534 });
535
536 ext.execute_with(test);
537
538 // assertions that must always hold
539 ext.execute_with(|| {
540 assert_eq!(
541 Balances::<Runtime>::iter().map(|(_, x)| x).sum::<u128>(),
542 TotalIssuance::<Runtime>::get().unwrap_or_default()
543 );
544 })
545 }
546 }
547
548 #[docify::export]
549 #[test]
550 fn first_test() {
551 TestState::new_empty().execute_with(|| {
552 // We expect Alice's account to have no funds.
553 assert_eq!(Balances::<Runtime>::get(&ALICE), None);
554 assert_eq!(TotalIssuance::<Runtime>::get(), None);
555
556 // mint some funds into Alice's account.
557 assert_ok!(Pallet::<Runtime>::mint_unsafe(
558 RuntimeOrigin::signed(ALICE),
559 ALICE,
560 100
561 ));
562
563 // re-check the above
564 assert_eq!(Balances::<Runtime>::get(&ALICE), Some(100));
565 assert_eq!(TotalIssuance::<Runtime>::get(), Some(100));
566 })
567 }
568
569 #[docify::export]
570 #[test]
571 fn state_builder_works() {
572 StateBuilder::default().build_and_execute(|| {
573 assert_eq!(Balances::<Runtime>::get(&ALICE), Some(100));
574 assert_eq!(Balances::<Runtime>::get(&BOB), Some(100));
575 assert_eq!(Balances::<Runtime>::get(&CHARLIE), None);
576 assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
577 });
578 }
579
580 #[docify::export]
581 #[test]
582 fn state_builder_add_balance() {
583 StateBuilder::default().add_balance(CHARLIE, 42).build_and_execute(|| {
584 assert_eq!(Balances::<Runtime>::get(&CHARLIE), Some(42));
585 assert_eq!(TotalIssuance::<Runtime>::get(), Some(242));
586 })
587 }
588
589 #[test]
590 #[should_panic]
591 fn state_builder_duplicate_genesis_fails() {
592 StateBuilder::default()
593 .add_balance(CHARLIE, 42)
594 .add_balance(CHARLIE, 43)
595 .build_and_execute(|| {
596 assert_eq!(Balances::<Runtime>::get(&CHARLIE), None);
597 assert_eq!(TotalIssuance::<Runtime>::get(), Some(242));
598 })
599 }
600
601 #[docify::export]
602 #[test]
603 fn mint_works() {
604 StateBuilder::default().build_and_execute(|| {
605 // given the initial state, when:
606 assert_ok!(Pallet::<Runtime>::mint_unsafe(RuntimeOrigin::signed(ALICE), BOB, 100));
607
608 // then:
609 assert_eq!(Balances::<Runtime>::get(&BOB), Some(200));
610 assert_eq!(TotalIssuance::<Runtime>::get(), Some(300));
611
612 // given:
613 assert_ok!(Pallet::<Runtime>::mint_unsafe(
614 RuntimeOrigin::signed(ALICE),
615 CHARLIE,
616 100
617 ));
618
619 // then:
620 assert_eq!(Balances::<Runtime>::get(&CHARLIE), Some(100));
621 assert_eq!(TotalIssuance::<Runtime>::get(), Some(400));
622 });
623 }
624
625 #[docify::export]
626 #[test]
627 fn transfer_works() {
628 StateBuilder::default().build_and_execute(|| {
629 // given the initial state, when:
630 assert_ok!(Pallet::<Runtime>::transfer(RuntimeOrigin::signed(ALICE), BOB, 50));
631
632 // then:
633 assert_eq!(Balances::<Runtime>::get(&ALICE), Some(50));
634 assert_eq!(Balances::<Runtime>::get(&BOB), Some(150));
635 assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
636
637 // when:
638 assert_ok!(Pallet::<Runtime>::transfer(RuntimeOrigin::signed(BOB), ALICE, 50));
639
640 // then:
641 assert_eq!(Balances::<Runtime>::get(&ALICE), Some(100));
642 assert_eq!(Balances::<Runtime>::get(&BOB), Some(100));
643 assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
644 });
645 }
646
647 #[docify::export]
648 #[test]
649 fn transfer_from_non_existent_fails() {
650 StateBuilder::default().build_and_execute(|| {
651 // given the initial state, when:
652 assert_err!(
653 Pallet::<Runtime>::transfer(RuntimeOrigin::signed(CHARLIE), ALICE, 10),
654 "NonExistentAccount"
655 );
656
657 // then nothing has changed.
658 assert_eq!(Balances::<Runtime>::get(&ALICE), Some(100));
659 assert_eq!(Balances::<Runtime>::get(&BOB), Some(100));
660 assert_eq!(Balances::<Runtime>::get(&CHARLIE), None);
661 assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
662 });
663 }
664 }
665}
666
667#[frame::pallet(dev_mode)]
668pub mod pallet_v2 {
669 use super::pallet::Balance;
670 use frame::prelude::*;
671
672 #[docify::export(config_v2)]
673 #[pallet::config]
674 pub trait Config: frame_system::Config {
675 /// The overarching event type of the runtime.
676 #[allow(deprecated)]
677 type RuntimeEvent: From<Event<Self>>
678 + IsType<<Self as frame_system::Config>::RuntimeEvent>
679 + TryInto<Event<Self>>;
680 }
681
682 #[pallet::pallet]
683 pub struct Pallet<T>(_);
684
685 #[pallet::storage]
686 pub type Balances<T: Config> = StorageMap<_, _, T::AccountId, Balance>;
687
688 #[pallet::storage]
689 pub type TotalIssuance<T: Config> = StorageValue<_, Balance>;
690
691 #[docify::export]
692 #[pallet::error]
693 pub enum Error<T> {
694 /// Account does not exist.
695 NonExistentAccount,
696 /// Account does not have enough balance.
697 InsufficientBalance,
698 }
699
700 #[docify::export]
701 #[pallet::event]
702 #[pallet::generate_deposit(pub(super) fn deposit_event)]
703 pub enum Event<T: Config> {
704 /// A transfer succeeded.
705 Transferred { from: T::AccountId, to: T::AccountId, amount: Balance },
706 }
707
708 #[pallet::call]
709 impl<T: Config> Pallet<T> {
710 #[docify::export(transfer_v2)]
711 pub fn transfer(
712 origin: T::RuntimeOrigin,
713 dest: T::AccountId,
714 amount: Balance,
715 ) -> DispatchResult {
716 let sender = ensure_signed(origin)?;
717
718 // ensure sender has enough balance, and if so, calculate what is left after `amount`.
719 let sender_balance =
720 Balances::<T>::get(&sender).ok_or(Error::<T>::NonExistentAccount)?;
721 let remainder =
722 sender_balance.checked_sub(amount).ok_or(Error::<T>::InsufficientBalance)?;
723
724 Balances::<T>::mutate(&dest, |b| *b = Some(b.unwrap_or(0) + amount));
725 Balances::<T>::insert(&sender, remainder);
726
727 Self::deposit_event(Event::<T>::Transferred { from: sender, to: dest, amount });
728
729 Ok(())
730 }
731 }
732
733 #[cfg(any(test, doc))]
734 pub mod tests {
735 use super::{super::pallet::tests::StateBuilder, *};
736 use frame::testing_prelude::*;
737 const ALICE: u64 = 1;
738 const BOB: u64 = 2;
739
740 #[docify::export]
741 pub mod runtime_v2 {
742 use super::*;
743 use crate::guides::your_first_pallet::pallet_v2 as pallet_currency;
744
745 construct_runtime!(
746 pub enum Runtime {
747 System: frame_system,
748 Currency: pallet_currency,
749 }
750 );
751
752 #[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
753 impl frame_system::Config for Runtime {
754 type Block = MockBlock<Runtime>;
755 type AccountId = u64;
756 }
757
758 impl pallet_currency::Config for Runtime {
759 type RuntimeEvent = RuntimeEvent;
760 }
761 }
762
763 pub(crate) use runtime_v2::*;
764
765 #[docify::export(transfer_works_v2)]
766 #[test]
767 fn transfer_works() {
768 StateBuilder::default().build_and_execute(|| {
769 // skip the genesis block, as events are not deposited there and we need them for
770 // the final assertion.
771 System::set_block_number(ALICE);
772
773 // given the initial state, when:
774 assert_ok!(Pallet::<Runtime>::transfer(RuntimeOrigin::signed(ALICE), BOB, 50));
775
776 // then:
777 assert_eq!(Balances::<Runtime>::get(&ALICE), Some(50));
778 assert_eq!(Balances::<Runtime>::get(&BOB), Some(150));
779 assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
780
781 // now we can also check that an event has been deposited:
782 assert_eq!(
783 System::read_events_for_pallet::<Event<Runtime>>(),
784 vec![Event::Transferred { from: ALICE, to: BOB, amount: 50 }]
785 );
786 });
787 }
788 }
789}