diff --git a/steps/11/README.md b/steps/11/README.md index e077a903..dda62b5b 100644 --- a/steps/11/README.md +++ b/steps/11/README.md @@ -29,7 +29,7 @@ These properties make hash functions key for ensuring data integrity and uniquen Due to the properties of a Hash, it is often referred to as a fingerprint. -For context, a 32-byte hash has 2^32 different possible outputs. This nearly as many atoms as there are in the whole universe! +For context, a 32-byte hash has 2^32 different possible outputs. This is nearly as many atoms as there are in the whole universe! This uniqueness property helps blockchain nodes come to consensus with one another. diff --git a/steps/16/README.md b/steps/16/README.md index 849975fb..316589e6 100644 --- a/steps/16/README.md +++ b/steps/16/README.md @@ -2,7 +2,7 @@ If you look into the history of "hacks" and "bugs" that happen in the blockchain world, a lot of it is associated with some kind of "unsafe" code. -Keeping our blockchain logic safe, and Rust is designed to handle it well. +We need to keep our blockchain logic safe, and Rust is designed to handle it well. ## Errors diff --git a/steps/20/README.md b/steps/20/README.md index 7b1ddf71..169722f0 100644 --- a/steps/20/README.md +++ b/steps/20/README.md @@ -19,6 +19,8 @@ Each `Key` can store a separate `Value`, which makes maps super useful. In this case `[u8; 32]` represents some unique identifier for each Kitty we will store, and `()` is simply a placeholder type for now. +Note that each storage item needs its own `#[pallet::storage]` attribute. + ## Conceptual The key difference between a `StorageValue` and a `StorageMap` is: diff --git a/steps/22/src/impls.rs b/steps/22/src/impls.rs index 89c7306a..44437026 100644 --- a/steps/22/src/impls.rs +++ b/steps/22/src/impls.rs @@ -2,11 +2,11 @@ use super::*; use frame::prelude::*; impl Pallet { - /* 🚧 TODO 🚧: Update this function signature to include `id` which is type `[u8; 32]`. */ + /* 🚧 TODO 🚧: Update this function signature to include `dna` which is type `[u8; 32]`. */ pub fn mint(owner: T::AccountId) -> DispatchResult { let current_count: u32 = CountForKitties::::get(); let new_count = current_count.checked_add(1).ok_or(Error::::TooManyKitties)?; - /* 🚧 TODO 🚧: In the `Kitties` map, under the key `id`, insert `()`. */ + /* 🚧 TODO 🚧: In the `Kitties` map, under the key `dna`, insert `()`. */ CountForKitties::::set(new_count); Self::deposit_event(Event::::Created { owner }); Ok(()) diff --git a/steps/22/src/lib.rs b/steps/22/src/lib.rs index 2a83b473..717c9009 100644 --- a/steps/22/src/lib.rs +++ b/steps/22/src/lib.rs @@ -40,8 +40,8 @@ pub mod pallet { pub fn create_kitty(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; /* 🚧 TODO 🚧: - - Create `const default_id`, which type `[u8; 32]` and has value `[0u8; 32]`. - - Pass `default_id` to the `mint` function as a second parameter. + - Create a `dna` variable for this kitty, which we will set as `[0u8; 32]` for now. + - Pass `dna` to the `mint` function as the second parameter. */ Self::mint(who)?; Ok(()) diff --git a/steps/27/README.md b/steps/27/README.md index 9662b30f..dea845d6 100644 --- a/steps/27/README.md +++ b/steps/27/README.md @@ -8,8 +8,8 @@ Creating a new struct in Rust is pretty straight forward. ```rust pub struct Kitty { - dna: [u8; 32], - owner: u32, + pub dna: [u8; 32], + pub owner: u32, } ``` @@ -38,8 +38,8 @@ The first, and most verbose option in our situation is to make the struct generi ```rust pub struct Kitty { - dna: [u8; 32], - owner: AccountId, + pub dna: [u8; 32], + pub owner: AccountId, } ``` @@ -47,9 +47,9 @@ If we want to use multiple generic types, we could just keep extending this patt ```rust pub struct Kitty { - dna: [u8; 32], - owner: AccountId, - created: BlockNumber, + pub dna: [u8; 32], + pub owner: AccountId, + pub created: BlockNumber, } ``` @@ -69,8 +69,8 @@ Let's look how that might look like: ```rust pub struct Kitty { - dna: [u8; 32], - owner: T::AccountId, + pub dna: [u8; 32], + pub owner: T::AccountId, } ``` diff --git a/steps/27/src/tests.rs b/steps/27/src/tests.rs index 5caa1349..38392b03 100644 --- a/steps/27/src/tests.rs +++ b/steps/27/src/tests.rs @@ -27,6 +27,9 @@ type Block = frame_system::mocking::MockBlock; // We create the constants `ALICE` and `BOB` to make it clear when we are representing users below. const ALICE: u64 = 1; const BOB: u64 = 2; +/* 🚧 TODO 🚧: + - Create `const DEFAULT_KITTY` with a `dna` of [0u8; 32] and an `owner` of 0 };. +*/ // Our blockchain tests only need 3 Pallets: // 1. System: Which is included with every FRAME runtime. diff --git a/steps/31/README.md b/steps/31/README.md index 48680d7e..37f9a647 100644 --- a/steps/31/README.md +++ b/steps/31/README.md @@ -37,7 +37,7 @@ let unique_payload = (item1, item2, item3); // To use the `hash_of` API, we need to bring the `Hash` trait into scope. use frame::traits::Hash; // Hash that object to get a unique identifier. -let hash: [u8; 32] = BlakeTwo256::hash_of(&unique_payload).into(). +let hash: [u8; 32] = BlakeTwo256::hash_of(&unique_payload).into(); ``` The `hash_of` API comes from the `Hash` trait and takes any `encode`-able object, and returns a `H256`, which is a 256-bit hash. As you can see in the code above, it is easy to convert that to a `[u8; 32]` by just calling `.into()`, since these two types are equivalent. diff --git a/steps/38/src/impls.rs b/steps/38/src/impls.rs index 4f1bdad9..3dd15659 100644 --- a/steps/38/src/impls.rs +++ b/steps/38/src/impls.rs @@ -42,7 +42,7 @@ impl Pallet { - `kitty_id` which is `[u8; 32]`. - It returns a `DispatchResult` - The inner logic for now is: - - Call `Self::dispatch_event` on and emit `Event:::Transferred` with params. + - Call `Self::deposit_event` and emit `Event:::Transferred` with params. - Return `Ok(())`. */ } diff --git a/steps/42/README.md b/steps/42/README.md index 90877e24..82a11584 100644 --- a/steps/42/README.md +++ b/steps/42/README.md @@ -101,7 +101,7 @@ T::NativeBalance::mint_into(alice, amount)?; T::NativeBalance::transfer(alice, bob, amount, Preserve)?; ``` -The key difference here is that we do NOT assume that these APIs must from from specifically `pallet_balances`. If you wanted to use another pallet in the `polkadot-sdk` ecosystem which provides these same functions, you can use it! Our pallet is NOT tightly coupled to which pallet provides access to the `NativeBalance`, it only requires that there is something implementing the `Inspect` and `Mutate` traits. +The key difference here is that we do NOT assume that these APIs must come from specifically `pallet_balances`. If you wanted to use another pallet in the `polkadot-sdk` ecosystem which provides these same functions, you can use it! Our pallet is NOT tightly coupled to which pallet provides access to the `NativeBalance`, it only requires that there is something implementing the `Inspect` and `Mutate` traits. The power of loose coupling may not be immediately obvious, but as you get deeper into developing in the Polkadot ecosystem, you will start to realize how powerful this approach can be. diff --git a/steps/42/src/tests.rs b/steps/42/src/tests.rs index ffe1390d..20bff4fd 100644 --- a/steps/42/src/tests.rs +++ b/steps/42/src/tests.rs @@ -61,6 +61,7 @@ impl pallet_balances::Config for TestRuntime { // will also need to update this configuration to represent that. impl pallet_kitties::Config for TestRuntime { type RuntimeEvent = RuntimeEvent; + /* 🚧 TODO 🚧: Add type `NativeBalance` as the `Balances` pallet. */ } // We need to run most of our tests using this function: `new_test_ext().execute_with(|| { ... });` diff --git a/steps/44/README.md b/steps/44/README.md index 7f8adbac..a680fdc2 100644 --- a/steps/44/README.md +++ b/steps/44/README.md @@ -20,7 +20,7 @@ Let's look at how we would interact with this generic type, and solve many of th The `Balance` type is ultimately configured inside `pallet_balances`, and remember, we don't have direct access to that pallet because we used loose coupling. -The way we can access the `Balance` type is through the `Inspect` trait of the `NativeBalance` associated type. Accessing it kind of funny, which is why we commonly introduce a `BalanceOf` alias type like so: +The way we can access the `Balance` type is through the `Inspect` trait of the `NativeBalance` associated type. Accessing it is kind of funny, which is why we commonly introduce a `BalanceOf` alias type like so: ```rust // Allows easy access our Pallet's `Balance` type. Comes from `Fungible` interface. diff --git a/steps/46/README.md b/steps/46/README.md index fa2d028c..5e0c171a 100644 --- a/steps/46/README.md +++ b/steps/46/README.md @@ -1,6 +1,6 @@ # Set Price Extrinsic -Now that we our Pallet set up to handle balances, let's actually use them. +Now that we have our Pallet set up to handle balances, let's actually use them. In this step, we will create an extrinsic which allows the owner of a kitty to set a price for the kitty. diff --git a/steps/50/README.md b/steps/50/README.md index 60cbe37f..909c412a 100644 --- a/steps/50/README.md +++ b/steps/50/README.md @@ -1,6 +1,6 @@ # Buy Kitty Extrinsic -Now that kitties can have a price, we want to enable them to be purchaseable. +Now that kitties can have a price, we want to enable them to be purchasable. For that, we will create the `buy_kitty` extrinsic. diff --git a/steps/52/README.md b/steps/52/README.md index 9c91cc7f..5ce0efd5 100644 --- a/steps/52/README.md +++ b/steps/52/README.md @@ -63,6 +63,12 @@ That is all beyond the scope of this tutorial, but the high level idea is that w In this context, we don't want someone to kill their account to buy a kitty, so we want to use `Preservation::Preserve` for our `transfer`. +> NOTE: Don't forget the TODO that imports this enum so you can use it: +> +> ```rust +> use frame::traits::tokens::Preservation; +> ``` + So the final syntax should look like: ```rust @@ -85,7 +91,7 @@ Both transfer functions need to succeed for the sale to complete successfully. If either one of them would fail, the whole purchase should fail. -Thankfully, both of our transfer functions return a result, and to handle things correctly here, we just ned to propagate up those errors. For that, we simply include `?` at the end of the function. +Thankfully, both of our transfer functions return a result, and to handle things correctly here, we just need to propagate up those errors. For that, we simply include `?` at the end of the function. If at any point our extrinsic or the logic inside the extrinsic returns an error, the whole extrinsic will fail and all changes to storage will be undone. This is exactly the same behavior you would expect from a smart contract, and keeps our state transition function functioning smoothly. diff --git a/steps/6/README.md b/steps/6/README.md index f4854d4b..2b3ee410 100644 --- a/steps/6/README.md +++ b/steps/6/README.md @@ -31,6 +31,8 @@ If you are familiar with smart contracts or any kind of blockchain application, Those transactions are processed, and then dispatched to callable functions within the blockchain. +For ergonomics, you will normally see callable functions defined in the `lib.rs`, but their logic implemented in a separate `impls.rs` file. This is really up to your preference, but since this is the common practice in the Polkadot SDK, the tutorial will use this pattern as well. + ### Pallet Call Macro Pallet development allows you to create callable functions by introducing the `#[pallet::call]` macro on top of a normal function implementation code block.