1212//! > This guide will build a currency pallet from scratch using only the lowest primitives of
1313//! > FRAME, and is mainly intended for education, not *applicability*. For example, almost all
1414//! > FRAME-based runtimes use various techniques to re-use a currency pallet instead of writing
15- //! > one. Further advance FRAME related topics are discussed in [`crate::reference_docs`].
15+ //! > one. Further advanced FRAME related topics are discussed in [`crate::reference_docs`].
1616//!
1717//! ## Topics Covered
1818//!
19- //! The following FRAME topics are covered in this guide. See the documentation of the
20- //! associated items to know more.
19+ //! The following FRAME topics are covered in this guide:
2120//!
2221//! - [Storage](frame::pallet_macros::storage)
2322//! - [Call](frame::pallet_macros::call)
5049//!
5150//! One should be a mapping from account-ids to a balance type, and one value that is the total
5251//! issuance.
53- //
54- // For the rest of this guide, we will opt for a balance type of u128.
52+ //!
53+ //! > For the rest of this guide, we will opt for a balance type of `u128`. For the sake of
54+ //! > simplicity, we are hardcoding this type. In a real pallet is best practice to define it as a
55+ //! > generic bounded type in the `Config` trait, and then specify it in the implementation.
5556#![ doc = docify:: embed!( "./src/guides/your_first_pallet/mod.rs" , Balance ) ]
5657//!
5758//! The definition of these two storage items, based on [`frame::pallet_macros::storage`] details,
160161#![ doc = docify:: embed!( "./src/guides/your_first_pallet/mod.rs" , first_test) ]
161162//!
162163//! In the first test, we simply assert that there is no total issuance, and no balance associated
163- //! with account `1` . Then, we mint some balance into `1` , and re-check.
164+ //! with Alice's account . Then, we mint some balance into Alice's , and re-check.
164165//!
165166//! As noted above, the `T::AccountId` is now `u64`. Moreover, `Runtime` is replacing `<T: Config>`.
166167//! This is why for example you see `Balances::<Runtime>::get(..)`. Finally, notice that the
167168//! dispatchables are simply functions that can be called on top of the `Pallet` struct.
168- //!
169- //! TODO: hard to explain exactly `RuntimeOrigin::signed(1)` at this point.
169+ // TODO: hard to explain exactly `RuntimeOrigin::signed(ALICE)` at this point.
170170//!
171171//! Congratulations! You have written your first pallet and tested it! Next, we learn a few optional
172172//! steps to improve our pallet.
186186#![ doc = docify:: embed!( "./src/guides/your_first_pallet/mod.rs" , StateBuilder ) ]
187187//!
188188//! This struct is meant to contain the same list of accounts and balances that we want to have at
189- //! the beginning of each block. We hardcoded this to `let accounts = vec![(1 , 100), (2, 100)];` so
190- //! far. Then, if desired, we attach a default value for this struct.
189+ //! the beginning of each block. We hardcoded this to `let accounts = vec![(ALICE , 100), (2, 100)];`
190+ //! so far. Then, if desired, we attach a default value for this struct.
191191#![ doc = docify:: embed!( "./src/guides/your_first_pallet/mod.rs" , default_state_builder) ]
192192//!
193193//! Like any other builder pattern, we attach functions to the type to mutate its internal
222222//! "success path" of a dispatchable, and one test for each "failure path", such as:
223223#![ doc = docify:: embed!( "./src/guides/your_first_pallet/mod.rs" , transfer_from_non_existent_fails) ]
224224//!
225- //! We leave it up to you to write a test that triggers to `InsufficientBalance` error.
225+ //! We leave it up to you to write a test that triggers the `InsufficientBalance` error.
226226//!
227227//! ### Event and Error
228228//!
229229//! Our pallet is mainly missing two parts that are common in most FRAME pallets: Events, and
230230//! Errors. First, let's understand what each is.
231231//!
232232//! - **Error**: The static string-based error scheme we used so far is good for readability, but it
233- //! has a few drawbacks. These string literals will bloat the final wasm blob, and are relatively
234- //! heavy to transmit and encode/decode. Moreover, it is easy to mistype them by one character.
235- //! FRAME errors are exactly a solution to maintain readability, whilst fixing the drawbacks
236- //! mentioned. In short, we use an enum to represent different variants of our error. These
237- //! variants are then mapped in an efficient way (using only `u8` indices) to
238- //! [`sp_runtime::DispatchError::Module`] Read more about this in [`frame::pallet_macros::error`].
239- //!
240- //! - **Event**: Events are akin to the return type of dispatchables. They should represent what
241- //! happened at the end of a dispatch operation. Therefore, the convention is to use passive tense
242- //! for event names (eg. `SomethingHappened`). This allows other sub-systems or external parties
243- //! (eg. a light-node, a DApp) to listen to particular events happening, without needing to
244- //! re-execute the whole state transition function.
245- //!
246- //! TODO: both need to be improved a lot at the pallet-macro rust-doc level. Also my explanation
247- //! of event is probably not the best.
233+ //! has a few drawbacks. The biggest problem with strings are that they are not type safe, e.g. a
234+ //! match statement cannot be exhaustive. These string literals will bloat the final wasm blob,
235+ //! and are relatively heavy to transmit and encode/decode. Moreover, it is easy to mistype them
236+ //! by one character. FRAME errors are exactly a solution to maintain readability, whilst fixing
237+ //! the drawbacks mentioned. In short, we use an enum to represent different variants of our
238+ //! 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`].
241+ //!
242+ //! - **Event**: Events are akin to the return type of dispatchables. They are mostly data blobs
243+ //! emitted by the runtime to let outside world know what is happening inside the pallet. Since
244+ //! otherwise, the outside world does not have an easy access to the state changes. They should
245+ //! represent what happened at the end of a dispatch operation. Therefore, the convention is to
246+ //! use passive tense for event names (eg. `SomethingHappened`). This allows other sub-systems or
247+ //! external parties (eg. a light-node, a DApp) to listen to particular events happening, without
248+ //! 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.
248251//!
249252//! With the explanation out of the way, let's see how these components can be added. Both follow a
250253//! fairly familiar syntax: normal Rust enums, with an extra `#[frame::event/error]` attribute
@@ -413,6 +416,9 @@ pub mod pallet {
413416 pub ( crate ) mod tests {
414417 use crate :: guides:: your_first_pallet:: pallet:: * ;
415418 use frame:: testing_prelude:: * ;
419+ const ALICE : u64 = 1 ;
420+ const BOB : u64 = 2 ;
421+ const CHARLIE : u64 = 3 ;
416422
417423 #[ docify:: export]
418424 mod runtime {
@@ -447,7 +453,7 @@ pub mod pallet {
447453 #[ docify:: export]
448454 fn new_test_state_basic ( ) -> TestState {
449455 let mut state = TestState :: new_empty ( ) ;
450- let accounts = vec ! [ ( 1 , 100 ) , ( 2 , 100 ) ] ;
456+ let accounts = vec ! [ ( ALICE , 100 ) , ( BOB , 100 ) ] ;
451457 state. execute_with ( || {
452458 for ( who, amount) in & accounts {
453459 Balances :: < Runtime > :: insert ( who, amount) ;
@@ -466,7 +472,7 @@ pub mod pallet {
466472 #[ docify:: export( default_state_builder) ]
467473 impl Default for StateBuilder {
468474 fn default ( ) -> Self {
469- Self { balances : vec ! [ ( 1 , 100 ) , ( 2 , 100 ) ] }
475+ Self { balances : vec ! [ ( ALICE , 100 ) , ( BOB , 100 ) ] }
470476 }
471477 }
472478
@@ -509,15 +515,19 @@ pub mod pallet {
509515 #[ test]
510516 fn first_test ( ) {
511517 TestState :: new_empty ( ) . execute_with ( || {
512- // We expect account 1 to have no funds.
513- assert_eq ! ( Balances :: <Runtime >:: get( & 1 ) , None ) ;
518+ // We expect Alice's account to have no funds.
519+ assert_eq ! ( Balances :: <Runtime >:: get( & ALICE ) , None ) ;
514520 assert_eq ! ( TotalIssuance :: <Runtime >:: get( ) , None ) ;
515521
516- // mint some funds into 1
517- assert_ok ! ( Pallet :: <Runtime >:: mint_unsafe( RuntimeOrigin :: signed( 1 ) , 1 , 100 ) ) ;
522+ // mint some funds into Alice's account.
523+ assert_ok ! ( Pallet :: <Runtime >:: mint_unsafe(
524+ RuntimeOrigin :: signed( ALICE ) ,
525+ ALICE ,
526+ 100
527+ ) ) ;
518528
519529 // re-check the above
520- assert_eq ! ( Balances :: <Runtime >:: get( & 1 ) , Some ( 100 ) ) ;
530+ assert_eq ! ( Balances :: <Runtime >:: get( & ALICE ) , Some ( 100 ) ) ;
521531 assert_eq ! ( TotalIssuance :: <Runtime >:: get( ) , Some ( 100 ) ) ;
522532 } )
523533 }
@@ -526,18 +536,18 @@ pub mod pallet {
526536 #[ test]
527537 fn state_builder_works ( ) {
528538 StateBuilder :: default ( ) . build_and_execute ( || {
529- assert_eq ! ( Balances :: <Runtime >:: get( & 1 ) , Some ( 100 ) ) ;
530- assert_eq ! ( Balances :: <Runtime >:: get( & 2 ) , Some ( 100 ) ) ;
531- assert_eq ! ( Balances :: <Runtime >:: get( & 3 ) , None ) ;
539+ assert_eq ! ( Balances :: <Runtime >:: get( & ALICE ) , Some ( 100 ) ) ;
540+ assert_eq ! ( Balances :: <Runtime >:: get( & BOB ) , Some ( 100 ) ) ;
541+ assert_eq ! ( Balances :: <Runtime >:: get( & CHARLIE ) , None ) ;
532542 assert_eq ! ( TotalIssuance :: <Runtime >:: get( ) , Some ( 200 ) ) ;
533543 } ) ;
534544 }
535545
536546 #[ docify:: export]
537547 #[ test]
538548 fn state_builder_add_balance ( ) {
539- StateBuilder :: default ( ) . add_balance ( 3 , 42 ) . build_and_execute ( || {
540- assert_eq ! ( Balances :: <Runtime >:: get( & 3 ) , Some ( 42 ) ) ;
549+ StateBuilder :: default ( ) . add_balance ( CHARLIE , 42 ) . build_and_execute ( || {
550+ assert_eq ! ( Balances :: <Runtime >:: get( & CHARLIE ) , Some ( 42 ) ) ;
541551 assert_eq ! ( TotalIssuance :: <Runtime >:: get( ) , Some ( 242 ) ) ;
542552 } )
543553 }
@@ -546,10 +556,10 @@ pub mod pallet {
546556 #[ should_panic]
547557 fn state_builder_duplicate_genesis_fails ( ) {
548558 StateBuilder :: default ( )
549- . add_balance ( 3 , 42 )
550- . add_balance ( 3 , 43 )
559+ . add_balance ( CHARLIE , 42 )
560+ . add_balance ( CHARLIE , 43 )
551561 . build_and_execute ( || {
552- assert_eq ! ( Balances :: <Runtime >:: get( & 3 ) , None ) ;
562+ assert_eq ! ( Balances :: <Runtime >:: get( & CHARLIE ) , None ) ;
553563 assert_eq ! ( TotalIssuance :: <Runtime >:: get( ) , Some ( 242 ) ) ;
554564 } )
555565 }
@@ -559,17 +569,21 @@ pub mod pallet {
559569 fn mint_works ( ) {
560570 StateBuilder :: default ( ) . build_and_execute ( || {
561571 // given the initial state, when:
562- assert_ok ! ( Pallet :: <Runtime >:: mint_unsafe( RuntimeOrigin :: signed( 1 ) , 2 , 100 ) ) ;
572+ assert_ok ! ( Pallet :: <Runtime >:: mint_unsafe( RuntimeOrigin :: signed( ALICE ) , BOB , 100 ) ) ;
563573
564574 // then:
565- assert_eq ! ( Balances :: <Runtime >:: get( & 2 ) , Some ( 200 ) ) ;
575+ assert_eq ! ( Balances :: <Runtime >:: get( & BOB ) , Some ( 200 ) ) ;
566576 assert_eq ! ( TotalIssuance :: <Runtime >:: get( ) , Some ( 300 ) ) ;
567577
568578 // given:
569- assert_ok ! ( Pallet :: <Runtime >:: mint_unsafe( RuntimeOrigin :: signed( 1 ) , 3 , 100 ) ) ;
579+ assert_ok ! ( Pallet :: <Runtime >:: mint_unsafe(
580+ RuntimeOrigin :: signed( ALICE ) ,
581+ CHARLIE ,
582+ 100
583+ ) ) ;
570584
571585 // then:
572- assert_eq ! ( Balances :: <Runtime >:: get( & 3 ) , Some ( 100 ) ) ;
586+ assert_eq ! ( Balances :: <Runtime >:: get( & CHARLIE ) , Some ( 100 ) ) ;
573587 assert_eq ! ( TotalIssuance :: <Runtime >:: get( ) , Some ( 400 ) ) ;
574588 } ) ;
575589 }
@@ -579,19 +593,19 @@ pub mod pallet {
579593 fn transfer_works ( ) {
580594 StateBuilder :: default ( ) . build_and_execute ( || {
581595 // given the the initial state, when:
582- assert_ok ! ( Pallet :: <Runtime >:: transfer( RuntimeOrigin :: signed( 1 ) , 2 , 50 ) ) ;
596+ assert_ok ! ( Pallet :: <Runtime >:: transfer( RuntimeOrigin :: signed( ALICE ) , BOB , 50 ) ) ;
583597
584598 // then:
585- assert_eq ! ( Balances :: <Runtime >:: get( & 1 ) , Some ( 50 ) ) ;
586- assert_eq ! ( Balances :: <Runtime >:: get( & 2 ) , Some ( 150 ) ) ;
599+ assert_eq ! ( Balances :: <Runtime >:: get( & ALICE ) , Some ( 50 ) ) ;
600+ assert_eq ! ( Balances :: <Runtime >:: get( & BOB ) , Some ( 150 ) ) ;
587601 assert_eq ! ( TotalIssuance :: <Runtime >:: get( ) , Some ( 200 ) ) ;
588602
589603 // when:
590- assert_ok ! ( Pallet :: <Runtime >:: transfer( RuntimeOrigin :: signed( 2 ) , 1 , 50 ) ) ;
604+ assert_ok ! ( Pallet :: <Runtime >:: transfer( RuntimeOrigin :: signed( BOB ) , ALICE , 50 ) ) ;
591605
592606 // then:
593- assert_eq ! ( Balances :: <Runtime >:: get( & 1 ) , Some ( 100 ) ) ;
594- assert_eq ! ( Balances :: <Runtime >:: get( & 2 ) , Some ( 100 ) ) ;
607+ assert_eq ! ( Balances :: <Runtime >:: get( & ALICE ) , Some ( 100 ) ) ;
608+ assert_eq ! ( Balances :: <Runtime >:: get( & BOB ) , Some ( 100 ) ) ;
595609 assert_eq ! ( TotalIssuance :: <Runtime >:: get( ) , Some ( 200 ) ) ;
596610 } ) ;
597611 }
@@ -602,14 +616,14 @@ pub mod pallet {
602616 StateBuilder :: default ( ) . build_and_execute ( || {
603617 // given the the initial state, when:
604618 assert_err ! (
605- Pallet :: <Runtime >:: transfer( RuntimeOrigin :: signed( 3 ) , 1 , 10 ) ,
619+ Pallet :: <Runtime >:: transfer( RuntimeOrigin :: signed( CHARLIE ) , ALICE , 10 ) ,
606620 "NonExistentAccount"
607621 ) ;
608622
609623 // then nothing has changed.
610- assert_eq ! ( Balances :: <Runtime >:: get( & 1 ) , Some ( 100 ) ) ;
611- assert_eq ! ( Balances :: <Runtime >:: get( & 2 ) , Some ( 100 ) ) ;
612- assert_eq ! ( Balances :: <Runtime >:: get( & 3 ) , None ) ;
624+ assert_eq ! ( Balances :: <Runtime >:: get( & ALICE ) , Some ( 100 ) ) ;
625+ assert_eq ! ( Balances :: <Runtime >:: get( & BOB ) , Some ( 100 ) ) ;
626+ assert_eq ! ( Balances :: <Runtime >:: get( & CHARLIE ) , None ) ;
613627 assert_eq ! ( TotalIssuance :: <Runtime >:: get( ) , Some ( 200 ) ) ;
614628 } ) ;
615629 }
@@ -685,6 +699,8 @@ pub mod pallet_v2 {
685699 pub mod tests {
686700 use super :: { super :: pallet:: tests:: StateBuilder , * } ;
687701 use frame:: testing_prelude:: * ;
702+ const ALICE : u64 = 1 ;
703+ const BOB : u64 = 2 ;
688704
689705 #[ docify:: export]
690706 pub mod runtime_v2 {
@@ -717,20 +733,20 @@ pub mod pallet_v2 {
717733 StateBuilder :: default ( ) . build_and_execute ( || {
718734 // skip the genesis block, as events are not deposited there and we need them for
719735 // the final assertion.
720- System :: set_block_number ( 1 ) ;
736+ System :: set_block_number ( ALICE ) ;
721737
722738 // given the the initial state, when:
723- assert_ok ! ( Pallet :: <Runtime >:: transfer( RuntimeOrigin :: signed( 1 ) , 2 , 50 ) ) ;
739+ assert_ok ! ( Pallet :: <Runtime >:: transfer( RuntimeOrigin :: signed( ALICE ) , BOB , 50 ) ) ;
724740
725741 // then:
726- assert_eq ! ( Balances :: <Runtime >:: get( & 1 ) , Some ( 50 ) ) ;
727- assert_eq ! ( Balances :: <Runtime >:: get( & 2 ) , Some ( 150 ) ) ;
742+ assert_eq ! ( Balances :: <Runtime >:: get( & ALICE ) , Some ( 50 ) ) ;
743+ assert_eq ! ( Balances :: <Runtime >:: get( & BOB ) , Some ( 150 ) ) ;
728744 assert_eq ! ( TotalIssuance :: <Runtime >:: get( ) , Some ( 200 ) ) ;
729745
730746 // now we can also check that an event has been deposited:
731747 assert_eq ! (
732748 System :: read_events_for_pallet:: <Event <Runtime >>( ) ,
733- vec![ Event :: Transferred { from: 1 , to: 2 , amount: 50 } ]
749+ vec![ Event :: Transferred { from: ALICE , to: BOB , amount: 50 } ]
734750 ) ;
735751 } ) ;
736752 }
0 commit comments